@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.
Files changed (3) hide show
  1. package/dist/dev-server.js +151 -50
  2. package/dist/index.js +364 -2777
  3. package/package.json +6 -5
@@ -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 ncc_1 = __importDefault(require("@vercel/ncc"));
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 createEdgeEventHandler(entrypoint) {
119
- const buildResult = await ncc_1.default(entrypoint, { target: 'es2022' });
120
- const userCode = buildResult.code;
121
- const initialCode = `
122
- ${userCode};
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
- addEventListener('fetch', async (event) => {
125
- let serializedRequest = await event.request.text();
126
- let requestDetails = JSON.parse(serializedRequest);
148
+ addEventListener('fetch', async (event) => {
149
+ try {
150
+ let serializedRequest = await event.request.text();
151
+ let requestDetails = JSON.parse(serializedRequest);
127
152
 
128
- let body;
153
+ let body;
129
154
 
130
- if (requestDetails.method !== 'GET' && requestDetails.method !== 'HEAD') {
131
- body = Uint8Array.from(atob(requestDetails.body), c => c.charCodeAt(0));
132
- }
155
+ if (requestDetails.method !== 'GET' && requestDetails.method !== 'HEAD') {
156
+ body = Uint8Array.from(atob(requestDetails.body), c => c.charCodeAt(0));
157
+ }
133
158
 
134
- let requestUrl = requestDetails.headers['x-forwarded-proto'] + '://' + requestDetails.headers['x-forwarded-host'] + requestDetails.url;
159
+ let requestUrl = requestDetails.headers['x-forwarded-proto'] + '://' + requestDetails.headers['x-forwarded-host'] + requestDetails.url;
135
160
 
136
- let request = new Request(requestUrl, {
137
- headers: requestDetails.headers,
138
- method: requestDetails.method,
139
- body: body
140
- });
161
+ let request = new Request(requestUrl, {
162
+ headers: requestDetails.headers,
163
+ method: requestDetails.method,
164
+ body: body
165
+ });
141
166
 
142
- event.request = request;
167
+ event.request = request;
143
168
 
144
- let edgeHandler = module.exports.default;
145
- let response = edgeHandler(event.request, event);
146
- return event.respondWith(response);
147
- })`;
148
- const edgeRuntime = new edge_runtime_1.EdgeRuntime({
149
- initialCode,
150
- extend: (context) => {
151
- Object.assign(context, {
152
- __dirname: '',
153
- module: {
154
- exports: {},
155
- },
156
- });
157
- return context;
158
- },
159
- });
160
- const server = await edge_runtime_1.runServer({ runtime: edgeRuntime });
161
- exit_hook_1.default(server.close);
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: await response.text(),
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, entrypoint);
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}": ${runtime}`);
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 runtime = parseRuntime(entrypoint);
187
- if (runtime === 'experimental-edge') {
188
- return createEdgeEventHandler(entrypoint);
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(entrypoint, options);
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
- const entryPointPath = path_1.join(process.cwd(), entrypoint);
202
- handleEvent = await createEventHandler(entryPointPath, { shouldAddHelpers });
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
- console.error(err);
363
+ logError(err);
263
364
  process.exit(1);
264
365
  });