@vercel/node 2.2.1-canary.1 → 2.3.2
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.js +151 -50
- package/dist/index.js +364 -2777
- package/package.json +6 -5
package/dist/dev-server.js
CHANGED
@@ -68,8 +68,19 @@ const exit_hook_1 = __importDefault(require("exit-hook"));
|
|
68
68
|
const edge_runtime_1 = require("edge-runtime");
|
69
69
|
const static_config_1 = require("@vercel/static-config");
|
70
70
|
const ts_morph_1 = require("ts-morph");
|
71
|
-
const
|
71
|
+
const esbuild_1 = __importDefault(require("esbuild"));
|
72
72
|
const node_fetch_1 = __importDefault(require("node-fetch"));
|
73
|
+
const util_1 = require("util");
|
74
|
+
function logError(error) {
|
75
|
+
console.error(error.message);
|
76
|
+
if (error.stack) {
|
77
|
+
// only show the stack trace if debug is enabled
|
78
|
+
// because it points to internals, not user code
|
79
|
+
const errorPrefixLength = 'Error: '.length;
|
80
|
+
const errorMessageLength = errorPrefixLength + error.message.length;
|
81
|
+
build_utils_1.debug(error.stack.substring(errorMessageLength + 1));
|
82
|
+
}
|
83
|
+
}
|
73
84
|
function listen(server, port, host) {
|
74
85
|
return new Promise(resolve => {
|
75
86
|
server.listen(port, host, () => {
|
@@ -115,81 +126,158 @@ async function serializeRequest(message) {
|
|
115
126
|
body,
|
116
127
|
});
|
117
128
|
}
|
118
|
-
async function
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
129
|
+
async function compileUserCode(entrypoint) {
|
130
|
+
try {
|
131
|
+
const result = await esbuild_1.default.build({
|
132
|
+
platform: 'node',
|
133
|
+
target: 'node14',
|
134
|
+
sourcemap: 'inline',
|
135
|
+
bundle: true,
|
136
|
+
entryPoints: [entrypoint],
|
137
|
+
write: false,
|
138
|
+
format: 'cjs',
|
139
|
+
});
|
140
|
+
const compiledFile = result.outputFiles?.[0];
|
141
|
+
if (!compiledFile) {
|
142
|
+
throw new Error(`Compilation of ${entrypoint} produced no output files.`);
|
143
|
+
}
|
144
|
+
const userCode = new util_1.TextDecoder().decode(compiledFile.contents);
|
145
|
+
return `
|
146
|
+
${userCode};
|
123
147
|
|
124
|
-
|
125
|
-
|
126
|
-
|
148
|
+
addEventListener('fetch', async (event) => {
|
149
|
+
try {
|
150
|
+
let serializedRequest = await event.request.text();
|
151
|
+
let requestDetails = JSON.parse(serializedRequest);
|
127
152
|
|
128
|
-
|
153
|
+
let body;
|
129
154
|
|
130
|
-
|
131
|
-
|
132
|
-
|
155
|
+
if (requestDetails.method !== 'GET' && requestDetails.method !== 'HEAD') {
|
156
|
+
body = Uint8Array.from(atob(requestDetails.body), c => c.charCodeAt(0));
|
157
|
+
}
|
133
158
|
|
134
|
-
|
159
|
+
let requestUrl = requestDetails.headers['x-forwarded-proto'] + '://' + requestDetails.headers['x-forwarded-host'] + requestDetails.url;
|
135
160
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
161
|
+
let request = new Request(requestUrl, {
|
162
|
+
headers: requestDetails.headers,
|
163
|
+
method: requestDetails.method,
|
164
|
+
body: body
|
165
|
+
});
|
141
166
|
|
142
|
-
|
167
|
+
event.request = request;
|
143
168
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
169
|
+
let edgeHandler = module.exports.default;
|
170
|
+
if (!edgeHandler) {
|
171
|
+
throw new Error('No default export was found. Add a default export to handle requests.');
|
172
|
+
}
|
173
|
+
|
174
|
+
let response = await edgeHandler(event.request, event);
|
175
|
+
|
176
|
+
return event.respondWith(response);
|
177
|
+
} catch (error) {
|
178
|
+
// we can't easily show a meaningful stack trace
|
179
|
+
// so, stick to just the error message for now
|
180
|
+
event.respondWith(new Response(error.message, {
|
181
|
+
status: 500,
|
182
|
+
headers: {
|
183
|
+
'x-vercel-failed': 'edge-wrapper'
|
184
|
+
}
|
185
|
+
}));
|
186
|
+
}
|
187
|
+
})`;
|
188
|
+
}
|
189
|
+
catch (error) {
|
190
|
+
// We can't easily show a meaningful stack trace from ncc -> edge-runtime.
|
191
|
+
// So, stick with just the message for now.
|
192
|
+
console.error(`Failed to instantiate edge runtime.`);
|
193
|
+
logError(error);
|
194
|
+
return undefined;
|
195
|
+
}
|
196
|
+
}
|
197
|
+
async function createEdgeRuntime(userCode) {
|
198
|
+
try {
|
199
|
+
if (!userCode) {
|
200
|
+
return undefined;
|
201
|
+
}
|
202
|
+
const edgeRuntime = new edge_runtime_1.EdgeRuntime({
|
203
|
+
initialCode: userCode,
|
204
|
+
extend: (context) => {
|
205
|
+
Object.assign(context, {
|
206
|
+
__dirname: '',
|
207
|
+
module: {
|
208
|
+
exports: {},
|
209
|
+
},
|
210
|
+
});
|
211
|
+
return context;
|
212
|
+
},
|
213
|
+
});
|
214
|
+
const server = await edge_runtime_1.runServer({ runtime: edgeRuntime });
|
215
|
+
exit_hook_1.default(server.close);
|
216
|
+
return server;
|
217
|
+
}
|
218
|
+
catch (error) {
|
219
|
+
// We can't easily show a meaningful stack trace from ncc -> edge-runtime.
|
220
|
+
// So, stick with just the message for now.
|
221
|
+
console.error('Failed to instantiate edge runtime.');
|
222
|
+
logError(error);
|
223
|
+
return undefined;
|
224
|
+
}
|
225
|
+
}
|
226
|
+
async function createEdgeEventHandler(entrypoint) {
|
227
|
+
const userCode = await compileUserCode(entrypoint);
|
228
|
+
const server = await createEdgeRuntime(userCode);
|
162
229
|
return async function (request) {
|
230
|
+
if (!server) {
|
231
|
+
// this error state is already logged, but we have to wait until here to exit the process
|
232
|
+
// this matches the serverless function bridge launcher's behavior when
|
233
|
+
// an error is thrown in the function
|
234
|
+
process.exit(1);
|
235
|
+
}
|
163
236
|
const response = await node_fetch_1.default(server.url, {
|
237
|
+
redirect: 'manual',
|
164
238
|
method: 'post',
|
165
239
|
body: await serializeRequest(request),
|
166
240
|
});
|
241
|
+
const body = await response.text();
|
242
|
+
const isUserError = response.headers.get('x-vercel-failed') === 'edge-wrapper';
|
243
|
+
if (isUserError && response.status >= 500) {
|
244
|
+
// this error was "unhandled" from the user code's perspective
|
245
|
+
console.log(`Unhandled rejection: ${body}`);
|
246
|
+
// this matches the serverless function bridge launcher's behavior when
|
247
|
+
// an error is thrown in the function
|
248
|
+
process.exit(1);
|
249
|
+
}
|
167
250
|
return {
|
168
251
|
statusCode: response.status,
|
169
252
|
headers: response.headers.raw(),
|
170
|
-
body
|
253
|
+
body,
|
171
254
|
encoding: 'utf8',
|
172
255
|
};
|
173
256
|
};
|
174
257
|
}
|
175
258
|
const validRuntimes = ['experimental-edge'];
|
176
|
-
function parseRuntime(entrypoint) {
|
259
|
+
function parseRuntime(entrypoint, entryPointPath) {
|
177
260
|
const project = new ts_morph_1.Project();
|
178
|
-
const staticConfig = static_config_1.getConfig(project,
|
261
|
+
const staticConfig = static_config_1.getConfig(project, entryPointPath);
|
179
262
|
const runtime = staticConfig?.runtime;
|
180
263
|
if (runtime && !validRuntimes.includes(runtime)) {
|
181
|
-
throw new Error(`Invalid function runtime for "${entrypoint}": ${
|
264
|
+
throw new Error(`Invalid function runtime "${runtime}" for "${entrypoint}". Valid runtimes are: ${JSON.stringify(validRuntimes)}`);
|
182
265
|
}
|
183
266
|
return runtime;
|
184
267
|
}
|
185
|
-
async function createEventHandler(entrypoint, options) {
|
186
|
-
const
|
187
|
-
|
188
|
-
|
268
|
+
async function createEventHandler(entrypoint, config, options) {
|
269
|
+
const entryPointPath = path_1.join(process.cwd(), entrypoint);
|
270
|
+
const runtime = parseRuntime(entrypoint, entryPointPath);
|
271
|
+
// `middleware.js`/`middleware.ts` file is always run as
|
272
|
+
// an Edge Function, otherwise needs to be opted-in via
|
273
|
+
// `export const config = { runtime: 'experimental-edge' }`
|
274
|
+
if (config.middleware === true || runtime === 'experimental-edge') {
|
275
|
+
return createEdgeEventHandler(entryPointPath);
|
189
276
|
}
|
190
|
-
return createServerlessEventHandler(
|
277
|
+
return createServerlessEventHandler(entryPointPath, options);
|
191
278
|
}
|
192
279
|
let handleEvent;
|
280
|
+
let handlerEventError;
|
193
281
|
async function main() {
|
194
282
|
const config = JSON.parse(process.env.VERCEL_DEV_CONFIG || '{}');
|
195
283
|
delete process.env.VERCEL_DEV_CONFIG;
|
@@ -198,8 +286,15 @@ async function main() {
|
|
198
286
|
const shouldAddHelpers = !(config.helpers === false || buildEnv.NODEJS_HELPERS === '0');
|
199
287
|
const proxyServer = http_1.createServer(onDevRequest);
|
200
288
|
await listen(proxyServer, 0, '127.0.0.1');
|
201
|
-
|
202
|
-
|
289
|
+
try {
|
290
|
+
handleEvent = await createEventHandler(entrypoint, config, {
|
291
|
+
shouldAddHelpers,
|
292
|
+
});
|
293
|
+
}
|
294
|
+
catch (error) {
|
295
|
+
logError(error);
|
296
|
+
handlerEventError = error;
|
297
|
+
}
|
203
298
|
const address = proxyServer.address();
|
204
299
|
if (typeof process.send === 'function') {
|
205
300
|
process.send(address);
|
@@ -224,6 +319,12 @@ function rawBody(readable) {
|
|
224
319
|
}
|
225
320
|
exports.rawBody = rawBody;
|
226
321
|
async function onDevRequest(req, res) {
|
322
|
+
if (handlerEventError) {
|
323
|
+
// this error state is already logged, but we have to wait until here to exit the process
|
324
|
+
// this matches the serverless function bridge launcher's behavior when
|
325
|
+
// an error is thrown in the function
|
326
|
+
process.exit(1);
|
327
|
+
}
|
227
328
|
if (!handleEvent) {
|
228
329
|
res.statusCode = 500;
|
229
330
|
res.end('Bridge is not ready, please try again');
|
@@ -259,6 +360,6 @@ function fixConfigDev(config) {
|
|
259
360
|
}
|
260
361
|
exports.fixConfigDev = fixConfigDev;
|
261
362
|
main().catch(err => {
|
262
|
-
|
363
|
+
logError(err);
|
263
364
|
process.exit(1);
|
264
365
|
});
|