@vercel/node 2.11.0 → 2.12.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.
@@ -1,4 +1,7 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.onDevRequest = void 0;
4
7
  const entrypoint = process.env.VERCEL_DEV_ENTRYPOINT;
@@ -6,27 +9,17 @@ delete process.env.VERCEL_DEV_ENTRYPOINT;
6
9
  if (!entrypoint) {
7
10
  throw new Error('`VERCEL_DEV_ENTRYPOINT` must be defined');
8
11
  }
9
- delete process.env.TS_NODE_TRANSPILE_ONLY;
10
- delete process.env.TS_NODE_COMPILER_OPTIONS;
11
12
  const path_1 = require("path");
12
13
  const useRequire = process.env.VERCEL_DEV_IS_ESM !== '1';
14
+ const edge_handler_1 = require("./edge-functions/edge-handler");
13
15
  const http_1 = require("http");
16
+ const serverless_handler_1 = require("./serverless-functions/serverless-handler");
17
+ const utils_1 = require("./utils");
14
18
  const static_config_1 = require("@vercel/static-config");
15
19
  const ts_morph_1 = require("ts-morph");
16
- const utils_1 = require("./utils");
17
- const edge_handler_1 = require("./edge-functions/edge-handler");
18
- const serverless_handler_1 = require("./serverless-functions/serverless-handler");
19
- function listen(server, port, host) {
20
- return new Promise(resolve => {
21
- server.listen(port, host, () => {
22
- resolve();
23
- });
24
- });
25
- }
26
- function parseRuntime(entrypoint, entryPointPath) {
27
- const project = new ts_morph_1.Project();
28
- const staticConfig = static_config_1.getConfig(project, entryPointPath);
29
- const runtime = staticConfig?.runtime;
20
+ const async_listen_1 = __importDefault(require("async-listen"));
21
+ const parseConfig = (entryPointPath) => static_config_1.getConfig(new ts_morph_1.Project(), entryPointPath);
22
+ function getRuntime(runtime, entrypoint) {
30
23
  if (runtime && !utils_1.isEdgeRuntime(runtime)) {
31
24
  throw new Error(`Invalid function runtime "${runtime}" for "${entrypoint}". Valid runtimes are: ${JSON.stringify(Object.values(utils_1.EdgeRuntimes))}. Learn more: https://vercel.link/creating-edge-functions`);
32
25
  }
@@ -34,7 +27,8 @@ function parseRuntime(entrypoint, entryPointPath) {
34
27
  }
35
28
  async function createEventHandler(entrypoint, config, options) {
36
29
  const entrypointPath = path_1.join(process.cwd(), entrypoint);
37
- const runtime = parseRuntime(entrypoint, entrypointPath);
30
+ const staticConfig = parseConfig(entrypointPath);
31
+ const runtime = getRuntime(staticConfig?.runtime, entrypoint);
38
32
  // `middleware.js`/`middleware.ts` file is always run as
39
33
  // an Edge Function, otherwise needs to be opted-in via
40
34
  // `export const config = { runtime: 'edge' }`
@@ -42,6 +36,7 @@ async function createEventHandler(entrypoint, config, options) {
42
36
  return edge_handler_1.createEdgeEventHandler(entrypointPath, entrypoint, config.middleware || false, config.zeroConfig);
43
37
  }
44
38
  return serverless_handler_1.createServerlessEventHandler(entrypointPath, {
39
+ mode: staticConfig?.supportsResponseStreaming ? 'streaming' : 'buffer',
45
40
  shouldAddHelpers: options.shouldAddHelpers,
46
41
  useRequire,
47
42
  });
@@ -55,7 +50,7 @@ async function main() {
55
50
  delete process.env.VERCEL_DEV_BUILD_ENV;
56
51
  const shouldAddHelpers = !(config.helpers === false || buildEnv.NODEJS_HELPERS === '0');
57
52
  const proxyServer = http_1.createServer(onDevRequest);
58
- await listen(proxyServer, 0, '127.0.0.1');
53
+ await async_listen_1.default(proxyServer, { host: '127.0.0.1', port: 0 });
59
54
  try {
60
55
  handleEvent = await createEventHandler(entrypoint, config, {
61
56
  shouldAddHelpers,
@@ -86,14 +81,19 @@ async function onDevRequest(req, res) {
86
81
  return;
87
82
  }
88
83
  try {
89
- const result = await handleEvent(req);
90
- res.statusCode = result.statusCode;
91
- for (const [key, value] of Object.entries(result.headers)) {
92
- if (typeof value !== 'undefined') {
84
+ const { headers, body, status } = await handleEvent(req);
85
+ res.statusCode = status;
86
+ for (const [key, value] of headers) {
87
+ if (value !== undefined) {
93
88
  res.setHeader(key, value);
94
89
  }
95
90
  }
96
- res.end(Buffer.from(result.body, result.encoding));
91
+ if (body instanceof Buffer) {
92
+ res.end(body);
93
+ }
94
+ else {
95
+ body.pipe(res);
96
+ }
97
97
  }
98
98
  catch (error) {
99
99
  res.statusCode = 500;
@@ -1,49 +1,29 @@
1
1
  // provided by the edge runtime:
2
2
  /* global addEventListener */
3
3
 
4
- function buildUrl(requestDetails) {
5
- const host = requestDetails.headers['x-forwarded-host'] || '127.0.0.1';
6
- const path = requestDetails.url || '/';
7
-
8
- const allProtocols = requestDetails.headers['x-forwarded-proto'];
9
- let proto;
10
- if (allProtocols) {
11
- // handle multi-protocol like: https,http://...
12
- proto = allProtocols.split(/\b/).shift();
13
- } else {
14
- proto = 'http';
15
- }
16
-
17
- return `${proto}://${host}${path}`;
4
+ function getUrl(url, headers) {
5
+ const urlObj = new URL(url);
6
+ const protocol = headers.get('x-forwarded-proto');
7
+ if (protocol) urlObj.protocol = protocol.split(/\b/).shift();
8
+ urlObj.host = headers.get('x-forwarded-host');
9
+ urlObj.port = headers.get('x-forwarded-port');
10
+ return urlObj.toString();
18
11
  }
19
12
 
20
- async function respond(
21
- userEdgeHandler,
22
- requestDetails,
23
- event,
24
- options,
25
- dependencies
26
- ) {
13
+ async function respond(userEdgeHandler, event, options, dependencies) {
27
14
  const { Request, Response } = dependencies;
28
15
  const { isMiddleware } = options;
29
-
30
- let body;
31
-
32
- if (requestDetails.method !== 'GET' && requestDetails.method !== 'HEAD') {
33
- if (requestDetails.body) {
34
- body = Uint8Array.from(atob(requestDetails.body), c => c.charCodeAt(0));
35
- }
36
- }
37
-
38
- const request = new Request(buildUrl(requestDetails), {
39
- headers: requestDetails.headers,
40
- method: requestDetails.method,
41
- body: body,
42
- });
43
-
44
- event.request = request;
45
-
46
- let response = await userEdgeHandler(event.request, event);
16
+ event.request.headers.set(
17
+ 'host',
18
+ event.request.headers.get('x-forwarded-host')
19
+ );
20
+ let response = await userEdgeHandler(
21
+ new Request(
22
+ getUrl(event.request.url, event.request.headers),
23
+ event.request
24
+ ),
25
+ event
26
+ );
47
27
 
48
28
  if (!response) {
49
29
  if (isMiddleware) {
@@ -85,10 +65,8 @@ async function parseRequestEvent(event) {
85
65
  function registerFetchListener(userEdgeHandler, options, dependencies) {
86
66
  addEventListener('fetch', async event => {
87
67
  try {
88
- const requestDetails = await parseRequestEvent(event);
89
68
  const response = await respond(
90
69
  userEdgeHandler,
91
- requestDetails,
92
70
  event,
93
71
  options,
94
72
  dependencies
@@ -100,11 +78,10 @@ function registerFetchListener(userEdgeHandler, options, dependencies) {
100
78
  });
101
79
  }
102
80
 
103
- // for testing:
104
81
  module.exports = {
105
- buildUrl,
106
- respond,
107
- toResponseError,
82
+ getUrl,
108
83
  parseRequestEvent,
109
84
  registerFetchListener,
85
+ respond,
86
+ toResponseError,
110
87
  };
@@ -1,4 +1,4 @@
1
1
  /// <reference types="node" />
2
- import { IncomingMessage } from 'http';
3
- import { VercelProxyResponse } from '@vercel/node-bridge/types';
2
+ import type { VercelProxyResponse } from '../types';
3
+ import type { IncomingMessage } from 'http';
4
4
  export declare function createEdgeEventHandler(entrypointFullPath: string, entrypointRelativePath: string, isMiddleware: boolean, isZeroConfig?: boolean): Promise<(request: IncomingMessage) => Promise<VercelProxyResponse>>;
@@ -1,34 +1,44 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
11
+ }) : function(o, v) {
12
+ o["default"] = v;
13
+ });
14
+ var __importStar = (this && this.__importStar) || function (mod) {
15
+ if (mod && mod.__esModule) return mod;
16
+ var result = {};
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
+ __setModuleDefault(result, mod);
19
+ return result;
20
+ };
2
21
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
22
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
23
  };
5
24
  Object.defineProperty(exports, "__esModule", { value: true });
6
25
  exports.createEdgeEventHandler = void 0;
7
- const build_utils_1 = require("@vercel/build-utils");
8
- const exit_hook_1 = __importDefault(require("exit-hook"));
9
- const edge_runtime_1 = require("edge-runtime");
10
- const esbuild_1 = __importDefault(require("esbuild"));
11
- const node_fetch_1 = __importDefault(require("node-fetch"));
12
26
  const edge_wasm_plugin_1 = require("./edge-wasm-plugin");
13
- const utils_1 = require("../utils");
14
- const fs_1 = require("fs");
15
27
  const edge_node_compat_plugin_1 = require("./edge-node-compat-plugin");
28
+ const edge_runtime_1 = require("edge-runtime");
29
+ const node_fetch_1 = __importStar(require("node-fetch"));
30
+ const error_utils_1 = require("@vercel/error-utils");
31
+ const fs_1 = require("fs");
32
+ const utils_1 = require("../utils");
33
+ const esbuild_1 = __importDefault(require("esbuild"));
34
+ const exit_hook_1 = __importDefault(require("exit-hook"));
35
+ const url_1 = require("url");
16
36
  const NODE_VERSION_MAJOR = process.version.match(/^v(\d+)\.\d+/)?.[1];
17
37
  const NODE_VERSION_IDENTIFIER = `node${NODE_VERSION_MAJOR}`;
18
38
  if (!NODE_VERSION_MAJOR) {
19
39
  throw new Error(`Unable to determine current node version: process.version=${process.version}`);
20
40
  }
21
41
  const edgeHandlerTemplate = fs_1.readFileSync(`${__dirname}/edge-handler-template.js`);
22
- async function serializeRequest(message) {
23
- const bodyBuffer = await build_utils_1.streamToBuffer(message);
24
- const body = bodyBuffer.toString('base64');
25
- return JSON.stringify({
26
- url: message.url,
27
- method: message.method,
28
- headers: message.headers,
29
- body,
30
- });
31
- }
32
42
  async function compileUserCode(entrypointFullPath, entrypointRelativePath, isMiddleware) {
33
43
  const { wasmAssets, plugin: edgeWasmPlugin } = edge_wasm_plugin_1.createEdgeWasmPlugin();
34
44
  const nodeCompatPlugin = edge_node_compat_plugin_1.createNodeCompatPlugin();
@@ -43,7 +53,20 @@ async function compileUserCode(entrypointFullPath, entrypointRelativePath, isMid
43
53
  sourcemap: 'inline',
44
54
  legalComments: 'none',
45
55
  bundle: true,
46
- plugins: [edgeWasmPlugin, nodeCompatPlugin.plugin],
56
+ plugins: [
57
+ edgeWasmPlugin,
58
+ nodeCompatPlugin.plugin,
59
+ {
60
+ name: 'import.meta.url',
61
+ setup({ onLoad }) {
62
+ onLoad({ filter: /\.[cm]?js$/, namespace: 'file' }, args => {
63
+ let code = fs_1.readFileSync(args.path, 'utf8');
64
+ code = code.replace(/\bimport\.meta\.url\b/g, JSON.stringify(url_1.pathToFileURL(__filename)));
65
+ return { contents: code };
66
+ });
67
+ },
68
+ },
69
+ ],
47
70
  entryPoints: [entrypointFullPath],
48
71
  write: false,
49
72
  format: 'cjs',
@@ -71,14 +94,8 @@ async function compileUserCode(entrypointFullPath, entrypointRelativePath, isMid
71
94
 
72
95
  // edge handler
73
96
  ${edgeHandlerTemplate};
74
- const dependencies = {
75
- Request,
76
- Response
77
- };
78
- const options = {
79
- isMiddleware,
80
- entrypointLabel
81
- };
97
+ const dependencies = { Request, Response };
98
+ const options = { isMiddleware, entrypointLabel };
82
99
  registerFetchListener(userEdgeHandler, options, dependencies);
83
100
  `;
84
101
  return {
@@ -91,20 +108,21 @@ async function compileUserCode(entrypointFullPath, entrypointRelativePath, isMid
91
108
  // We can't easily show a meaningful stack trace from ncc -> edge-runtime.
92
109
  // So, stick with just the message for now.
93
110
  console.error(`Failed to compile user code for edge runtime.`);
94
- utils_1.logError(error);
111
+ if (error_utils_1.isError(error))
112
+ utils_1.logError(error);
95
113
  return undefined;
96
114
  }
97
115
  }
98
- async function createEdgeRuntime(params) {
116
+ async function createEdgeRuntimeServer(params) {
99
117
  try {
100
118
  if (!params) {
101
119
  return undefined;
102
120
  }
103
121
  const wasmBindings = await params.wasmAssets.getContext();
104
122
  const nodeCompatBindings = params.nodeCompatBindings.getContext();
105
- const edgeRuntime = new edge_runtime_1.EdgeRuntime({
123
+ const runtime = new edge_runtime_1.EdgeRuntime({
106
124
  initialCode: params.userCode,
107
- extend: (context) => {
125
+ extend: context => {
108
126
  Object.assign(context, {
109
127
  // This is required for esbuild wrapping logic to resolve
110
128
  module: {},
@@ -122,8 +140,8 @@ async function createEdgeRuntime(params) {
122
140
  return context;
123
141
  },
124
142
  });
125
- const server = await edge_runtime_1.runServer({ runtime: edgeRuntime });
126
- exit_hook_1.default(server.close);
143
+ const server = await edge_runtime_1.runServer({ runtime });
144
+ exit_hook_1.default(() => server.close());
127
145
  return server;
128
146
  }
129
147
  catch (error) {
@@ -136,7 +154,7 @@ async function createEdgeRuntime(params) {
136
154
  }
137
155
  async function createEdgeEventHandler(entrypointFullPath, entrypointRelativePath, isMiddleware, isZeroConfig) {
138
156
  const userCode = await compileUserCode(entrypointFullPath, entrypointRelativePath, isMiddleware);
139
- const server = await createEdgeRuntime(userCode);
157
+ const server = await createEdgeRuntimeServer(userCode);
140
158
  return async function (request) {
141
159
  if (!server) {
142
160
  // this error state is already logged, but we have to wait until here to exit the process
@@ -144,14 +162,20 @@ async function createEdgeEventHandler(entrypointFullPath, entrypointRelativePath
144
162
  // an error is thrown in the function
145
163
  process.exit(1);
146
164
  }
147
- const response = await node_fetch_1.default(server.url, {
165
+ const headers = new node_fetch_1.Headers(request.headers);
166
+ const body = await utils_1.serializeBody(request);
167
+ if (body !== undefined)
168
+ headers.set('content-length', String(body.length));
169
+ const url = new URL(request.url ?? '/', server.url);
170
+ const response = await node_fetch_1.default(url, {
171
+ body,
172
+ headers,
173
+ method: request.method,
148
174
  redirect: 'manual',
149
- method: 'post',
150
- body: await serializeRequest(request),
151
175
  });
152
- const body = await response.text();
153
176
  const isUserError = response.headers.get('x-vercel-failed') === 'edge-wrapper';
154
177
  if (isUserError && response.status >= 500) {
178
+ const body = await response.text();
155
179
  // We can't currently get a real stack trace from the Edge Function error,
156
180
  // but we can fake a basic one that is still usefult to the user.
157
181
  const fakeStackTrace = ` at (${entrypointRelativePath})`;
@@ -162,9 +186,9 @@ async function createEdgeEventHandler(entrypointFullPath, entrypointRelativePath
162
186
  process.exit(1);
163
187
  }
164
188
  return {
165
- statusCode: response.status,
166
- headers: response.headers.raw(),
167
- body,
189
+ status: response.status,
190
+ headers: response.headers,
191
+ body: response.body,
168
192
  encoding: 'utf8',
169
193
  };
170
194
  };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  /// <reference types="node" />
2
2
  import { ServerResponse, IncomingMessage } from 'http';
3
+ import type { Headers } from 'node-fetch';
3
4
  export declare type VercelRequestCookies = {
4
5
  [key: string]: string;
5
6
  };
@@ -31,3 +32,9 @@ export declare type NowRequest = VercelRequest;
31
32
  export declare type NowResponse = VercelResponse;
32
33
  /** @deprecated Use VercelApiHandler instead. */
33
34
  export declare type NowApiHandler = VercelApiHandler;
35
+ export interface VercelProxyResponse {
36
+ status: number;
37
+ headers: Headers;
38
+ body: Buffer | NodeJS.ReadableStream;
39
+ encoding: BufferEncoding;
40
+ }
package/dist/index.js CHANGED
@@ -140129,6 +140129,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
140129
140129
  };
140130
140130
  Object.defineProperty(exports, "__esModule", ({ value: true }));
140131
140131
  exports.startDevServer = exports.prepareCache = exports.build = exports.version = exports.shouldServe = void 0;
140132
+ const error_utils_1 = __webpack_require__(27915);
140132
140133
  const url_1 = __importDefault(__webpack_require__(78835));
140133
140134
  const child_process_1 = __webpack_require__(63129);
140134
140135
  const fs_1 = __webpack_require__(35747);
@@ -140264,13 +140265,14 @@ async function compile(workPath, baseDir, entrypointPath, config, nodeVersion, i
140264
140265
  sourceCache.set(relPath, source);
140265
140266
  return source;
140266
140267
  }
140267
- catch (e) {
140268
- if (e.code === 'ENOENT' || e.code === 'EISDIR') {
140268
+ catch (error) {
140269
+ if (error_utils_1.isErrnoException(error) &&
140270
+ (error.code === 'ENOENT' || error.code === 'EISDIR')) {
140269
140271
  // `null` represents a not found
140270
140272
  sourceCache.set(relPath, null);
140271
140273
  return null;
140272
140274
  }
140273
- throw e;
140275
+ throw error;
140274
140276
  }
140275
140277
  },
140276
140278
  });
@@ -140495,8 +140497,8 @@ const startDevServer = async (opts) => {
140495
140497
  filename: 'package.json',
140496
140498
  });
140497
140499
  const pkg = pathToPkg ? require_(pathToPkg) : {};
140498
- const isTypescript = ['.ts', '.tsx', '.mts', '.cts'].includes(ext);
140499
- const maybeTranspile = isTypescript || !['.cjs', '.mjs'].includes(ext);
140500
+ const isTypeScript = ['.ts', '.tsx', '.mts', '.cts'].includes(ext);
140501
+ const maybeTranspile = isTypeScript || !['.cjs', '.mjs'].includes(ext);
140500
140502
  const isEsm = ext === '.mjs' ||
140501
140503
  ext === '.mts' ||
140502
140504
  (pkg.type === 'module' && ['.js', '.ts', '.tsx'].includes(ext));
@@ -140528,10 +140530,10 @@ const startDevServer = async (opts) => {
140528
140530
  try {
140529
140531
  tsConfig = ts.readConfigFile(pathToTsConfig, ts.sys.readFile).config;
140530
140532
  }
140531
- catch (err) {
140532
- if (err.code !== 'ENOENT') {
140533
+ catch (error) {
140534
+ if (error_utils_1.isErrnoException(error) && error.code !== 'ENOENT') {
140533
140535
  console.error(`Error while parsing "${pathToTsConfig}"`);
140534
- throw err;
140536
+ throw error;
140535
140537
  }
140536
140538
  }
140537
140539
  }
@@ -140552,7 +140554,7 @@ const startDevServer = async (opts) => {
140552
140554
  entrypoint,
140553
140555
  require_,
140554
140556
  isEsm,
140555
- isTypeScript: isTypescript,
140557
+ isTypeScript,
140556
140558
  maybeTranspile,
140557
140559
  meta,
140558
140560
  tsConfig,
@@ -140561,7 +140563,7 @@ const startDevServer = async (opts) => {
140561
140563
  const message = await fork_dev_server_1.readMessage(child);
140562
140564
  if (message.state === 'message') {
140563
140565
  // "message" event
140564
- if (isTypescript) {
140566
+ if (isTypeScript) {
140565
140567
  // Invoke `tsc --noEmit` asynchronously in the background, so
140566
140568
  // that the HTTP request is not blocked by the type checking.
140567
140569
  doTypeCheck(opts, pathToTsConfig).catch((err) => {
@@ -140601,11 +140603,9 @@ async function doTypeCheck({ entrypoint, workPath, meta = {} }, projectTsConfig)
140601
140603
  await fs_1.promises.mkdir(entrypointCacheDir, { recursive: true });
140602
140604
  await fs_1.promises.writeFile(tsconfigPath, json, { flag: 'wx' });
140603
140605
  }
140604
- catch (err) {
140605
- // Don't throw if the file already exists
140606
- if (err.code !== 'EEXIST') {
140607
- throw err;
140608
- }
140606
+ catch (error) {
140607
+ if (error_utils_1.isErrnoException(error) && error.code !== 'EEXIST')
140608
+ throw error;
140609
140609
  }
140610
140610
  const child = child_process_1.spawn(process.execPath, [
140611
140611
  tscPath,
@@ -141016,10 +141016,10 @@ function filterDiagnostics(diagnostics, ignore) {
141016
141016
  "use strict";
141017
141017
 
141018
141018
  Object.defineProperty(exports, "__esModule", ({ value: true }));
141019
- exports.isEdgeRuntime = exports.EdgeRuntimes = exports.logError = exports.entrypointToOutputPath = exports.getRegExpFromMatchers = void 0;
141020
- const path_1 = __webpack_require__(85622);
141021
- const path_to_regexp_1 = __webpack_require__(91344);
141019
+ exports.serializeBody = exports.isEdgeRuntime = exports.EdgeRuntimes = exports.logError = exports.entrypointToOutputPath = exports.getRegExpFromMatchers = void 0;
141022
141020
  const build_utils_1 = __webpack_require__(63445);
141021
+ const path_to_regexp_1 = __webpack_require__(91344);
141022
+ const path_1 = __webpack_require__(85622);
141023
141023
  function getRegExpFromMatchers(matcherOrMatchers) {
141024
141024
  if (!matcherOrMatchers) {
141025
141025
  return '^/.*$';
@@ -141082,6 +141082,12 @@ function isEdgeRuntime(runtime) {
141082
141082
  Object.values(EdgeRuntimes).includes(runtime));
141083
141083
  }
141084
141084
  exports.isEdgeRuntime = isEdgeRuntime;
141085
+ async function serializeBody(request) {
141086
+ return request.method !== 'GET' && request.method !== 'HEAD'
141087
+ ? await build_utils_1.streamToBuffer(request)
141088
+ : undefined;
141089
+ }
141090
+ exports.serializeBody = serializeBody;
141085
141091
 
141086
141092
 
141087
141093
  /***/ }),
@@ -141157,6 +141163,14 @@ module.exports = require("@vercel/build-utils");
141157
141163
 
141158
141164
  /***/ }),
141159
141165
 
141166
+ /***/ 27915:
141167
+ /***/ ((module) => {
141168
+
141169
+ "use strict";
141170
+ module.exports = require("@vercel/error-utils");
141171
+
141172
+ /***/ }),
141173
+
141160
141174
  /***/ 27167:
141161
141175
  /***/ ((module) => {
141162
141176
 
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+
3
+ const { pathToFileURL } = require('url');
4
+ const { isAbsolute } = require('path');
5
+
6
+ function dynamicImport(filepath) {
7
+ const id = isAbsolute(filepath) ? pathToFileURL(filepath).href : filepath;
8
+ return import(id);
9
+ }
10
+
11
+ module.exports = {
12
+ dynamicImport,
13
+ };
@@ -0,0 +1,22 @@
1
+ /// <reference types="node" />
2
+ import type { ServerResponse, IncomingMessage } from 'http';
3
+ declare type VercelRequestCookies = {
4
+ [key: string]: string;
5
+ };
6
+ declare type VercelRequestQuery = {
7
+ [key: string]: string | string[];
8
+ };
9
+ declare type VercelRequestBody = any;
10
+ export declare type VercelRequest = IncomingMessage & {
11
+ query: VercelRequestQuery;
12
+ cookies: VercelRequestCookies;
13
+ body: VercelRequestBody;
14
+ };
15
+ export declare type VercelResponse = ServerResponse & {
16
+ send: (body: any) => VercelResponse;
17
+ json: (jsonBody: any) => VercelResponse;
18
+ status: (statusCode: number) => VercelResponse;
19
+ redirect: (statusOrUrl: string | number, url?: string) => VercelResponse;
20
+ };
21
+ export declare function addHelpers(_req: IncomingMessage, _res: ServerResponse): Promise<void>;
22
+ export {};
@@ -0,0 +1,221 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.addHelpers = void 0;
4
+ const utils_1 = require("../utils");
5
+ const stream_1 = require("stream");
6
+ class ApiError extends Error {
7
+ constructor(statusCode, message) {
8
+ super(message);
9
+ this.statusCode = statusCode;
10
+ }
11
+ }
12
+ function getBodyParser(body, contentType) {
13
+ return function parseBody() {
14
+ const { parse: parseContentType } = require('content-type');
15
+ const { type } = parseContentType(contentType);
16
+ if (type === 'application/json') {
17
+ try {
18
+ const str = body.toString();
19
+ return str ? JSON.parse(str) : {};
20
+ }
21
+ catch (error) {
22
+ throw new ApiError(400, 'Invalid JSON');
23
+ }
24
+ }
25
+ if (type === 'application/octet-stream')
26
+ return body;
27
+ if (type === 'application/x-www-form-urlencoded') {
28
+ const { parse: parseQS } = require('querystring');
29
+ // note: querystring.parse does not produce an iterable object
30
+ // https://nodejs.org/api/querystring.html#querystring_querystring_parse_str_sep_eq_options
31
+ return parseQS(body.toString());
32
+ }
33
+ if (type === 'text/plain')
34
+ return body.toString();
35
+ return undefined;
36
+ };
37
+ }
38
+ function getQueryParser({ url = '/' }) {
39
+ return function parseQuery() {
40
+ const { parse: parseURL } = require('url');
41
+ return parseURL(url, true).query;
42
+ };
43
+ }
44
+ function getCookieParser(req) {
45
+ return function parseCookie() {
46
+ const header = req.headers.cookie;
47
+ if (!header)
48
+ return {};
49
+ const { parse } = require('cookie');
50
+ return parse(Array.isArray(header) ? header.join(';') : header);
51
+ };
52
+ }
53
+ function status(res, statusCode) {
54
+ res.statusCode = statusCode;
55
+ return res;
56
+ }
57
+ function setCharset(type, charset) {
58
+ const { parse, format } = require('content-type');
59
+ const parsed = parse(type);
60
+ parsed.parameters.charset = charset;
61
+ return format(parsed);
62
+ }
63
+ function redirect(res, statusOrUrl, url) {
64
+ if (typeof statusOrUrl === 'string') {
65
+ url = statusOrUrl;
66
+ statusOrUrl = 307;
67
+ }
68
+ if (typeof statusOrUrl !== 'number' || typeof url !== 'string') {
69
+ 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').`);
70
+ }
71
+ res.writeHead(statusOrUrl, { Location: url }).end();
72
+ return res;
73
+ }
74
+ function setLazyProp(req, prop, getter) {
75
+ const opts = { configurable: true, enumerable: true };
76
+ const optsReset = { ...opts, writable: true };
77
+ Object.defineProperty(req, prop, {
78
+ ...opts,
79
+ get: () => {
80
+ const value = getter();
81
+ // we set the property on the object to avoid recalculating it
82
+ Object.defineProperty(req, prop, { ...optsReset, value });
83
+ return value;
84
+ },
85
+ set: value => {
86
+ Object.defineProperty(req, prop, { ...optsReset, value });
87
+ },
88
+ });
89
+ }
90
+ function createETag(body, encoding) {
91
+ const etag = require('etag');
92
+ const buf = !Buffer.isBuffer(body) ? Buffer.from(body, encoding) : body;
93
+ return etag(buf, { weak: true });
94
+ }
95
+ function json(req, res, jsonBody) {
96
+ const body = JSON.stringify(jsonBody);
97
+ if (!res.getHeader('content-type')) {
98
+ res.setHeader('content-type', 'application/json; charset=utf-8');
99
+ }
100
+ return send(req, res, body);
101
+ }
102
+ function send(req, res, body) {
103
+ let chunk = body;
104
+ let encoding;
105
+ switch (typeof chunk) {
106
+ // string defaulting to html
107
+ case 'string':
108
+ if (!res.getHeader('content-type')) {
109
+ res.setHeader('content-type', 'text/html');
110
+ }
111
+ break;
112
+ case 'boolean':
113
+ case 'number':
114
+ case 'object':
115
+ if (chunk === null) {
116
+ chunk = '';
117
+ }
118
+ else if (Buffer.isBuffer(chunk)) {
119
+ if (!res.getHeader('content-type')) {
120
+ res.setHeader('content-type', 'application/octet-stream');
121
+ }
122
+ }
123
+ else {
124
+ return json(req, res, chunk);
125
+ }
126
+ break;
127
+ }
128
+ // write strings in utf-8
129
+ if (typeof chunk === 'string') {
130
+ encoding = 'utf8';
131
+ // reflect this in content-type
132
+ const type = res.getHeader('content-type');
133
+ if (typeof type === 'string') {
134
+ res.setHeader('content-type', setCharset(type, 'utf-8'));
135
+ }
136
+ }
137
+ // populate Content-Length
138
+ let len;
139
+ if (chunk !== undefined) {
140
+ if (Buffer.isBuffer(chunk)) {
141
+ // get length of Buffer
142
+ len = chunk.length;
143
+ }
144
+ else if (typeof chunk === 'string') {
145
+ if (chunk.length < 1000) {
146
+ // just calculate length small chunk
147
+ len = Buffer.byteLength(chunk, encoding);
148
+ }
149
+ else {
150
+ // convert chunk to Buffer and calculate
151
+ const buf = Buffer.from(chunk, encoding);
152
+ len = buf.length;
153
+ chunk = buf;
154
+ encoding = undefined;
155
+ }
156
+ }
157
+ else {
158
+ throw new Error('`body` is not a valid string, object, boolean, number, Stream, or Buffer');
159
+ }
160
+ if (len !== undefined) {
161
+ res.setHeader('content-length', len);
162
+ }
163
+ }
164
+ // populate ETag
165
+ let etag;
166
+ if (!res.getHeader('etag') &&
167
+ len !== undefined &&
168
+ (etag = createETag(chunk, encoding))) {
169
+ res.setHeader('etag', etag);
170
+ }
171
+ // strip irrelevant headers
172
+ if (204 === res.statusCode || 304 === res.statusCode) {
173
+ res.removeHeader('Content-Type');
174
+ res.removeHeader('Content-Length');
175
+ res.removeHeader('Transfer-Encoding');
176
+ chunk = '';
177
+ }
178
+ if (req.method === 'HEAD') {
179
+ // skip body for HEAD
180
+ res.end();
181
+ }
182
+ else if (encoding) {
183
+ // respond with encoding
184
+ res.end(chunk, encoding);
185
+ }
186
+ else {
187
+ // respond without encoding
188
+ res.end(chunk);
189
+ }
190
+ return res;
191
+ }
192
+ function restoreBody(req, body) {
193
+ const replicateBody = new stream_1.PassThrough();
194
+ const on = replicateBody.on.bind(replicateBody);
195
+ const originalOn = req.on.bind(req);
196
+ req.read = replicateBody.read.bind(replicateBody);
197
+ req.on = req.addListener = (name, cb) =>
198
+ // @ts-expect-error
199
+ name === 'data' || name === 'end' ? on(name, cb) : originalOn(name, cb);
200
+ replicateBody.write(body);
201
+ replicateBody.end();
202
+ }
203
+ async function readBody(req) {
204
+ const body = (await utils_1.serializeBody(req)) || Buffer.from('');
205
+ restoreBody(req, body);
206
+ return body;
207
+ }
208
+ async function addHelpers(_req, _res) {
209
+ const req = _req;
210
+ const res = _res;
211
+ setLazyProp(req, 'cookies', getCookieParser(req));
212
+ setLazyProp(req, 'query', getQueryParser(req));
213
+ const contentType = req.headers['content-type'];
214
+ const body = contentType === undefined ? Buffer.from('') : await readBody(req);
215
+ setLazyProp(req, 'body', getBodyParser(body, contentType));
216
+ res.status = statusCode => status(res, statusCode);
217
+ res.redirect = (statusOrUrl, url) => redirect(res, statusOrUrl, url);
218
+ res.send = body => send(req, res, body);
219
+ res.json = jsonBody => json(req, res, jsonBody);
220
+ }
221
+ exports.addHelpers = addHelpers;
@@ -1,7 +1,10 @@
1
1
  /// <reference types="node" />
2
- import { IncomingMessage } from 'http';
3
- import { VercelProxyResponse } from '@vercel/node-bridge/types';
4
- export declare function createServerlessEventHandler(entrypoint: string, options: {
2
+ import type { IncomingMessage } from 'http';
3
+ import type { VercelProxyResponse } from '../types';
4
+ declare type ServerlessServerOptions = {
5
5
  shouldAddHelpers: boolean;
6
6
  useRequire: boolean;
7
- }): Promise<(request: IncomingMessage) => Promise<VercelProxyResponse>>;
7
+ mode: 'streaming' | 'buffer';
8
+ };
9
+ export declare function createServerlessEventHandler(entrypointPath: string, options: ServerlessServerOptions): Promise<(request: IncomingMessage) => Promise<VercelProxyResponse>>;
10
+ export {};
@@ -1,47 +1,70 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.createServerlessEventHandler = void 0;
4
- const launcher_js_1 = require("@vercel/node-bridge/launcher.js");
5
- function rawBody(readable) {
6
- return new Promise((resolve, reject) => {
7
- let bytes = 0;
8
- const chunks = [];
9
- readable.on('error', reject);
10
- readable.on('data', chunk => {
11
- chunks.push(chunk);
12
- bytes += chunk.length;
13
- });
14
- readable.on('end', () => {
15
- resolve(Buffer.concat(chunks, bytes));
16
- });
7
+ const helpers_1 = require("./helpers");
8
+ const http_1 = require("http");
9
+ // @ts-expect-error
10
+ const dynamic_import_js_1 = require("./dynamic-import.js");
11
+ const utils_1 = require("../utils");
12
+ const build_utils_1 = require("@vercel/build-utils");
13
+ const exit_hook_1 = __importDefault(require("exit-hook"));
14
+ const node_fetch_1 = __importDefault(require("node-fetch"));
15
+ const async_listen_1 = __importDefault(require("async-listen"));
16
+ async function createServerlessServer(userCode, options) {
17
+ const server = http_1.createServer(async (req, res) => {
18
+ if (options.shouldAddHelpers)
19
+ await helpers_1.addHelpers(req, res);
20
+ return userCode(req, res);
17
21
  });
22
+ exit_hook_1.default(() => server.close());
23
+ return { url: await async_listen_1.default(server) };
18
24
  }
19
- async function createServerlessEventHandler(entrypoint, options) {
20
- const launcher = launcher_js_1.getVercelLauncher({
21
- entrypointPath: entrypoint,
22
- helpersPath: './helpers.js',
23
- shouldAddHelpers: options.shouldAddHelpers,
24
- useRequire: options.useRequire,
25
- // not used
26
- bridgePath: '',
27
- sourcemapSupportPath: '',
28
- });
29
- const bridge = launcher();
25
+ async function compileUserCode(entrypointPath, options) {
26
+ let fn = options.useRequire
27
+ ? require(entrypointPath)
28
+ : await dynamic_import_js_1.dynamicImport(entrypointPath);
29
+ /**
30
+ * In some cases we might have nested default props due to TS => JS
31
+ */
32
+ for (let i = 0; i < 5; i++) {
33
+ if (fn.default)
34
+ fn = fn.default;
35
+ }
36
+ return fn;
37
+ }
38
+ async function createServerlessEventHandler(entrypointPath, options) {
39
+ const userCode = await compileUserCode(entrypointPath, options);
40
+ const server = await createServerlessServer(userCode, options);
30
41
  return async function (request) {
31
- const body = await rawBody(request);
32
- const event = {
33
- Action: 'Invoke',
34
- body: JSON.stringify({
35
- method: request.method,
36
- path: request.url,
37
- headers: request.headers,
38
- encoding: 'base64',
39
- body: body.toString('base64'),
40
- }),
41
- };
42
- return bridge.launcher(event, {
43
- callbackWaitsForEmptyEventLoop: false,
42
+ const url = new URL(request.url ?? '/', server.url);
43
+ const response = await node_fetch_1.default(url, {
44
+ body: await utils_1.serializeBody(request),
45
+ headers: {
46
+ ...request.headers,
47
+ host: request.headers['x-forwarded-host'],
48
+ },
49
+ method: request.method,
50
+ redirect: 'manual',
44
51
  });
52
+ let body;
53
+ if (options.mode === 'streaming') {
54
+ body = response.body;
55
+ }
56
+ else {
57
+ body = await build_utils_1.streamToBuffer(response.body);
58
+ response.headers.delete('transfer-encoding');
59
+ //@ts-expect-error
60
+ response.headers.set('content-length', body.length);
61
+ }
62
+ return {
63
+ status: response.status,
64
+ headers: response.headers,
65
+ body,
66
+ encoding: 'utf8',
67
+ };
45
68
  };
46
69
  }
47
70
  exports.createServerlessEventHandler = createServerlessEventHandler;
package/dist/utils.js CHANGED
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isEdgeRuntime = exports.EdgeRuntimes = exports.logError = exports.entrypointToOutputPath = exports.getRegExpFromMatchers = void 0;
4
- const path_1 = require("path");
5
- const path_to_regexp_1 = require("path-to-regexp");
3
+ exports.serializeBody = exports.isEdgeRuntime = exports.EdgeRuntimes = exports.logError = exports.entrypointToOutputPath = exports.getRegExpFromMatchers = void 0;
6
4
  const build_utils_1 = require("@vercel/build-utils");
5
+ const path_to_regexp_1 = require("path-to-regexp");
6
+ const path_1 = require("path");
7
7
  function getRegExpFromMatchers(matcherOrMatchers) {
8
8
  if (!matcherOrMatchers) {
9
9
  return '^/.*$';
@@ -66,3 +66,9 @@ function isEdgeRuntime(runtime) {
66
66
  Object.values(EdgeRuntimes).includes(runtime));
67
67
  }
68
68
  exports.isEdgeRuntime = isEdgeRuntime;
69
+ async function serializeBody(request) {
70
+ return request.method !== 'GET' && request.method !== 'HEAD'
71
+ ? await build_utils_1.streamToBuffer(request)
72
+ : undefined;
73
+ }
74
+ exports.serializeBody = serializeBody;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/node",
3
- "version": "2.11.0",
3
+ "version": "2.12.0",
4
4
  "license": "Apache-2.0",
5
5
  "main": "./dist/index",
6
6
  "homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
@@ -11,7 +11,7 @@
11
11
  },
12
12
  "scripts": {
13
13
  "build": "node build",
14
- "test": "jest --env node --verbose --bail --runInBand",
14
+ "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --env node --verbose --bail --runInBand",
15
15
  "test-unit": "pnpm test test/unit",
16
16
  "test-e2e": "pnpm test test/integration"
17
17
  },
@@ -22,9 +22,11 @@
22
22
  "@edge-runtime/vm": "2.0.0",
23
23
  "@types/node": "14.18.33",
24
24
  "@vercel/build-utils": "6.7.1",
25
+ "@vercel/error-utils": "1.0.8",
25
26
  "@vercel/node-bridge": "4.0.1",
26
27
  "@vercel/static-config": "2.0.16",
27
- "edge-runtime": "2.0.0",
28
+ "async-listen": "1.2.0",
29
+ "edge-runtime": "2.1.4",
28
30
  "esbuild": "0.14.47",
29
31
  "exit-hook": "2.2.1",
30
32
  "node-fetch": "2.6.7",
@@ -48,11 +50,12 @@
48
50
  "@vercel/nft": "0.22.5",
49
51
  "content-type": "1.0.4",
50
52
  "cookie": "0.4.0",
53
+ "cross-env": "7.0.3",
51
54
  "etag": "1.8.1",
52
55
  "execa": "3.2.0",
53
56
  "fs-extra": "11.1.0",
54
57
  "source-map-support": "0.5.12",
55
58
  "test-listen": "1.1.0"
56
59
  },
57
- "gitHead": "a63b9d960bec291e152308c30074843aab802c8d"
60
+ "gitHead": "7082da84511740ea0304b5f93b0073bbb3f3a9ed"
58
61
  }