@vercel/node 3.0.5 → 3.0.7

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.
@@ -1,229 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.addHelpers = exports.getBodyParser = void 0;
4
- const utils_1 = require("../utils");
5
- const stream_1 = require("stream");
6
- const content_type_1 = require("content-type");
7
- const querystring_1 = require("querystring");
8
- class ApiError extends Error {
9
- constructor(statusCode, message) {
10
- super(message);
11
- this.statusCode = statusCode;
12
- }
13
- }
14
- function normalizeContentType(contentType) {
15
- if (!contentType) {
16
- return 'text/plain';
17
- }
18
- const { type } = (0, content_type_1.parse)(contentType);
19
- return type;
20
- }
21
- function getBodyParser(body, contentType) {
22
- return function parseBody() {
23
- const type = normalizeContentType(contentType);
24
- if (type === 'application/json') {
25
- try {
26
- const str = body.toString();
27
- return str ? JSON.parse(str) : {};
28
- }
29
- catch (error) {
30
- throw new ApiError(400, 'Invalid JSON');
31
- }
32
- }
33
- if (type === 'application/octet-stream')
34
- return body;
35
- if (type === 'application/x-www-form-urlencoded') {
36
- // note: querystring.parse does not produce an iterable object
37
- // https://nodejs.org/api/querystring.html#querystring_querystring_parse_str_sep_eq_options
38
- return (0, querystring_1.parse)(body.toString());
39
- }
40
- if (type === 'text/plain')
41
- return body.toString();
42
- return undefined;
43
- };
44
- }
45
- exports.getBodyParser = getBodyParser;
46
- function getQueryParser({ url = '/' }) {
47
- return function parseQuery() {
48
- const { parse: parseURL } = require('url');
49
- return parseURL(url, true).query;
50
- };
51
- }
52
- function getCookieParser(req) {
53
- return function parseCookie() {
54
- const header = req.headers.cookie;
55
- if (!header)
56
- return {};
57
- const { parse } = require('cookie');
58
- return parse(Array.isArray(header) ? header.join(';') : header);
59
- };
60
- }
61
- function status(res, statusCode) {
62
- res.statusCode = statusCode;
63
- return res;
64
- }
65
- function setCharset(type, charset) {
66
- const { parse, format } = require('content-type');
67
- const parsed = parse(type);
68
- parsed.parameters.charset = charset;
69
- return format(parsed);
70
- }
71
- function redirect(res, statusOrUrl, url) {
72
- if (typeof statusOrUrl === 'string') {
73
- url = statusOrUrl;
74
- statusOrUrl = 307;
75
- }
76
- if (typeof statusOrUrl !== 'number' || typeof url !== 'string') {
77
- throw new Error(`Invalid redirect arguments. Please use a single argument URL, e.g. res.redirect('/destination') or use a status code and URL, e.g. res.redirect(307, '/destination').`);
78
- }
79
- res.writeHead(statusOrUrl, { Location: url }).end();
80
- return res;
81
- }
82
- function setLazyProp(req, prop, getter) {
83
- const opts = { configurable: true, enumerable: true };
84
- const optsReset = { ...opts, writable: true };
85
- Object.defineProperty(req, prop, {
86
- ...opts,
87
- get: () => {
88
- const value = getter();
89
- // we set the property on the object to avoid recalculating it
90
- Object.defineProperty(req, prop, { ...optsReset, value });
91
- return value;
92
- },
93
- set: value => {
94
- Object.defineProperty(req, prop, { ...optsReset, value });
95
- },
96
- });
97
- }
98
- function createETag(body, encoding) {
99
- const etag = require('etag');
100
- const buf = !Buffer.isBuffer(body) ? Buffer.from(body, encoding) : body;
101
- return etag(buf, { weak: true });
102
- }
103
- function json(req, res, jsonBody) {
104
- const body = JSON.stringify(jsonBody);
105
- if (!res.getHeader('content-type')) {
106
- res.setHeader('content-type', 'application/json; charset=utf-8');
107
- }
108
- return send(req, res, body);
109
- }
110
- function send(req, res, body) {
111
- let chunk = body;
112
- let encoding;
113
- switch (typeof chunk) {
114
- // string defaulting to html
115
- case 'string':
116
- if (!res.getHeader('content-type')) {
117
- res.setHeader('content-type', 'text/html');
118
- }
119
- break;
120
- case 'boolean':
121
- case 'number':
122
- case 'object':
123
- if (chunk === null) {
124
- chunk = '';
125
- }
126
- else if (Buffer.isBuffer(chunk)) {
127
- if (!res.getHeader('content-type')) {
128
- res.setHeader('content-type', 'application/octet-stream');
129
- }
130
- }
131
- else {
132
- return json(req, res, chunk);
133
- }
134
- break;
135
- }
136
- // write strings in utf-8
137
- if (typeof chunk === 'string') {
138
- encoding = 'utf8';
139
- // reflect this in content-type
140
- const type = res.getHeader('content-type');
141
- if (typeof type === 'string') {
142
- res.setHeader('content-type', setCharset(type, 'utf-8'));
143
- }
144
- }
145
- // populate Content-Length
146
- let len;
147
- if (chunk !== undefined) {
148
- if (Buffer.isBuffer(chunk)) {
149
- // get length of Buffer
150
- len = chunk.length;
151
- }
152
- else if (typeof chunk === 'string') {
153
- if (chunk.length < 1000) {
154
- // just calculate length small chunk
155
- len = Buffer.byteLength(chunk, encoding);
156
- }
157
- else {
158
- // convert chunk to Buffer and calculate
159
- const buf = Buffer.from(chunk, encoding);
160
- len = buf.length;
161
- chunk = buf;
162
- encoding = undefined;
163
- }
164
- }
165
- else {
166
- throw new Error('`body` is not a valid string, object, boolean, number, Stream, or Buffer');
167
- }
168
- if (len !== undefined) {
169
- res.setHeader('content-length', len);
170
- }
171
- }
172
- // populate ETag
173
- let etag;
174
- if (!res.getHeader('etag') &&
175
- len !== undefined &&
176
- (etag = createETag(chunk, encoding))) {
177
- res.setHeader('etag', etag);
178
- }
179
- // strip irrelevant headers
180
- if (204 === res.statusCode || 304 === res.statusCode) {
181
- res.removeHeader('Content-Type');
182
- res.removeHeader('Content-Length');
183
- res.removeHeader('Transfer-Encoding');
184
- chunk = '';
185
- }
186
- if (req.method === 'HEAD') {
187
- // skip body for HEAD
188
- res.end();
189
- }
190
- else if (encoding) {
191
- // respond with encoding
192
- res.end(chunk, encoding);
193
- }
194
- else {
195
- // respond without encoding
196
- res.end(chunk);
197
- }
198
- return res;
199
- }
200
- function restoreBody(req, body) {
201
- const replicateBody = new stream_1.PassThrough();
202
- const on = replicateBody.on.bind(replicateBody);
203
- const originalOn = req.on.bind(req);
204
- req.read = replicateBody.read.bind(replicateBody);
205
- req.on = req.addListener = (name, cb) =>
206
- // @ts-expect-error
207
- name === 'data' || name === 'end' ? on(name, cb) : originalOn(name, cb);
208
- replicateBody.write(body);
209
- replicateBody.end();
210
- }
211
- async function readBody(req) {
212
- const body = (await (0, utils_1.serializeBody)(req)) || Buffer.from('');
213
- restoreBody(req, body);
214
- return body;
215
- }
216
- async function addHelpers(_req, _res) {
217
- const req = _req;
218
- const res = _res;
219
- setLazyProp(req, 'cookies', getCookieParser(req));
220
- setLazyProp(req, 'query', getQueryParser(req));
221
- const contentType = req.headers['content-type'];
222
- const body = contentType === undefined ? Buffer.from('') : await readBody(req);
223
- setLazyProp(req, 'body', getBodyParser(body, contentType));
224
- res.status = statusCode => status(res, statusCode);
225
- res.redirect = (statusOrUrl, url) => redirect(res, statusOrUrl, url);
226
- res.send = body => send(req, res, body);
227
- res.json = jsonBody => json(req, res, jsonBody);
228
- }
229
- exports.addHelpers = addHelpers;
@@ -1,116 +0,0 @@
1
- import { addHelpers } from './helpers.js';
2
- import { createServer } from 'http';
3
- import { serializeBody } from '../utils.js';
4
- import exitHook from 'exit-hook';
5
- import { Headers, fetch } from 'undici';
6
- import { listen } from 'async-listen';
7
- import { isAbsolute } from 'path';
8
- import { pathToFileURL } from 'url';
9
- import zlib from 'zlib';
10
- const [NODE_MAJOR] = process.versions.node.split('.').map(v => Number(v));
11
- /* https://nextjs.org/docs/app/building-your-application/routing/router-handlers#supported-http-methods */
12
- const HTTP_METHODS = [
13
- 'GET',
14
- 'HEAD',
15
- 'OPTIONS',
16
- 'POST',
17
- 'PUT',
18
- 'DELETE',
19
- 'PATCH',
20
- ];
21
- function compress(body, encoding) {
22
- switch (encoding) {
23
- case 'br':
24
- return zlib.brotliCompressSync(body, {
25
- params: {
26
- [zlib.constants.BROTLI_PARAM_QUALITY]: 0,
27
- },
28
- });
29
- case 'gzip':
30
- return zlib.gzipSync(body, {
31
- level: zlib.constants.Z_BEST_SPEED,
32
- });
33
- case 'deflate':
34
- return zlib.deflateSync(body, {
35
- level: zlib.constants.Z_BEST_SPEED,
36
- });
37
- default:
38
- throw new Error(`encoding '${encoding}' not supported`);
39
- }
40
- }
41
- async function createServerlessServer(userCode) {
42
- const server = createServer(userCode);
43
- exitHook(() => server.close());
44
- return { url: await listen(server) };
45
- }
46
- async function compileUserCode(entrypointPath, options) {
47
- const id = isAbsolute(entrypointPath)
48
- ? pathToFileURL(entrypointPath).href
49
- : entrypointPath;
50
- let listener = await import(id);
51
- /**
52
- * In some cases we might have nested default props due to TS => JS
53
- */
54
- for (let i = 0; i < 5; i++) {
55
- if (listener.default)
56
- listener = listener.default;
57
- }
58
- if (HTTP_METHODS.some(method => typeof listener[method] === 'function')) {
59
- if (NODE_MAJOR < 18) {
60
- throw new Error('Node.js v18 or above is required to use HTTP method exports in your functions.');
61
- }
62
- const { getWebExportsHandler } = await import('./helpers-web.js');
63
- return getWebExportsHandler(listener, HTTP_METHODS);
64
- }
65
- return async (req, res) => {
66
- if (options.shouldAddHelpers)
67
- await addHelpers(req, res);
68
- return listener(req, res);
69
- };
70
- }
71
- export async function createServerlessEventHandler(entrypointPath, options) {
72
- const userCode = await compileUserCode(entrypointPath, options);
73
- const server = await createServerlessServer(userCode);
74
- const isStreaming = options.mode === 'streaming';
75
- return async function (request) {
76
- const url = new URL(request.url ?? '/', server.url);
77
- const response = await fetch(url, {
78
- body: await serializeBody(request),
79
- compress: !isStreaming,
80
- // @ts-expect-error
81
- headers: {
82
- ...request.headers,
83
- host: request.headers['x-forwarded-host'],
84
- },
85
- method: request.method,
86
- redirect: 'manual',
87
- });
88
- let body = null;
89
- let headers = response.headers;
90
- if (isStreaming) {
91
- body = response.body;
92
- }
93
- else {
94
- body = Buffer.from(await response.arrayBuffer());
95
- const contentEncoding = response.headers.get('content-encoding');
96
- if (contentEncoding) {
97
- body = compress(body, contentEncoding);
98
- const clonedHeaders = [];
99
- for (const [key, value] of response.headers.entries()) {
100
- if (key !== 'transfer-encoding') {
101
- // transfer-encoding is only for streaming response
102
- clonedHeaders.push([key, value]);
103
- }
104
- }
105
- clonedHeaders.push(['content-length', String(Buffer.byteLength(body))]);
106
- headers = new Headers(clonedHeaders);
107
- }
108
- }
109
- return {
110
- status: response.status,
111
- headers,
112
- body,
113
- encoding: 'utf8',
114
- };
115
- };
116
- }
package/dist/types.js DELETED
@@ -1,2 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });