@vercel/node 2.2.1-canary.1 → 2.3.0
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 +137 -49
- package/dist/index.js +20 -3
- package/package.json +3 -3
package/dist/dev-server.js
CHANGED
@@ -70,6 +70,16 @@ const static_config_1 = require("@vercel/static-config");
|
|
70
70
|
const ts_morph_1 = require("ts-morph");
|
71
71
|
const ncc_1 = __importDefault(require("@vercel/ncc"));
|
72
72
|
const node_fetch_1 = __importDefault(require("node-fetch"));
|
73
|
+
function logError(error) {
|
74
|
+
console.error(error.message);
|
75
|
+
if (error.stack) {
|
76
|
+
// only show the stack trace if debug is enabled
|
77
|
+
// because it points to internals, not user code
|
78
|
+
const errorPrefixLength = 'Error: '.length;
|
79
|
+
const errorMessageLength = errorPrefixLength + error.message.length;
|
80
|
+
build_utils_1.debug(error.stack.substring(errorMessageLength + 1));
|
81
|
+
}
|
82
|
+
}
|
73
83
|
function listen(server, port, host) {
|
74
84
|
return new Promise(resolve => {
|
75
85
|
server.listen(port, host, () => {
|
@@ -115,81 +125,146 @@ async function serializeRequest(message) {
|
|
115
125
|
body,
|
116
126
|
});
|
117
127
|
}
|
118
|
-
async function
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
128
|
+
async function compileUserCode(entrypoint) {
|
129
|
+
try {
|
130
|
+
const buildResult = await ncc_1.default(entrypoint, {
|
131
|
+
target: 'es2022',
|
132
|
+
});
|
133
|
+
const userCode = buildResult.code;
|
134
|
+
return `
|
135
|
+
${userCode};
|
123
136
|
|
124
|
-
|
125
|
-
|
126
|
-
|
137
|
+
addEventListener('fetch', async (event) => {
|
138
|
+
try {
|
139
|
+
let serializedRequest = await event.request.text();
|
140
|
+
let requestDetails = JSON.parse(serializedRequest);
|
127
141
|
|
128
|
-
|
142
|
+
let body;
|
129
143
|
|
130
|
-
|
131
|
-
|
132
|
-
|
144
|
+
if (requestDetails.method !== 'GET' && requestDetails.method !== 'HEAD') {
|
145
|
+
body = Uint8Array.from(atob(requestDetails.body), c => c.charCodeAt(0));
|
146
|
+
}
|
133
147
|
|
134
|
-
|
148
|
+
let requestUrl = requestDetails.headers['x-forwarded-proto'] + '://' + requestDetails.headers['x-forwarded-host'] + requestDetails.url;
|
135
149
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
150
|
+
let request = new Request(requestUrl, {
|
151
|
+
headers: requestDetails.headers,
|
152
|
+
method: requestDetails.method,
|
153
|
+
body: body
|
154
|
+
});
|
141
155
|
|
142
|
-
|
156
|
+
event.request = request;
|
143
157
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
158
|
+
let edgeHandler = module.exports.default;
|
159
|
+
if (!edgeHandler) {
|
160
|
+
throw new Error('No default export was found. Add a default export to handle requests.');
|
161
|
+
}
|
162
|
+
|
163
|
+
let response = await edgeHandler(event.request, event);
|
164
|
+
|
165
|
+
return event.respondWith(response);
|
166
|
+
} catch (error) {
|
167
|
+
// we can't easily show a meaningful stack trace
|
168
|
+
// so, stick to just the error message for now
|
169
|
+
event.respondWith(new Response(error.message, {
|
170
|
+
status: 500,
|
171
|
+
headers: {
|
172
|
+
'x-vercel-failed': 'edge-wrapper'
|
173
|
+
}
|
174
|
+
}));
|
175
|
+
}
|
176
|
+
})`;
|
177
|
+
}
|
178
|
+
catch (error) {
|
179
|
+
// We can't easily show a meaningful stack trace from ncc -> edge-runtime.
|
180
|
+
// So, stick with just the message for now.
|
181
|
+
console.log(`Failed to instantiate edge runtime: ${error.message}`);
|
182
|
+
return undefined;
|
183
|
+
}
|
184
|
+
}
|
185
|
+
async function createEdgeRuntime(userCode) {
|
186
|
+
try {
|
187
|
+
if (!userCode) {
|
188
|
+
return undefined;
|
189
|
+
}
|
190
|
+
const edgeRuntime = new edge_runtime_1.EdgeRuntime({
|
191
|
+
initialCode: userCode,
|
192
|
+
extend: (context) => {
|
193
|
+
Object.assign(context, {
|
194
|
+
__dirname: '',
|
195
|
+
module: {
|
196
|
+
exports: {},
|
197
|
+
},
|
198
|
+
});
|
199
|
+
return context;
|
200
|
+
},
|
201
|
+
});
|
202
|
+
const server = await edge_runtime_1.runServer({ runtime: edgeRuntime });
|
203
|
+
exit_hook_1.default(server.close);
|
204
|
+
return server;
|
205
|
+
}
|
206
|
+
catch (error) {
|
207
|
+
// We can't easily show a meaningful stack trace from ncc -> edge-runtime.
|
208
|
+
// So, stick with just the message for now.
|
209
|
+
console.log(`Failed to instantiate edge runtime: ${error.message}`);
|
210
|
+
return undefined;
|
211
|
+
}
|
212
|
+
}
|
213
|
+
async function createEdgeEventHandler(entrypoint) {
|
214
|
+
const userCode = await compileUserCode(entrypoint);
|
215
|
+
const server = await createEdgeRuntime(userCode);
|
162
216
|
return async function (request) {
|
217
|
+
if (!server) {
|
218
|
+
// this error state is already logged, but we have to wait until here to exit the process
|
219
|
+
// this matches the serverless function bridge launcher's behavior when
|
220
|
+
// an error is thrown in the function
|
221
|
+
process.exit(1);
|
222
|
+
}
|
163
223
|
const response = await node_fetch_1.default(server.url, {
|
224
|
+
redirect: 'manual',
|
164
225
|
method: 'post',
|
165
226
|
body: await serializeRequest(request),
|
166
227
|
});
|
228
|
+
const body = await response.text();
|
229
|
+
const isUserError = response.headers.get('x-vercel-failed') === 'edge-wrapper';
|
230
|
+
if (isUserError && response.status >= 500) {
|
231
|
+
// this error was "unhandled" from the user code's perspective
|
232
|
+
console.log(`Unhandled rejection: ${body}`);
|
233
|
+
// this matches the serverless function bridge launcher's behavior when
|
234
|
+
// an error is thrown in the function
|
235
|
+
process.exit(1);
|
236
|
+
}
|
167
237
|
return {
|
168
238
|
statusCode: response.status,
|
169
239
|
headers: response.headers.raw(),
|
170
|
-
body
|
240
|
+
body,
|
171
241
|
encoding: 'utf8',
|
172
242
|
};
|
173
243
|
};
|
174
244
|
}
|
175
245
|
const validRuntimes = ['experimental-edge'];
|
176
|
-
function parseRuntime(entrypoint) {
|
246
|
+
function parseRuntime(entrypoint, entryPointPath) {
|
177
247
|
const project = new ts_morph_1.Project();
|
178
|
-
const staticConfig = static_config_1.getConfig(project,
|
248
|
+
const staticConfig = static_config_1.getConfig(project, entryPointPath);
|
179
249
|
const runtime = staticConfig?.runtime;
|
180
250
|
if (runtime && !validRuntimes.includes(runtime)) {
|
181
|
-
throw new Error(`Invalid function runtime for "${entrypoint}": ${
|
251
|
+
throw new Error(`Invalid function runtime "${runtime}" for "${entrypoint}". Valid runtimes are: ${JSON.stringify(validRuntimes)}`);
|
182
252
|
}
|
183
253
|
return runtime;
|
184
254
|
}
|
185
|
-
async function createEventHandler(entrypoint, options) {
|
186
|
-
const
|
187
|
-
|
188
|
-
|
255
|
+
async function createEventHandler(entrypoint, config, options) {
|
256
|
+
const entryPointPath = path_1.join(process.cwd(), entrypoint);
|
257
|
+
const runtime = parseRuntime(entrypoint, entryPointPath);
|
258
|
+
// `middleware.js`/`middleware.ts` file is always run as
|
259
|
+
// an Edge Function, otherwise needs to be opted-in via
|
260
|
+
// `export const config = { runtime: 'experimental-edge' }`
|
261
|
+
if (config.middleware === true || runtime === 'experimental-edge') {
|
262
|
+
return createEdgeEventHandler(entryPointPath);
|
189
263
|
}
|
190
|
-
return createServerlessEventHandler(
|
264
|
+
return createServerlessEventHandler(entryPointPath, options);
|
191
265
|
}
|
192
266
|
let handleEvent;
|
267
|
+
let handlerEventError;
|
193
268
|
async function main() {
|
194
269
|
const config = JSON.parse(process.env.VERCEL_DEV_CONFIG || '{}');
|
195
270
|
delete process.env.VERCEL_DEV_CONFIG;
|
@@ -198,8 +273,15 @@ async function main() {
|
|
198
273
|
const shouldAddHelpers = !(config.helpers === false || buildEnv.NODEJS_HELPERS === '0');
|
199
274
|
const proxyServer = http_1.createServer(onDevRequest);
|
200
275
|
await listen(proxyServer, 0, '127.0.0.1');
|
201
|
-
|
202
|
-
|
276
|
+
try {
|
277
|
+
handleEvent = await createEventHandler(entrypoint, config, {
|
278
|
+
shouldAddHelpers,
|
279
|
+
});
|
280
|
+
}
|
281
|
+
catch (error) {
|
282
|
+
logError(error);
|
283
|
+
handlerEventError = error;
|
284
|
+
}
|
203
285
|
const address = proxyServer.address();
|
204
286
|
if (typeof process.send === 'function') {
|
205
287
|
process.send(address);
|
@@ -224,6 +306,12 @@ function rawBody(readable) {
|
|
224
306
|
}
|
225
307
|
exports.rawBody = rawBody;
|
226
308
|
async function onDevRequest(req, res) {
|
309
|
+
if (handlerEventError) {
|
310
|
+
// this error state is already logged, but we have to wait until here to exit the process
|
311
|
+
// this matches the serverless function bridge launcher's behavior when
|
312
|
+
// an error is thrown in the function
|
313
|
+
process.exit(1);
|
314
|
+
}
|
227
315
|
if (!handleEvent) {
|
228
316
|
res.statusCode = 500;
|
229
317
|
res.end('Bridge is not ready, please try again');
|
@@ -259,6 +347,6 @@ function fixConfigDev(config) {
|
|
259
347
|
}
|
260
348
|
exports.fixConfigDev = fixConfigDev;
|
261
349
|
main().catch(err => {
|
262
|
-
|
350
|
+
logError(err);
|
263
351
|
process.exit(1);
|
264
352
|
});
|
package/dist/index.js
CHANGED
@@ -307234,7 +307234,21 @@ const prepareCache = ({ repoRootPath, workPath }) => {
|
|
307234
307234
|
exports.prepareCache = prepareCache;
|
307235
307235
|
const startDevServer = async (opts) => {
|
307236
307236
|
const { entrypoint, workPath, config, meta = {} } = opts;
|
307237
|
-
const
|
307237
|
+
const entrypointPath = path_1.join(workPath, entrypoint);
|
307238
|
+
if (config.middleware === true && typeof meta.requestUrl === 'string') {
|
307239
|
+
// TODO: static config is also parsed in `dev-server.ts`.
|
307240
|
+
// we should pass in this version as an env var instead.
|
307241
|
+
const project = new ts_morph_1.Project();
|
307242
|
+
const staticConfig = static_config_1.getConfig(project, entrypointPath);
|
307243
|
+
// Middleware is a catch-all for all paths unless a `matcher` property is defined
|
307244
|
+
const matchers = new RegExp(utils_1.getRegExpFromMatchers(staticConfig?.matcher));
|
307245
|
+
if (!matchers.test(meta.requestUrl)) {
|
307246
|
+
// If the "matchers" doesn't say to handle this
|
307247
|
+
// path then skip middleware invocation
|
307248
|
+
return null;
|
307249
|
+
}
|
307250
|
+
}
|
307251
|
+
const entryDir = path_1.dirname(entrypointPath);
|
307238
307252
|
const projectTsConfig = await build_utils_1.walkParentDirs({
|
307239
307253
|
base: workPath,
|
307240
307254
|
start: entryDir,
|
@@ -307263,6 +307277,9 @@ const startDevServer = async (opts) => {
|
|
307263
307277
|
},
|
307264
307278
|
});
|
307265
307279
|
const { pid } = child;
|
307280
|
+
if (!pid) {
|
307281
|
+
throw new Error(`Child Process has no "pid" when forking: "${devServerPath}"`);
|
307282
|
+
}
|
307266
307283
|
const onMessage = once_1.default(child, 'message');
|
307267
307284
|
const onExit = once_1.default.spread(child, 'exit');
|
307268
307285
|
const result = await Promise.race([onMessage, onExit]);
|
@@ -307284,12 +307301,12 @@ const startDevServer = async (opts) => {
|
|
307284
307301
|
// Got "exit" event from child process
|
307285
307302
|
const [exitCode, signal] = result;
|
307286
307303
|
const reason = signal ? `"${signal}" signal` : `exit code ${exitCode}`;
|
307287
|
-
throw new Error(
|
307304
|
+
throw new Error(`Function \`${entrypoint}\` failed with ${reason}`);
|
307288
307305
|
}
|
307289
307306
|
};
|
307290
307307
|
exports.startDevServer = startDevServer;
|
307291
307308
|
async function doTypeCheck({ entrypoint, workPath, meta = {} }, projectTsConfig) {
|
307292
|
-
const { devCacheDir = path_1.join(workPath, '.
|
307309
|
+
const { devCacheDir = path_1.join(workPath, '.vercel', 'cache') } = meta;
|
307293
307310
|
const entrypointCacheDir = path_1.join(devCacheDir, 'node', entrypoint);
|
307294
307311
|
// In order to type-check a single file, a standalone tsconfig
|
307295
307312
|
// file needs to be created that inherits from the base one :(
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@vercel/node",
|
3
|
-
"version": "2.
|
3
|
+
"version": "2.3.0",
|
4
4
|
"license": "MIT",
|
5
5
|
"main": "./dist/index",
|
6
6
|
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
|
@@ -31,7 +31,7 @@
|
|
31
31
|
},
|
32
32
|
"dependencies": {
|
33
33
|
"@types/node": "*",
|
34
|
-
"@vercel/build-utils": "4.
|
34
|
+
"@vercel/build-utils": "4.2.0",
|
35
35
|
"@vercel/ncc": "0.24.0",
|
36
36
|
"@vercel/node-bridge": "3.0.0",
|
37
37
|
"@vercel/static-config": "2.0.1",
|
@@ -60,5 +60,5 @@
|
|
60
60
|
"source-map-support": "0.5.12",
|
61
61
|
"test-listen": "1.1.0"
|
62
62
|
},
|
63
|
-
"gitHead": "
|
63
|
+
"gitHead": "eed39913e1394477b224c38efe29429b17eeada6"
|
64
64
|
}
|