@vercel/client 17.2.40 → 17.2.42

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.
@@ -0,0 +1,22 @@
1
+ /// <reference types="node" />
2
+ import type { Agent } from 'http';
3
+ import type { DeploymentEventType } from './types';
4
+ /**
5
+ * Continues a manual deployment by uploading build outputs and calling the
6
+ * continue endpoint. Waits for READY state unless the caller stops consuming
7
+ * events early.
8
+ */
9
+ export declare function continueDeployment(options: {
10
+ agent?: Agent;
11
+ apiUrl?: string;
12
+ debug?: boolean;
13
+ deploymentId: string;
14
+ path: string;
15
+ teamId?: string;
16
+ token: string;
17
+ userAgent?: string;
18
+ vercelOutputDir?: string;
19
+ }): AsyncIterableIterator<{
20
+ type: DeploymentEventType;
21
+ payload: any;
22
+ }>;
@@ -0,0 +1,210 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var continue_exports = {};
30
+ __export(continue_exports, {
31
+ continueDeployment: () => continueDeployment
32
+ });
33
+ module.exports = __toCommonJS(continue_exports);
34
+ var import_path = require("path");
35
+ var import_fs_extra = __toESM(require("fs-extra"));
36
+ var import_check_deployment_status = require("./check-deployment-status");
37
+ var import_utils = require("./utils");
38
+ var import_hashes = require("./utils/hashes");
39
+ var import_ready_state = require("./utils/ready-state");
40
+ var import_upload = require("./upload");
41
+ var import_errors = require("./errors");
42
+ async function* continueDeployment(options) {
43
+ const debug = (0, import_utils.createDebug)(options.debug);
44
+ debug(`Continuing deployment: ${options.deploymentId}`);
45
+ const outputDir = options.vercelOutputDir || (0, import_path.join)(options.path, ".vercel", "output");
46
+ if (!await import_fs_extra.default.pathExists(outputDir)) {
47
+ return yield {
48
+ type: "error",
49
+ payload: new import_errors.DeploymentError({
50
+ code: "output_dir_not_found",
51
+ message: `Output directory not found at ${outputDir}. Run 'vercel build' first.`
52
+ })
53
+ };
54
+ }
55
+ const { fileList } = await (0, import_utils.buildFileTree)(
56
+ options.path,
57
+ { isDirectory: true, prebuilt: true, vercelOutputDir: outputDir },
58
+ debug
59
+ );
60
+ const files = await (0, import_hashes.hashes)(fileList);
61
+ debug(`Calculated ${files.size} unique hashes`);
62
+ yield { type: "hashes-calculated", payload: (0, import_hashes.mapToObject)(files) };
63
+ let deployment;
64
+ let result = await postContinue({
65
+ deploymentId: options.deploymentId,
66
+ files,
67
+ outputDir,
68
+ path: options.path,
69
+ token: options.token,
70
+ teamId: options.teamId,
71
+ apiUrl: options.apiUrl,
72
+ userAgent: options.userAgent,
73
+ agent: options.agent,
74
+ debug: options.debug
75
+ });
76
+ if (result.type === "missing_files") {
77
+ debug(`Uploading ${result.missing.length} missing files...`);
78
+ const uploads = result.missing.map(
79
+ (sha) => new import_upload.UploadProgress(sha, files.get(sha))
80
+ );
81
+ yield {
82
+ type: "file-count",
83
+ payload: { total: files, missing: result.missing, uploads }
84
+ };
85
+ for await (const event of (0, import_upload.uploadFiles)({
86
+ agent: options.agent,
87
+ apiUrl: options.apiUrl,
88
+ debug: options.debug,
89
+ teamId: options.teamId,
90
+ token: options.token,
91
+ userAgent: options.userAgent,
92
+ shas: result.missing,
93
+ files,
94
+ uploads
95
+ })) {
96
+ if (event.type === "error") {
97
+ return yield event;
98
+ }
99
+ yield event;
100
+ }
101
+ yield { type: "all-files-uploaded", payload: files };
102
+ result = await postContinue({
103
+ deploymentId: options.deploymentId,
104
+ files,
105
+ outputDir,
106
+ path: options.path,
107
+ token: options.token,
108
+ teamId: options.teamId,
109
+ apiUrl: options.apiUrl,
110
+ userAgent: options.userAgent,
111
+ agent: options.agent,
112
+ debug: options.debug
113
+ });
114
+ if (result.type === "missing_files") {
115
+ return yield {
116
+ type: "error",
117
+ payload: {
118
+ code: "missing_files",
119
+ message: "Missing files",
120
+ missing: result.missing
121
+ }
122
+ };
123
+ }
124
+ }
125
+ if (result.type === "error") {
126
+ return yield { type: "error", payload: result.error };
127
+ }
128
+ if (result.type === "success") {
129
+ deployment = result.deployment;
130
+ yield { type: "created", payload: deployment };
131
+ }
132
+ if (!deployment) {
133
+ return yield {
134
+ type: "error",
135
+ payload: new import_errors.DeploymentError({
136
+ code: "continue_failed",
137
+ message: "Failed to continue deployment after uploading files"
138
+ })
139
+ };
140
+ }
141
+ if ((0, import_ready_state.isReady)(deployment) && (0, import_ready_state.isAliasAssigned)(deployment)) {
142
+ yield { type: "ready", payload: deployment };
143
+ return yield { type: "alias-assigned", payload: deployment };
144
+ }
145
+ yield* (0, import_check_deployment_status.checkDeploymentStatus)(deployment, {
146
+ agent: options.agent,
147
+ apiUrl: options.apiUrl,
148
+ debug: options.debug,
149
+ path: options.path,
150
+ teamId: options.teamId,
151
+ token: options.token,
152
+ userAgent: options.userAgent
153
+ });
154
+ }
155
+ async function postContinue(options) {
156
+ const debug = (0, import_utils.createDebug)(options.debug);
157
+ debug(`Calling continue deployment endpoint for ${options.deploymentId}`);
158
+ const response = await (0, import_utils.fetch)(
159
+ `/deployments/${options.deploymentId}/continue${options.teamId ? `?teamId=${options.teamId}` : ""}`,
160
+ options.token,
161
+ {
162
+ method: "POST",
163
+ headers: {
164
+ Accept: "application/json",
165
+ "Content-Type": "application/json"
166
+ },
167
+ body: JSON.stringify({
168
+ files: (0, import_utils.prepareFiles)(options.files, {
169
+ isDirectory: true,
170
+ path: options.path,
171
+ token: options.token
172
+ })
173
+ }),
174
+ apiUrl: options.apiUrl,
175
+ userAgent: options.userAgent,
176
+ agent: options.agent
177
+ }
178
+ );
179
+ let result;
180
+ try {
181
+ result = await response.json();
182
+ } catch {
183
+ return {
184
+ type: "error",
185
+ error: new Error("Invalid JSON response from continue endpoint")
186
+ };
187
+ }
188
+ if (!response.ok || result.error) {
189
+ if (result.error?.code === "missing_files" || result.code === "missing_files") {
190
+ debug(
191
+ `Continue deployment returned missing_files: ${(result.error?.missing || result.missing || []).length} files`
192
+ );
193
+ return {
194
+ type: "missing_files",
195
+ missing: result.error?.missing || result.missing || []
196
+ };
197
+ }
198
+ debug("Continue deployment request failed");
199
+ return {
200
+ type: "error",
201
+ error: result.error ? { ...result.error, status: response.status } : { ...result, status: response.status }
202
+ };
203
+ }
204
+ debug("Continue deployment succeeded");
205
+ return { type: "success", deployment: result };
206
+ }
207
+ // Annotate the CommonJS export names for ESM import in node:
208
+ 0 && (module.exports = {
209
+ continueDeployment
210
+ });
@@ -34,6 +34,7 @@ module.exports = __toCommonJS(create_deployment_exports);
34
34
  var import_fs_extra = require("fs-extra");
35
35
  var import_path = require("path");
36
36
  var import_hashes = require("./utils/hashes");
37
+ var import_deploy = require("./deploy");
37
38
  var import_upload = require("./upload");
38
39
  var import_utils = require("./utils");
39
40
  var import_errors = require("./errors");
@@ -64,6 +65,22 @@ function buildCreateDeployment() {
64
65
  message: "Options object must include a `token`"
65
66
  });
66
67
  }
68
+ if (clientOptions.manual) {
69
+ debug("Manual provisioning mode enabled");
70
+ if (!clientOptions.prebuilt) {
71
+ throw new import_errors.DeploymentError({
72
+ code: "invalid_options",
73
+ message: "The `manual` option requires `prebuilt` to be true"
74
+ });
75
+ }
76
+ deploymentOptions.build = deploymentOptions.build || {};
77
+ deploymentOptions.build.env = deploymentOptions.build.env || {};
78
+ deploymentOptions.build.env.VERCEL_MANUAL_PROVISIONING = "1";
79
+ deploymentOptions.version = 2;
80
+ debug("Creating deployment with manual provisioning...");
81
+ yield* (0, import_deploy.deploy)(/* @__PURE__ */ new Map(), clientOptions, deploymentOptions);
82
+ return;
83
+ }
67
84
  clientOptions.isDirectory = !Array.isArray(path) && (0, import_fs_extra.lstatSync)(path).isDirectory();
68
85
  if (Array.isArray(path)) {
69
86
  for (const filePath of path) {
package/dist/deploy.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { FilesMap } from './utils/hashes';
2
- import { DeploymentOptions, VercelClientOptions } from './types';
2
+ import { DeploymentOptions, VercelClientOptions, DeploymentEventType } from './types';
3
3
  export declare function deploy(files: FilesMap, clientOptions: VercelClientOptions, deploymentOptions: DeploymentOptions): AsyncIterableIterator<{
4
- type: string;
4
+ type: DeploymentEventType;
5
5
  payload: any;
6
6
  }>;
package/dist/deploy.js CHANGED
@@ -110,14 +110,14 @@ function getDefaultName(files, clientOptions) {
110
110
  }
111
111
  async function* deploy(files, clientOptions, deploymentOptions) {
112
112
  const debug = (0, import_utils.createDebug)(clientOptions.debug);
113
- if (!deploymentOptions.name) {
113
+ if (!deploymentOptions.name && files.size > 0) {
114
114
  deploymentOptions.version = 2;
115
115
  deploymentOptions.name = files.size === 1 ? "file" : getDefaultName(files, clientOptions);
116
116
  if (deploymentOptions.name === "file") {
117
117
  debug('Setting deployment name to "file" for single-file deployment');
118
118
  }
119
119
  }
120
- if (!deploymentOptions.name) {
120
+ if (!deploymentOptions.name && files.size > 0) {
121
121
  deploymentOptions.name = clientOptions.defaultName || getDefaultName(files, clientOptions);
122
122
  debug("No name provided. Defaulting to", deploymentOptions.name);
123
123
  }
@@ -144,6 +144,10 @@ async function* deploy(files, clientOptions, deploymentOptions) {
144
144
  debug("An unexpected error occurred when creating the deployment");
145
145
  return yield { type: "error", payload: e };
146
146
  }
147
+ if (clientOptions.manual) {
148
+ debug("Manual mode - skipping ready state check");
149
+ return;
150
+ }
147
151
  if (deployment) {
148
152
  if ((0, import_ready_state.isReady)(deployment) && (0, import_ready_state.isAliasAssigned)(deployment)) {
149
153
  debug("Deployment state changed to READY 3");
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export { continueDeployment } from './continue';
1
2
  export { checkDeploymentStatus } from './check-deployment-status';
2
3
  export { getVercelIgnore, buildFileTree } from './utils/index';
3
4
  export declare const createDeployment: (clientOptions: import("./types").VercelClientOptions, deploymentOptions?: import("./types").DeploymentOptions) => AsyncIterableIterator<{
package/dist/index.js CHANGED
@@ -31,11 +31,13 @@ var src_exports = {};
31
31
  __export(src_exports, {
32
32
  buildFileTree: () => import_utils.buildFileTree,
33
33
  checkDeploymentStatus: () => import_check_deployment_status.checkDeploymentStatus,
34
+ continueDeployment: () => import_continue.continueDeployment,
34
35
  createDeployment: () => createDeployment,
35
36
  getVercelIgnore: () => import_utils.getVercelIgnore
36
37
  });
37
38
  module.exports = __toCommonJS(src_exports);
38
39
  var import_create_deployment = __toESM(require("./create-deployment"));
40
+ var import_continue = require("./continue");
39
41
  var import_check_deployment_status = require("./check-deployment-status");
40
42
  var import_utils = require("./utils/index");
41
43
  __reExport(src_exports, require("./errors"), module.exports);
@@ -45,6 +47,7 @@ const createDeployment = (0, import_create_deployment.default)();
45
47
  0 && (module.exports = {
46
48
  buildFileTree,
47
49
  checkDeploymentStatus,
50
+ continueDeployment,
48
51
  createDeployment,
49
52
  getVercelIgnore,
50
53
  ...require("./errors"),
package/dist/types.d.ts CHANGED
@@ -31,6 +31,11 @@ export interface VercelClientOptions {
31
31
  * This file will be included in prebuilt deployments.
32
32
  */
33
33
  bulkRedirectsPath?: string | null;
34
+ /**
35
+ * When true, creates an experimental manual deployment. This mode requires
36
+ * that the user later continues the deployment with an API call.
37
+ */
38
+ manual?: boolean;
34
39
  }
35
40
  /** @deprecated Use VercelClientOptions instead. */
36
41
  export type NowClientOptions = VercelClientOptions;
package/dist/upload.d.ts CHANGED
@@ -1,3 +1,30 @@
1
- import { FilesMap } from './utils/hashes';
2
- import { VercelClientOptions, DeploymentOptions } from './types';
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ import { EventEmitter } from 'node:events';
4
+ import { DeploymentFile, FilesMap } from './utils/hashes';
5
+ import type { Agent } from 'http';
6
+ import type { VercelClientOptions, DeploymentOptions, DeploymentEventType } from './types';
3
7
  export declare function upload(files: FilesMap, clientOptions: VercelClientOptions, deploymentOptions: DeploymentOptions): AsyncIterableIterator<any>;
8
+ /**
9
+ * Uploads files to the /v2/files endpoint with retry and fault tolerance.
10
+ */
11
+ export declare function uploadFiles(options: {
12
+ agent?: Agent;
13
+ apiUrl?: string;
14
+ debug?: boolean;
15
+ files: FilesMap;
16
+ shas: string[];
17
+ teamId?: string;
18
+ token: string;
19
+ uploads: UploadProgress[];
20
+ userAgent?: string;
21
+ }): AsyncIterableIterator<{
22
+ type: DeploymentEventType;
23
+ payload: any;
24
+ }>;
25
+ export declare class UploadProgress extends EventEmitter {
26
+ sha: string;
27
+ file: DeploymentFile;
28
+ bytesUploaded: number;
29
+ constructor(sha: string, file: DeploymentFile);
30
+ }
package/dist/upload.js CHANGED
@@ -28,7 +28,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
  var upload_exports = {};
30
30
  __export(upload_exports, {
31
- upload: () => upload
31
+ UploadProgress: () => UploadProgress,
32
+ upload: () => upload,
33
+ uploadFiles: () => uploadFiles
32
34
  });
33
35
  module.exports = __toCommonJS(upload_exports);
34
36
  var import_http = __toESM(require("http"));
@@ -47,9 +49,8 @@ const isClientNetworkError = (err) => {
47
49
  return false;
48
50
  };
49
51
  async function* upload(files, clientOptions, deploymentOptions) {
50
- const { token, teamId, apiUrl, userAgent } = clientOptions;
51
52
  const debug = (0, import_utils.createDebug)(clientOptions.debug);
52
- if (!files && !token && !teamId) {
53
+ if (!files && !clientOptions.token && !clientOptions.teamId) {
53
54
  debug(`Neither 'files', 'token' nor 'teamId are present. Exiting`);
54
55
  return;
55
56
  }
@@ -78,23 +79,60 @@ async function* upload(files, clientOptions, deploymentOptions) {
78
79
  type: "file-count",
79
80
  payload: { total: files, missing: shas, uploads }
80
81
  };
82
+ const uploadGenerator = uploadFiles({
83
+ agent: clientOptions.agent,
84
+ apiUrl: clientOptions.apiUrl,
85
+ debug: clientOptions.debug,
86
+ teamId: clientOptions.teamId,
87
+ token: clientOptions.token,
88
+ userAgent: clientOptions.userAgent,
89
+ files,
90
+ shas,
91
+ uploads
92
+ });
93
+ for await (const event of uploadGenerator) {
94
+ if (event.type === "error") {
95
+ return yield event;
96
+ } else {
97
+ yield event;
98
+ }
99
+ }
100
+ debug("All files uploaded");
101
+ yield { type: "all-files-uploaded", payload: files };
102
+ try {
103
+ debug("Starting deployment creation");
104
+ for await (const event of (0, import_deploy.deploy)(files, clientOptions, deploymentOptions)) {
105
+ if (event.type === "alias-assigned") {
106
+ debug("Deployment is ready");
107
+ return yield event;
108
+ }
109
+ yield event;
110
+ }
111
+ } catch (e) {
112
+ debug("An unexpected error occurred when starting deployment creation");
113
+ yield { type: "error", payload: e };
114
+ }
115
+ }
116
+ async function* uploadFiles(options) {
117
+ const debug = (0, import_utils.createDebug)(options.debug);
81
118
  const uploadList = {};
82
119
  debug("Building an upload list...");
83
120
  const semaphore = new import_async_sema.Sema(50, { capacity: 50 });
84
- const defaultAgent = apiUrl?.startsWith("https://") ? new import_https.default.Agent({ keepAlive: true }) : new import_http.default.Agent({ keepAlive: true });
121
+ const defaultAgent = options.apiUrl?.startsWith("https://") ? new import_https.default.Agent({ keepAlive: true }) : new import_http.default.Agent({ keepAlive: true });
85
122
  const abortControllers = /* @__PURE__ */ new Set();
86
123
  let aborted = false;
87
- shas.forEach((sha, index) => {
88
- const uploadProgress = uploads[index];
124
+ options.shas.forEach((sha, index) => {
125
+ const uploadProgress = options.uploads[index];
89
126
  uploadList[sha] = (0, import_async_retry.default)(
90
127
  async (bail) => {
91
- const file = files.get(sha);
128
+ const file = options.files.get(sha);
92
129
  if (!file) {
93
130
  debug(`File ${sha} is undefined. Bailing`);
94
131
  return bail(new Error(`File ${sha} is undefined`));
95
132
  }
96
133
  await semaphore.acquire();
97
134
  if (aborted) {
135
+ semaphore.release();
98
136
  return bail(new Error("Upload aborted"));
99
137
  }
100
138
  const { data } = file;
@@ -125,9 +163,9 @@ async function* upload(files, clientOptions, deploymentOptions) {
125
163
  try {
126
164
  const res = await (0, import_utils.fetch)(
127
165
  import_utils.API_FILES,
128
- token,
166
+ options.token,
129
167
  {
130
- agent: clientOptions.agent || defaultAgent,
168
+ agent: options.agent || defaultAgent,
131
169
  method: "POST",
132
170
  headers: {
133
171
  "Content-Type": "application/octet-stream",
@@ -136,13 +174,13 @@ async function* upload(files, clientOptions, deploymentOptions) {
136
174
  "x-now-size": data.length
137
175
  },
138
176
  body,
139
- teamId,
140
- apiUrl,
141
- userAgent,
177
+ teamId: options.teamId,
178
+ apiUrl: options.apiUrl,
179
+ userAgent: options.userAgent,
142
180
  // @ts-expect-error: typescript is getting confused with the signal types from node (web & server) and node-fetch (server only)
143
181
  signal: abortController.signal
144
182
  },
145
- clientOptions.debug
183
+ options.debug
146
184
  );
147
185
  if (res.status === 200) {
148
186
  debug(
@@ -202,21 +240,6 @@ ${e}`);
202
240
  return yield { type: "error", payload: e };
203
241
  }
204
242
  }
205
- debug("All files uploaded");
206
- yield { type: "all-files-uploaded", payload: files };
207
- try {
208
- debug("Starting deployment creation");
209
- for await (const event of (0, import_deploy.deploy)(files, clientOptions, deploymentOptions)) {
210
- if (event.type === "alias-assigned") {
211
- debug("Deployment is ready");
212
- return yield event;
213
- }
214
- yield event;
215
- }
216
- } catch (e) {
217
- debug("An unexpected error occurred when starting deployment creation");
218
- yield { type: "error", payload: e };
219
- }
220
243
  }
221
244
  class UploadProgress extends import_node_events.EventEmitter {
222
245
  constructor(sha, file) {
@@ -228,5 +251,7 @@ class UploadProgress extends import_node_events.EventEmitter {
228
251
  }
229
252
  // Annotate the CommonJS export names for ESM import in node:
230
253
  0 && (module.exports = {
231
- upload
254
+ UploadProgress,
255
+ upload,
256
+ uploadFiles
232
257
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercel/client",
3
- "version": "17.2.40",
3
+ "version": "17.2.42",
4
4
  "main": "dist/index.js",
5
5
  "typings": "dist/index.d.ts",
6
6
  "homepage": "https://vercel.com",
@@ -42,9 +42,9 @@
42
42
  "querystring": "^0.2.0",
43
43
  "sleep-promise": "8.0.1",
44
44
  "tar-fs": "1.16.3",
45
- "@vercel/build-utils": "13.4.0",
46
45
  "@vercel/error-utils": "2.0.3",
47
- "@vercel/routing-utils": "5.3.2"
46
+ "@vercel/build-utils": "13.4.0",
47
+ "@vercel/routing-utils": "5.3.3"
48
48
  },
49
49
  "scripts": {
50
50
  "build": "node ../../utils/build.mjs",