@vercel/node 3.0.5 → 3.0.6
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/dev-server.mjs +1170 -79
- package/dist/index.d.ts +29 -21
- package/dist/index.js +100042 -135984
- package/package.json +7 -9
- package/dist/babel.js +0 -32
- package/dist/edge-functions/edge-handler.mjs +0 -175
- package/dist/edge-functions/edge-node-compat-plugin.mjs +0 -140
- package/dist/edge-functions/edge-wasm-plugin.mjs +0 -77
- package/dist/fork-dev-server.js +0 -78
- package/dist/serverless-functions/helpers-web.d.ts +0 -7
- package/dist/serverless-functions/helpers-web.js +0 -63
- package/dist/serverless-functions/helpers.js +0 -229
- package/dist/serverless-functions/serverless-handler.mjs +0 -116
- package/dist/types.js +0 -2
- package/dist/typescript.js +0 -368
- package/dist/utils.js +0 -90
- /package/dist/{edge-functions/edge-handler-template.js → edge-handler-template.js} +0 -0
@@ -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