@vercel/client 17.6.4 → 18.0.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.
@@ -146,8 +146,7 @@ async function* checkDeploymentStatus(deployment, clientOptions) {
146
146
  {
147
147
  apiUrl,
148
148
  userAgent,
149
- agent: clientOptions.agent,
150
- // node-fetch's bundled AbortSignal type predates the native one.
149
+ dispatcher: clientOptions.dispatcher,
151
150
  signal: aliasAssignedSignal
152
151
  }
153
152
  );
@@ -1,12 +1,11 @@
1
- import type { Agent } from 'http';
2
- import type { ArchiveFormat, DeploymentEventType } from './types';
1
+ import type { ArchiveFormat, DeploymentEventType, FetchDispatcher } from './types';
3
2
  /**
4
3
  * Continues a manual deployment by uploading build outputs and calling the
5
4
  * continue endpoint. Waits for READY state unless the caller stops consuming
6
5
  * events early.
7
6
  */
8
7
  export declare function continueDeployment(options: {
9
- agent?: Agent;
8
+ dispatcher?: FetchDispatcher;
10
9
  apiUrl?: string;
11
10
  debug?: boolean;
12
11
  deploymentId: string;
package/dist/continue.js CHANGED
@@ -84,7 +84,7 @@ async function* continueDeployment(options) {
84
84
  teamId: options.teamId,
85
85
  apiUrl: options.apiUrl,
86
86
  userAgent: options.userAgent,
87
- agent: options.agent,
87
+ dispatcher: options.dispatcher,
88
88
  debug: options.debug
89
89
  });
90
90
  if (result.type === "missing_files") {
@@ -97,7 +97,7 @@ async function* continueDeployment(options) {
97
97
  payload: { total: files, missing: result.missing, uploads }
98
98
  };
99
99
  for await (const event of (0, import_upload.uploadFiles)({
100
- agent: options.agent,
100
+ dispatcher: options.dispatcher,
101
101
  apiUrl: options.apiUrl,
102
102
  debug: options.debug,
103
103
  teamId: options.teamId,
@@ -122,7 +122,7 @@ async function* continueDeployment(options) {
122
122
  teamId: options.teamId,
123
123
  apiUrl: options.apiUrl,
124
124
  userAgent: options.userAgent,
125
- agent: options.agent,
125
+ dispatcher: options.dispatcher,
126
126
  debug: options.debug
127
127
  });
128
128
  if (result.type === "missing_files") {
@@ -157,7 +157,7 @@ async function* continueDeployment(options) {
157
157
  return yield { type: "alias-assigned", payload: deployment };
158
158
  }
159
159
  yield* (0, import_check_deployment_status.checkDeploymentStatus)(deployment, {
160
- agent: options.agent,
160
+ dispatcher: options.dispatcher,
161
161
  apiUrl: options.apiUrl,
162
162
  debug: options.debug,
163
163
  path: options.path,
@@ -187,7 +187,7 @@ async function postContinue(options) {
187
187
  }),
188
188
  apiUrl: options.apiUrl,
189
189
  userAgent: options.userAgent,
190
- agent: options.agent
190
+ dispatcher: options.dispatcher
191
191
  }
192
192
  );
193
193
  let result;
package/dist/deploy.js CHANGED
@@ -56,7 +56,7 @@ async function* postDeployment(files, clientOptions, deploymentOptions) {
56
56
  }),
57
57
  apiUrl: clientOptions.apiUrl,
58
58
  userAgent: clientOptions.userAgent,
59
- agent: clientOptions.agent
59
+ dispatcher: clientOptions.dispatcher
60
60
  }
61
61
  );
62
62
  let deployment = void 0;
package/dist/types.d.ts CHANGED
@@ -1,7 +1,14 @@
1
- import type { Agent } from 'http';
2
1
  import type { Builder, BuilderFunctions, Images, ProjectSettings, Cron, ExperimentalServices, ExperimentalServiceGroups, ExperimentalServicesV2, Services } from '@vercel/build-utils';
3
2
  import type { Header, Route, Redirect, Rewrite } from '@vercel/routing-utils';
4
3
  export { DeploymentEventType } from './utils';
4
+ /**
5
+ * Minimal interface of an undici `Dispatcher` (e.g. `undici.ProxyAgent`),
6
+ * passed to `fetch` as the non-standard `dispatcher` init option to customize
7
+ * connection handling, such as routing requests through an HTTP(S) proxy.
8
+ */
9
+ export interface FetchDispatcher {
10
+ dispatch(options: unknown, handler: unknown): boolean;
11
+ }
5
12
  export interface Dictionary<T> {
6
13
  [key: string]: T;
7
14
  }
@@ -39,7 +46,7 @@ export interface VercelClientOptions {
39
46
  isDirectory?: boolean;
40
47
  skipAutoDetectionConfirmation?: boolean;
41
48
  archive?: ArchiveFormat;
42
- agent?: Agent;
49
+ dispatcher?: FetchDispatcher;
43
50
  projectName?: string;
44
51
  /**
45
52
  * Path to a file containing bulk redirects (relative to the project root).
package/dist/upload.d.ts CHANGED
@@ -1,13 +1,12 @@
1
1
  import { EventEmitter } from 'node:events';
2
2
  import { DeploymentFile, FilesMap } from './utils/hashes';
3
- import type { Agent } from 'http';
4
- import type { VercelClientOptions, DeploymentOptions, DeploymentEventType } from './types';
3
+ import type { FetchDispatcher, VercelClientOptions, DeploymentOptions, DeploymentEventType } from './types';
5
4
  export declare function upload(files: FilesMap, clientOptions: VercelClientOptions, deploymentOptions: DeploymentOptions): AsyncIterableIterator<any>;
6
5
  /**
7
6
  * Uploads files to the /v2/files endpoint with retry and fault tolerance.
8
7
  */
9
8
  export declare function uploadFiles(options: {
10
- agent?: Agent;
9
+ dispatcher?: FetchDispatcher;
11
10
  apiUrl?: string;
12
11
  debug?: boolean;
13
12
  files: FilesMap;
package/dist/upload.js CHANGED
@@ -33,8 +33,6 @@ __export(upload_exports, {
33
33
  uploadFiles: () => uploadFiles
34
34
  });
35
35
  module.exports = __toCommonJS(upload_exports);
36
- var import_http = __toESM(require("http"));
37
- var import_https = __toESM(require("https"));
38
36
  var import_fs = require("fs");
39
37
  var import_stream = require("stream");
40
38
  var import_node_events = require("node:events");
@@ -44,10 +42,16 @@ var import_utils = require("./utils");
44
42
  var import_errors = require("./errors");
45
43
  var import_deploy = require("./deploy");
46
44
  const isClientNetworkError = (err) => {
45
+ if (!(err instanceof Error)) {
46
+ return false;
47
+ }
47
48
  if (err.message) {
48
- return err.message.includes("ETIMEDOUT") || err.message.includes("ECONNREFUSED") || err.message.includes("ENOTFOUND") || err.message.includes("ECONNRESET") || err.message.includes("EAI_FAIL") || err.message.includes("socket hang up") || err.message.includes("network socket disconnected");
49
+ const matches = err.message.includes("ETIMEDOUT") || err.message.includes("ECONNREFUSED") || err.message.includes("ENOTFOUND") || err.message.includes("ECONNRESET") || err.message.includes("EAI_FAIL") || err.message.includes("socket hang up") || err.message.includes("network socket disconnected");
50
+ if (matches) {
51
+ return true;
52
+ }
49
53
  }
50
- return false;
54
+ return isClientNetworkError(err.cause);
51
55
  };
52
56
  async function* upload(files, clientOptions, deploymentOptions) {
53
57
  const debug = (0, import_utils.createDebug)(clientOptions.debug);
@@ -83,7 +87,7 @@ async function* upload(files, clientOptions, deploymentOptions) {
83
87
  payload: { total: files, missing: shas, uploads }
84
88
  };
85
89
  const uploadGenerator = uploadFiles({
86
- agent: clientOptions.agent,
90
+ dispatcher: clientOptions.dispatcher,
87
91
  apiUrl: clientOptions.apiUrl,
88
92
  debug: clientOptions.debug,
89
93
  teamId: clientOptions.teamId,
@@ -121,7 +125,6 @@ async function* uploadFiles(options) {
121
125
  const uploadList = {};
122
126
  debug("Building an upload list...");
123
127
  const semaphore = new import_async_sema.Sema(50, { capacity: 50 });
124
- const defaultAgent = options.apiUrl?.startsWith("https://") ? new import_https.default.Agent({ keepAlive: true }) : new import_http.default.Agent({ keepAlive: true });
125
128
  const abortControllers = /* @__PURE__ */ new Set();
126
129
  let aborted = false;
127
130
  options.shas.forEach((sha, index) => {
@@ -142,35 +145,27 @@ async function* uploadFiles(options) {
142
145
  uploadProgress.bytesUploaded = 0;
143
146
  let body;
144
147
  let contentLength;
148
+ const counter = new import_stream.Transform({
149
+ transform(chunk, _encoding, callback) {
150
+ uploadProgress.bytesUploaded += chunk.length;
151
+ uploadProgress.emit("progress");
152
+ callback(null, chunk);
153
+ }
154
+ });
145
155
  if (typeof data !== "undefined") {
146
156
  contentLength = data.length;
147
- const buffered = new import_stream.Readable();
148
- const originalRead = buffered.read.bind(buffered);
149
- buffered.read = function(...args) {
150
- const chunk = originalRead(...args);
151
- if (chunk) {
152
- uploadProgress.bytesUploaded += chunk.length;
153
- uploadProgress.emit("progress");
154
- }
155
- return chunk;
156
- };
157
157
  const chunkSize = 16384;
158
- for (let i = 0; i < data.length; i += chunkSize) {
159
- const chunk = data.slice(i, i + chunkSize);
160
- buffered.push(chunk);
158
+ function* chunks() {
159
+ for (let i = 0; i < data.length; i += chunkSize) {
160
+ yield data.slice(i, i + chunkSize);
161
+ }
161
162
  }
162
- buffered.push(null);
163
- body = buffered;
163
+ const buffered = import_stream.Readable.from(chunks());
164
+ buffered.on("error", (err2) => counter.destroy(err2));
165
+ body = buffered.pipe(counter);
164
166
  } else if (typeof size === "number") {
165
167
  contentLength = size;
166
168
  const fileStream = (0, import_fs.createReadStream)(names[0]);
167
- const counter = new import_stream.Transform({
168
- transform(chunk, _encoding, callback) {
169
- uploadProgress.bytesUploaded += chunk.length;
170
- uploadProgress.emit("progress");
171
- callback(null, chunk);
172
- }
173
- });
174
169
  fileStream.on("error", (err2) => counter.destroy(err2));
175
170
  counter.on("close", () => fileStream.destroy());
176
171
  body = fileStream.pipe(counter);
@@ -187,19 +182,18 @@ async function* uploadFiles(options) {
187
182
  import_utils.API_FILES,
188
183
  options.token,
189
184
  {
190
- agent: options.agent || defaultAgent,
185
+ dispatcher: options.dispatcher,
191
186
  method: "POST",
192
187
  headers: {
193
188
  "Content-Type": "application/octet-stream",
194
- "Content-Length": contentLength,
189
+ "Content-Length": String(contentLength),
195
190
  "x-now-digest": sha,
196
- "x-now-size": contentLength
191
+ "x-now-size": String(contentLength)
197
192
  },
198
193
  body,
199
194
  teamId: options.teamId,
200
195
  apiUrl: options.apiUrl,
201
196
  userAgent: options.userAgent,
202
- // @ts-expect-error: typescript is getting confused with the signal types from node (web & server) and node-fetch (server only)
203
197
  signal: abortController.signal
204
198
  },
205
199
  options.debug
@@ -226,7 +220,7 @@ async function* uploadFiles(options) {
226
220
  } catch (e) {
227
221
  debug(`An unexpected error occurred in upload promise:
228
222
  ${e}`);
229
- err = new Error(e);
223
+ err = e instanceof Error ? e : new Error(String(e));
230
224
  }
231
225
  semaphore.release();
232
226
  if (err) {
@@ -1,7 +1,7 @@
1
1
  import { FilesMap } from './hashes';
2
- import { RequestInit } from 'node-fetch';
2
+ import { Readable } from 'stream';
3
3
  import ignore from 'ignore';
4
- import { VercelClientOptions, VercelConfig } from '../types';
4
+ import { FetchDispatcher, VercelClientOptions, VercelConfig } from '../types';
5
5
  type Ignore = ReturnType<typeof ignore>;
6
6
  export declare const API_FILES = "/v2/files";
7
7
  declare const EVENTS_ARRAY: readonly ["hashes-calculated", "file-count", "file-uploaded", "all-files-uploaded", "created", "building", "ready", "alias-assigned", "warning", "error", "notice", "tip", "canceled", "checks-registered", "checks-completed", "checks-running", "checks-conclusion-succeeded", "checks-conclusion-failed", "checks-conclusion-skipped", "checks-conclusion-canceled", "checks-v2-failed"];
@@ -17,7 +17,8 @@ export declare function getVercelIgnore(cwd: string | string[], prebuilt?: boole
17
17
  ig: Ignore;
18
18
  ignores: string[];
19
19
  }>;
20
- interface FetchOpts extends RequestInit {
20
+ type NativeRequestInit = NonNullable<Parameters<typeof globalThis.fetch>[1]>;
21
+ interface FetchOpts extends Omit<NativeRequestInit, 'body' | 'headers' | 'dispatcher'> {
21
22
  apiUrl?: string;
22
23
  method?: string;
23
24
  teamId?: string;
@@ -25,6 +26,8 @@ interface FetchOpts extends RequestInit {
25
26
  [key: string]: any;
26
27
  };
27
28
  userAgent?: string;
29
+ body?: NonNullable<NativeRequestInit['body']> | Buffer | Readable;
30
+ dispatcher?: FetchDispatcher;
28
31
  }
29
32
  export declare const fetchApi: (url: string, token: string, opts?: FetchOpts, debugEnabled?: boolean) => Promise<any>;
30
33
  export interface PreparedFile {
@@ -39,8 +39,8 @@ __export(utils_exports, {
39
39
  prepareFiles: () => prepareFiles
40
40
  });
41
41
  module.exports = __toCommonJS(utils_exports);
42
- var import_node_fetch = __toESM(require("node-fetch"));
43
42
  var import_path = require("path");
43
+ var import_stream = require("stream");
44
44
  var import_url = require("url");
45
45
  var import_ignore = __toESM(require("ignore"));
46
46
  var import_pkg = require("../pkg");
@@ -320,10 +320,13 @@ const fetchApi = async (url, token, opts = {}, debugEnabled) => {
320
320
  accept: "application/json",
321
321
  "user-agent": userAgent
322
322
  };
323
+ if (opts.body instanceof import_stream.Readable) {
324
+ opts.duplex = "half";
325
+ }
323
326
  debug(`${opts.method || "GET"} ${url}`);
324
327
  time = Date.now();
325
328
  try {
326
- const res = await (0, import_node_fetch.default)(url, opts);
329
+ const res = await fetch(url, opts);
327
330
  debug(`DONE in ${Date.now() - time}ms: ${opts.method || "GET"} ${url}`);
328
331
  return res;
329
332
  } finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/client",
3
- "version": "17.6.4",
3
+ "version": "18.0.0",
4
4
  "main": "dist/index.js",
5
5
  "typings": "dist/index.d.ts",
6
6
  "homepage": "https://vercel.com",
@@ -22,7 +22,6 @@
22
22
  "@types/minimatch": "3.0.5",
23
23
  "@types/ms": "0.7.30",
24
24
  "@types/node": "20.11.0",
25
- "@types/node-fetch": "2.5.4",
26
25
  "@types/recursive-readdir": "2.2.0",
27
26
  "@types/tar-fs": "1.16.1",
28
27
  "vitest": "2.0.1"
@@ -35,13 +34,12 @@
35
34
  "ignore": "4.0.6",
36
35
  "minimatch": "5.0.1",
37
36
  "ms": "2.1.2",
38
- "node-fetch": "2.6.7",
39
37
  "querystring": "^0.2.0",
40
38
  "sleep-promise": "8.0.1",
41
39
  "tar-fs": "1.16.3",
42
- "@vercel/build-utils": "13.32.3",
43
- "@vercel/routing-utils": "6.4.0",
44
- "@vercel/error-utils": "2.2.0"
40
+ "@vercel/error-utils": "2.2.0",
41
+ "@vercel/build-utils": "13.33.1",
42
+ "@vercel/routing-utils": "6.4.0"
45
43
  },
46
44
  "scripts": {
47
45
  "build": "node ../../utils/build.mjs",