@prisma/cli 3.0.0-dev.53.1 → 3.0.0-dev.54.1

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.
@@ -16,24 +16,28 @@ var PreviewBuildStrategy = class {
16
16
  #appPath;
17
17
  #entrypoint;
18
18
  #buildType;
19
+ #signal;
19
20
  constructor(options) {
20
21
  this.#appPath = options.appPath;
21
22
  this.#entrypoint = options.entrypoint;
22
23
  this.#buildType = options.buildType ?? "auto";
24
+ this.#signal = options.signal;
23
25
  }
24
- async canBuild() {
26
+ async canBuild(signal = this.#signal) {
25
27
  const { strategy } = await resolvePreviewBuildStrategy({
26
28
  appPath: this.#appPath,
27
29
  entrypoint: this.#entrypoint,
28
- buildType: this.#buildType
30
+ buildType: this.#buildType,
31
+ signal
29
32
  });
30
- return strategy.canBuild();
33
+ return strategy.canBuild(signal);
31
34
  }
32
- async execute() {
35
+ async execute(signal = this.#signal) {
33
36
  const { artifact } = await executePreviewBuild({
34
37
  appPath: this.#appPath,
35
38
  entrypoint: this.#entrypoint,
36
- buildType: this.#buildType
39
+ buildType: this.#buildType,
40
+ signal
37
41
  });
38
42
  return artifact;
39
43
  }
@@ -42,12 +46,13 @@ async function executePreviewBuild(options) {
42
46
  const { strategy, buildType } = await resolvePreviewBuildStrategy({
43
47
  appPath: options.appPath,
44
48
  entrypoint: options.entrypoint,
45
- buildType: options.buildType ?? "auto"
49
+ buildType: options.buildType ?? "auto",
50
+ signal: options.signal
46
51
  });
47
- const artifact = await strategy.execute();
52
+ const artifact = await strategy.execute(options.signal);
48
53
  try {
49
- if (buildType === "nextjs") await restageNextjsArtifact(artifact, options.appPath);
50
- await normalizeArtifactSymlinks(artifact.directory, options.appPath);
54
+ if (buildType === "nextjs") await restageNextjsArtifact(artifact, options.appPath, options.signal);
55
+ await normalizeArtifactSymlinks(artifact.directory, options.appPath, options.signal);
51
56
  return {
52
57
  artifact,
53
58
  buildType
@@ -62,7 +67,8 @@ async function resolvePreviewBuildStrategy(options) {
62
67
  const strategy = await createPreviewBuildStrategy({
63
68
  appPath: options.appPath,
64
69
  entrypoint: options.entrypoint,
65
- buildType: options.buildType
70
+ buildType: options.buildType,
71
+ signal: options.signal
66
72
  });
67
73
  return {
68
74
  buildType: options.buildType,
@@ -74,9 +80,10 @@ async function resolvePreviewBuildStrategy(options) {
74
80
  const strategy = await createPreviewBuildStrategy({
75
81
  appPath: options.appPath,
76
82
  entrypoint: options.entrypoint,
77
- buildType
83
+ buildType,
84
+ signal: options.signal
78
85
  });
79
- if (await strategy.canBuild()) return {
86
+ if (await strategy.canBuild(options.signal)) return {
80
87
  buildType,
81
88
  strategy
82
89
  };
@@ -86,7 +93,8 @@ async function resolvePreviewBuildStrategy(options) {
86
93
  strategy: await createPreviewBuildStrategy({
87
94
  appPath: options.appPath,
88
95
  entrypoint: options.entrypoint,
89
- buildType: "bun"
96
+ buildType: "bun",
97
+ signal: options.signal
90
98
  })
91
99
  };
92
100
  }
@@ -97,7 +105,7 @@ async function createPreviewBuildStrategy(options) {
97
105
  case "astro": return new AstroBuild({ appPath: options.appPath });
98
106
  case "tanstack-start": return new TanstackStartBuild({ appPath: options.appPath });
99
107
  case "bun": {
100
- const entrypoint = await resolveBunEntrypoint(options.appPath, options.entrypoint);
108
+ const entrypoint = await resolveBunEntrypoint(options.appPath, options.entrypoint, options.signal);
101
109
  return new BunBuild({
102
110
  appPath: options.appPath,
103
111
  entrypoint
@@ -112,74 +120,78 @@ async function stageNextjsStandaloneArtifact(options) {
112
120
  await copyPathMaterializingSymlinks(standaloneRoot, artifactRoot, {
113
121
  standaloneRoot,
114
122
  appRoot,
115
- sourceRoot: await resolveSourceRoot(appRoot)
123
+ sourceRoot: await resolveSourceRoot(appRoot, options.signal),
124
+ signal: options.signal
116
125
  });
117
- await hoistPnpmDependencies(path.join(artifactRoot, "node_modules"));
126
+ await hoistPnpmDependencies(path.join(artifactRoot, "node_modules"), options.signal);
118
127
  }
119
- async function restageNextjsArtifact(artifact, appPath) {
128
+ async function restageNextjsArtifact(artifact, appPath, signal) {
120
129
  const artifactDir = artifact.directory;
121
130
  const standaloneDir = path.join(appPath, ".next", "standalone");
122
- await rm(artifactDir, {
131
+ await unsupportedFilesystemBoundary(signal, () => rm(artifactDir, {
123
132
  recursive: true,
124
133
  force: true
125
- });
134
+ }));
126
135
  await stageNextjsStandaloneArtifact({
127
136
  standaloneDir,
128
137
  artifactDir,
129
- appPath
138
+ appPath,
139
+ signal
130
140
  });
131
141
  const serverSubpath = nextjsServerSubpath(artifact.entrypoint);
132
142
  const serverDir = serverSubpath ? path.join(artifactDir, serverSubpath) : artifactDir;
133
143
  const publicDir = path.join(appPath, "public");
134
- if (await directoryExists(publicDir)) await cp(publicDir, path.join(serverDir, "public"), {
144
+ if (await directoryExists(publicDir, signal)) await unsupportedFilesystemBoundary(signal, () => cp(publicDir, path.join(serverDir, "public"), {
135
145
  recursive: true,
136
146
  verbatimSymlinks: true
137
- });
147
+ }));
138
148
  const staticDir = path.join(appPath, ".next", "static");
139
- if (await directoryExists(staticDir)) await cp(staticDir, path.join(serverDir, ".next", "static"), {
149
+ if (await directoryExists(staticDir, signal)) await unsupportedFilesystemBoundary(signal, () => cp(staticDir, path.join(serverDir, ".next", "static"), {
140
150
  recursive: true,
141
151
  verbatimSymlinks: true
142
- });
152
+ }));
143
153
  }
144
154
  function nextjsServerSubpath(entrypoint) {
145
155
  const dir = path.posix.dirname(entrypoint);
146
156
  return dir === "." ? "" : dir;
147
157
  }
148
- async function hoistPnpmDependencies(nodeModulesDir) {
158
+ async function hoistPnpmDependencies(nodeModulesDir, signal) {
149
159
  const pnpmNodeModulesDir = path.join(nodeModulesDir, ".pnpm", "node_modules");
150
- if (!await directoryExists(pnpmNodeModulesDir)) return;
151
- const entries = await readdir(pnpmNodeModulesDir, { withFileTypes: true });
160
+ if (!await directoryExists(pnpmNodeModulesDir, signal)) return;
161
+ const entries = await unsupportedFilesystemBoundary(signal, () => readdir(pnpmNodeModulesDir, { withFileTypes: true }));
152
162
  for (const entry of entries) {
153
163
  const sourcePath = path.join(pnpmNodeModulesDir, entry.name);
154
164
  if (entry.name.startsWith("@") && entry.isDirectory()) {
155
- const scopedEntries = await readdir(sourcePath, { withFileTypes: true });
165
+ const scopedEntries = await unsupportedFilesystemBoundary(signal, () => readdir(sourcePath, { withFileTypes: true }));
156
166
  for (const scopedEntry of scopedEntries) {
157
167
  const scopedDestination = path.join(nodeModulesDir, entry.name, scopedEntry.name);
158
- if (await pathExists(scopedDestination)) continue;
159
- await mkdir(path.dirname(scopedDestination), { recursive: true });
168
+ if (await pathExists(scopedDestination, signal)) continue;
169
+ await unsupportedFilesystemBoundary(signal, () => mkdir(path.dirname(scopedDestination), { recursive: true }));
160
170
  await copyPathMaterializingSymlinks(path.join(sourcePath, scopedEntry.name), scopedDestination, {
161
171
  standaloneRoot: pnpmNodeModulesDir,
162
172
  appRoot: nodeModulesDir,
163
- sourceRoot: nodeModulesDir
173
+ sourceRoot: nodeModulesDir,
174
+ signal
164
175
  });
165
176
  }
166
177
  continue;
167
178
  }
168
179
  const destinationPath = path.join(nodeModulesDir, entry.name);
169
- if (await pathExists(destinationPath)) continue;
180
+ if (await pathExists(destinationPath, signal)) continue;
170
181
  await copyPathMaterializingSymlinks(sourcePath, destinationPath, {
171
182
  standaloneRoot: pnpmNodeModulesDir,
172
183
  appRoot: nodeModulesDir,
173
- sourceRoot: nodeModulesDir
184
+ sourceRoot: nodeModulesDir,
185
+ signal
174
186
  });
175
187
  }
176
188
  }
177
- async function normalizeArtifactSymlinks(artifactDir, appPath) {
189
+ async function normalizeArtifactSymlinks(artifactDir, appPath, signal) {
178
190
  const normalizedArtifactDir = path.resolve(artifactDir);
179
191
  const normalizedAppPath = path.resolve(appPath);
180
192
  await walkDirectory(normalizedArtifactDir);
181
193
  async function walkDirectory(directory) {
182
- const entries = await readdir(directory, { withFileTypes: true });
194
+ const entries = await unsupportedFilesystemBoundary(signal, () => readdir(directory, { withFileTypes: true }));
183
195
  for (const entry of entries) {
184
196
  const fullPath = path.join(directory, entry.name);
185
197
  if (entry.isDirectory()) {
@@ -187,19 +199,19 @@ async function normalizeArtifactSymlinks(artifactDir, appPath) {
187
199
  continue;
188
200
  }
189
201
  if (!entry.isSymbolicLink()) continue;
190
- const target = await readlink(fullPath);
202
+ const target = await unsupportedFilesystemBoundary(signal, () => readlink(fullPath));
191
203
  const resolvedTarget = path.resolve(path.dirname(fullPath), target);
192
204
  if (isPathWithin(normalizedArtifactDir, resolvedTarget)) continue;
193
205
  if (!isPathWithin(normalizedAppPath, resolvedTarget)) throw new Error(`Build artifact symlink escapes the app directory: ${resolvedTarget}`);
194
- const targetStat = await stat(resolvedTarget);
195
- await rm(fullPath, {
206
+ const targetStat = await unsupportedFilesystemBoundary(signal, () => stat(resolvedTarget));
207
+ await unsupportedFilesystemBoundary(signal, () => rm(fullPath, {
196
208
  force: true,
197
209
  recursive: true
198
- });
199
- await cp(resolvedTarget, fullPath, {
210
+ }));
211
+ await unsupportedFilesystemBoundary(signal, () => cp(resolvedTarget, fullPath, {
200
212
  recursive: targetStat.isDirectory(),
201
213
  dereference: true
202
- });
214
+ }));
203
215
  if (targetStat.isDirectory()) await walkDirectory(fullPath);
204
216
  }
205
217
  }
@@ -208,8 +220,11 @@ function isPathWithin(rootPath, candidatePath) {
208
220
  const relativePath = path.relative(rootPath, candidatePath);
209
221
  return relativePath === "" || !relativePath.startsWith(`..${path.sep}`) && relativePath !== ".." && !path.isAbsolute(relativePath);
210
222
  }
223
+ function isPathWithinWorkspaceDependency(sourceRoot, candidatePath) {
224
+ return isPathWithin(path.join(sourceRoot, "node_modules"), candidatePath);
225
+ }
211
226
  async function copyPathMaterializingSymlinks(sourcePath, destinationPath, options) {
212
- const sourceStat = await lstat(sourcePath);
227
+ const sourceStat = await unsupportedFilesystemBoundary(options.signal, () => lstat(sourcePath));
213
228
  if (sourceStat.isSymbolicLink()) {
214
229
  const resolvedTarget = await resolveSymlinkTarget(sourcePath, options);
215
230
  if (resolvedTarget === null) return;
@@ -217,27 +232,27 @@ async function copyPathMaterializingSymlinks(sourcePath, destinationPath, option
217
232
  return;
218
233
  }
219
234
  if (sourceStat.isDirectory()) {
220
- await mkdir(destinationPath, { recursive: true });
221
- const entries = await readdir(sourcePath, { withFileTypes: true });
235
+ await unsupportedFilesystemBoundary(options.signal, () => mkdir(destinationPath, { recursive: true }));
236
+ const entries = await unsupportedFilesystemBoundary(options.signal, () => readdir(sourcePath, { withFileTypes: true }));
222
237
  for (const entry of entries) await copyPathMaterializingSymlinks(path.join(sourcePath, entry.name), path.join(destinationPath, entry.name), options);
223
238
  return;
224
239
  }
225
240
  if (sourceStat.isFile()) {
226
- await mkdir(path.dirname(destinationPath), { recursive: true });
227
- await copyFile(sourcePath, destinationPath);
228
- await chmod(destinationPath, sourceStat.mode);
241
+ await unsupportedFilesystemBoundary(options.signal, () => mkdir(path.dirname(destinationPath), { recursive: true }));
242
+ await unsupportedFilesystemBoundary(options.signal, () => copyFile(sourcePath, destinationPath));
243
+ await unsupportedFilesystemBoundary(options.signal, () => chmod(destinationPath, sourceStat.mode));
229
244
  }
230
245
  }
231
246
  async function resolveSymlinkTarget(symlinkPath, options) {
232
- const linkTarget = await readlink(symlinkPath);
247
+ const linkTarget = await unsupportedFilesystemBoundary(options.signal, () => readlink(symlinkPath));
233
248
  const resolvedTarget = path.resolve(path.dirname(symlinkPath), linkTarget);
234
- if (await pathExists(resolvedTarget)) {
235
- if (!isPathWithin(options.appRoot, resolvedTarget) && !isPathWithin(options.sourceRoot, resolvedTarget)) throw new Error(`Build artifact symlink escapes the app directory: ${resolvedTarget}`);
249
+ if (await pathExists(resolvedTarget, options.signal)) {
250
+ if (!isPathWithin(options.appRoot, resolvedTarget) && !isPathWithinWorkspaceDependency(options.sourceRoot, resolvedTarget)) throw new Error(`Build artifact symlink escapes the app directory: ${resolvedTarget}`);
236
251
  return resolvedTarget;
237
252
  }
238
253
  if (isPathWithin(options.standaloneRoot, resolvedTarget)) {
239
254
  const fallbackTarget = path.join(options.appRoot, path.relative(options.standaloneRoot, resolvedTarget));
240
- if (await pathExists(fallbackTarget)) return fallbackTarget;
255
+ if (await pathExists(fallbackTarget, options.signal)) return fallbackTarget;
241
256
  }
242
257
  if (isPnpmHoistLink(symlinkPath)) return null;
243
258
  throw new Error(`Next.js standalone symlink target is missing: ${symlinkPath} -> ${linkTarget} (resolved to ${resolvedTarget})`);
@@ -247,38 +262,51 @@ function isPnpmHoistLink(symlinkPath) {
247
262
  for (let i = 0; i < parts.length - 1; i++) if (parts[i] === ".pnpm" && parts[i + 1] === "node_modules") return true;
248
263
  return false;
249
264
  }
250
- async function pathExists(targetPath) {
265
+ async function pathExists(targetPath, signal) {
251
266
  try {
252
- await stat(targetPath);
267
+ await unsupportedFilesystemBoundary(signal, () => stat(targetPath));
253
268
  return true;
254
- } catch {
269
+ } catch (error) {
270
+ if (signal?.aborted) throw error;
255
271
  return false;
256
272
  }
257
273
  }
258
- async function directoryExists(targetPath) {
274
+ async function directoryExists(targetPath, signal) {
259
275
  try {
260
- return (await stat(targetPath)).isDirectory();
261
- } catch {
276
+ return (await unsupportedFilesystemBoundary(signal, () => stat(targetPath))).isDirectory();
277
+ } catch (error) {
278
+ if (signal?.aborted) throw error;
262
279
  return false;
263
280
  }
264
281
  }
265
- async function resolveSourceRoot(appRoot) {
282
+ async function resolveSourceRoot(appRoot, signal) {
266
283
  let current = path.resolve(appRoot);
267
284
  while (true) {
268
- if (await pathExists(path.join(current, ".git")) || await pathExists(path.join(current, "pnpm-workspace.yaml")) || await pathExists(path.join(current, "bun.lock")) || await pathExists(path.join(current, "bun.lockb")) || await packageJsonDeclaresWorkspaces(current)) return current;
285
+ if (await pathExists(path.join(current, ".git"), signal) || await pathExists(path.join(current, "pnpm-workspace.yaml"), signal) || await pathExists(path.join(current, "bun.lock"), signal) || await pathExists(path.join(current, "bun.lockb"), signal) || await packageJsonDeclaresWorkspaces(current, signal)) return current;
269
286
  const parent = path.dirname(current);
270
287
  if (parent === current) return path.resolve(appRoot);
271
288
  current = parent;
272
289
  }
273
290
  }
274
- async function packageJsonDeclaresWorkspaces(directory) {
291
+ async function packageJsonDeclaresWorkspaces(directory, signal) {
292
+ signal?.throwIfAborted();
275
293
  try {
276
- const content = await readFile(path.join(directory, "package.json"), "utf8");
294
+ const content = await readFile(path.join(directory, "package.json"), {
295
+ encoding: "utf8",
296
+ signal
297
+ });
277
298
  const parsed = JSON.parse(content);
278
299
  return Boolean(parsed.workspaces);
279
- } catch {
300
+ } catch (error) {
301
+ if (signal?.aborted) throw error;
280
302
  return false;
281
303
  }
282
304
  }
305
+ async function unsupportedFilesystemBoundary(signal, operation) {
306
+ signal?.throwIfAborted();
307
+ const result = await operation();
308
+ signal?.throwIfAborted();
309
+ return result;
310
+ }
283
311
  //#endregion
284
312
  export { PREVIEW_BUILD_TYPES, PreviewBuildStrategy, RESOLVED_PREVIEW_BUILD_TYPES, executePreviewBuild };
@@ -19,7 +19,10 @@ function createPreviewAppProvider(client, options) {
19
19
  const sdk = new ComputeClient(client);
20
20
  return {
21
21
  async createProject(options) {
22
- const projectResult = await sdk.createProject({ name: options.name });
22
+ const projectResult = await sdk.createProject({
23
+ name: options.name,
24
+ signal: options.signal
25
+ });
23
26
  if (projectResult.isErr()) throw new Error(projectResult.error.message);
24
27
  return {
25
28
  id: projectResult.value.id,
@@ -29,17 +32,22 @@ function createPreviewAppProvider(client, options) {
29
32
  async listApps(projectId, options) {
30
33
  return listComputeServices(client, {
31
34
  projectId,
32
- branchGitName: options?.branchName
35
+ branchGitName: options?.branchName,
36
+ signal: options?.signal
33
37
  });
34
38
  },
35
- async removeApp(appId) {
36
- const appResult = await sdk.showService({ serviceId: appId });
39
+ async removeApp(appId, options) {
40
+ const appResult = await sdk.showService({
41
+ serviceId: appId,
42
+ signal: options?.signal
43
+ });
37
44
  if (appResult.isErr()) throw new Error(appResult.error.message);
38
45
  const destroyResult = await sdk.destroyService({
39
46
  serviceId: appId,
40
47
  keepService: false,
41
48
  timeoutSeconds: 120,
42
- pollIntervalMs: 2e3
49
+ pollIntervalMs: 2e3,
50
+ signal: options?.signal
43
51
  });
44
52
  if (destroyResult.isErr()) throw new Error(destroyResult.error.message);
45
53
  return {
@@ -47,17 +55,18 @@ function createPreviewAppProvider(client, options) {
47
55
  name: appResult.value.name
48
56
  };
49
57
  },
50
- async listDomains(appId) {
51
- return listComputeServiceDomains(client, appId);
58
+ async listDomains(appId, options) {
59
+ return listComputeServiceDomains(client, appId, options?.signal);
52
60
  },
53
61
  async addDomain(options) {
54
62
  const result = await client.POST("/v1/compute-services/{computeServiceId}/domains", {
55
63
  params: { path: { computeServiceId: options.appId } },
56
- body: { hostname: options.hostname }
64
+ body: { hostname: options.hostname },
65
+ signal: options.signal
57
66
  });
58
67
  if (result.error || !result.data) {
59
68
  if (result.response.status === 409) {
60
- const existing = (await listComputeServiceDomains(client, options.appId)).find((domain) => sameHostname(domain.hostname, options.hostname));
69
+ const existing = (await listComputeServiceDomains(client, options.appId, options.signal)).find((domain) => sameHostname(domain.hostname, options.hostname));
61
70
  if (existing) return {
62
71
  domain: existing,
63
72
  existing: true
@@ -70,17 +79,26 @@ function createPreviewAppProvider(client, options) {
70
79
  existing: false
71
80
  };
72
81
  },
73
- async showDomain(domainId) {
74
- const result = await client.GET("/v1/domains/{domainId}", { params: { path: { domainId } } });
82
+ async showDomain(domainId, options) {
83
+ const result = await client.GET("/v1/domains/{domainId}", {
84
+ params: { path: { domainId } },
85
+ signal: options?.signal
86
+ });
75
87
  if (result.error || !result.data) throw domainApiCallError("Failed to show custom domain", result.response, result.error);
76
88
  return normalizeDomainRecord(result.data.data);
77
89
  },
78
- async removeDomain(domainId) {
79
- const result = await client.DELETE("/v1/domains/{domainId}", { params: { path: { domainId } } });
90
+ async removeDomain(domainId, options) {
91
+ const result = await client.DELETE("/v1/domains/{domainId}", {
92
+ params: { path: { domainId } },
93
+ signal: options?.signal
94
+ });
80
95
  if (result.error) throw domainApiCallError("Failed to remove custom domain", result.response, result.error);
81
96
  },
82
- async retryDomain(domainId) {
83
- const result = await client.POST("/v1/domains/{domainId}/retry", { params: { path: { domainId } } });
97
+ async retryDomain(domainId, options) {
98
+ const result = await client.POST("/v1/domains/{domainId}/retry", {
99
+ params: { path: { domainId } },
100
+ signal: options?.signal
101
+ });
84
102
  if (result.error || !result.data) throw domainApiCallError("Failed to retry custom domain", result.response, result.error);
85
103
  return normalizeDomainRecord(result.data.data);
86
104
  },
@@ -90,6 +108,7 @@ function createPreviewAppProvider(client, options) {
90
108
  versionId: options.deploymentId,
91
109
  timeoutSeconds: 120,
92
110
  pollIntervalMs: 2e3,
111
+ signal: options.signal,
93
112
  progress: options.progress
94
113
  });
95
114
  if (promoteResult.isErr()) throw new Error(promoteResult.error.message);
@@ -103,7 +122,8 @@ function createPreviewAppProvider(client, options) {
103
122
  projectId: options.projectId,
104
123
  branchName: options.branchName,
105
124
  appName: options.appName,
106
- region: options.region
125
+ region: options.region,
126
+ signal: options.signal
107
127
  }) : {
108
128
  appId: void 0,
109
129
  appName: options.appName,
@@ -113,7 +133,8 @@ function createPreviewAppProvider(client, options) {
113
133
  strategy: new PreviewBuildStrategy({
114
134
  appPath: path.resolve(options.cwd),
115
135
  entrypoint: options.entrypoint,
116
- buildType: options.buildType
136
+ buildType: options.buildType,
137
+ signal: options.signal
117
138
  }),
118
139
  projectId: options.projectId,
119
140
  serviceId: resolvedApp.appId,
@@ -124,6 +145,7 @@ function createPreviewAppProvider(client, options) {
124
145
  timeoutSeconds: 120,
125
146
  pollIntervalMs: 2e3,
126
147
  interaction: options.interaction,
148
+ signal: options.signal,
127
149
  progress: options.progress
128
150
  });
129
151
  if (deployResult.isErr()) throw new Error(deployResult.error.message);
@@ -150,6 +172,7 @@ function createPreviewAppProvider(client, options) {
150
172
  envVars: options.envVars,
151
173
  timeoutSeconds: 120,
152
174
  pollIntervalMs: 2e3,
175
+ signal: options.signal,
153
176
  progress: options.progress
154
177
  });
155
178
  if (updateResult.isErr()) throw new Error(updateResult.error.message);
@@ -158,10 +181,17 @@ function createPreviewAppProvider(client, options) {
158
181
  versionId: updateResult.value.versionId,
159
182
  timeoutSeconds: 120,
160
183
  pollIntervalMs: 2e3,
184
+ signal: options.signal,
161
185
  progress: options.promoteProgress
162
186
  });
163
187
  if (promoteResult.isErr()) throw new Error(promoteResult.error.message);
164
- const [serviceResult, versionResult] = await Promise.all([sdk.showService({ serviceId: options.appId }), sdk.showVersion({ versionId: updateResult.value.versionId })]);
188
+ const [serviceResult, versionResult] = await Promise.all([sdk.showService({
189
+ serviceId: options.appId,
190
+ signal: options.signal
191
+ }), sdk.showVersion({
192
+ versionId: updateResult.value.versionId,
193
+ signal: options.signal
194
+ })]);
165
195
  if (serviceResult.isErr()) throw new Error(serviceResult.error.message);
166
196
  if (versionResult.isErr()) throw new Error(versionResult.error.message);
167
197
  return {
@@ -184,7 +214,13 @@ function createPreviewAppProvider(client, options) {
184
214
  };
185
215
  },
186
216
  async listAppEnvNames(options) {
187
- const [serviceResult, versionResult] = await Promise.all([sdk.showService({ serviceId: options.appId }), sdk.showVersion({ versionId: options.deploymentId })]);
217
+ const [serviceResult, versionResult] = await Promise.all([sdk.showService({
218
+ serviceId: options.appId,
219
+ signal: options.signal
220
+ }), sdk.showVersion({
221
+ versionId: options.deploymentId,
222
+ signal: options.signal
223
+ })]);
188
224
  if (serviceResult.isErr()) throw new Error(serviceResult.error.message);
189
225
  if (versionResult.isErr()) throw new Error(versionResult.error.message);
190
226
  return {
@@ -206,8 +242,14 @@ function createPreviewAppProvider(client, options) {
206
242
  variables: envVarNames(versionResult.value.envVars)
207
243
  };
208
244
  },
209
- async listDeployments(appId) {
210
- const [appResult, versionsResult] = await Promise.all([sdk.showService({ serviceId: appId }), sdk.listVersions({ serviceId: appId })]);
245
+ async listDeployments(appId, options) {
246
+ const [appResult, versionsResult] = await Promise.all([sdk.showService({
247
+ serviceId: appId,
248
+ signal: options?.signal
249
+ }), sdk.listVersions({
250
+ serviceId: appId,
251
+ signal: options?.signal
252
+ })]);
211
253
  if (appResult.isErr()) throw new Error(appResult.error.message);
212
254
  if (versionsResult.isErr()) throw new Error(versionsResult.error.message);
213
255
  return {
@@ -230,14 +272,17 @@ function createPreviewAppProvider(client, options) {
230
272
  }))
231
273
  };
232
274
  },
233
- async showDeployment(deploymentId) {
234
- const deploymentResult = await sdk.showVersion({ versionId: deploymentId });
275
+ async showDeployment(deploymentId, options) {
276
+ const deploymentResult = await sdk.showVersion({
277
+ versionId: deploymentId,
278
+ signal: options?.signal
279
+ });
235
280
  if (deploymentResult.isErr()) {
236
281
  if (ApiError.is(deploymentResult.error) && deploymentResult.error.statusCode === 404) return null;
237
282
  throw new Error(deploymentResult.error.message);
238
283
  }
239
284
  return {
240
- app: await findAppForDeployment(sdk, deploymentId),
285
+ app: await findAppForDeployment(sdk, deploymentId, options?.signal),
241
286
  deployment: {
242
287
  id: deploymentResult.value.id,
243
288
  status: deploymentResult.value.status,
@@ -263,10 +308,13 @@ function createPreviewAppProvider(client, options) {
263
308
  };
264
309
  }
265
310
  async function listBranches(client, options) {
266
- const result = await client.GET("/v1/projects/{projectId}/branches", { params: {
267
- path: { projectId: options.projectId },
268
- query: { gitName: options.gitName }
269
- } });
311
+ const result = await client.GET("/v1/projects/{projectId}/branches", {
312
+ params: {
313
+ path: { projectId: options.projectId },
314
+ query: { gitName: options.gitName }
315
+ },
316
+ signal: options.signal
317
+ });
270
318
  if (result.error || !result.data) throw apiCallError("Failed to list branches", result.response, result.error);
271
319
  return result.data.data;
272
320
  }
@@ -278,7 +326,8 @@ async function resolveOrCreateBranch(client, options) {
278
326
  body: {
279
327
  gitName: options.gitName,
280
328
  isDefault: options.gitName === "main"
281
- }
329
+ },
330
+ signal: options.signal
282
331
  });
283
332
  if (result.error || !result.data) {
284
333
  if (result.response.status === 409) {
@@ -293,11 +342,14 @@ async function listComputeServices(client, options) {
293
342
  const services = [];
294
343
  let cursor;
295
344
  while (true) {
296
- const result = await client.GET("/v1/compute-services", { params: { query: {
297
- projectId: options.projectId,
298
- branchGitName: options.branchGitName,
299
- cursor
300
- } } });
345
+ const result = await client.GET("/v1/compute-services", {
346
+ params: { query: {
347
+ projectId: options.projectId,
348
+ branchGitName: options.branchGitName,
349
+ cursor
350
+ } },
351
+ signal: options.signal
352
+ });
301
353
  if (result.error || !result.data) throw apiCallError("Failed to list apps", result.response, result.error);
302
354
  services.push(...result.data.data);
303
355
  if (!result.data.pagination.hasMore || !result.data.pagination.nextCursor) break;
@@ -312,8 +364,11 @@ async function listComputeServices(client, options) {
312
364
  liveUrl: toAbsoluteUrl(service.serviceEndpointDomain ?? null)
313
365
  }));
314
366
  }
315
- async function listComputeServiceDomains(client, computeServiceId) {
316
- const result = await client.GET("/v1/compute-services/{computeServiceId}/domains", { params: { path: { computeServiceId } } });
367
+ async function listComputeServiceDomains(client, computeServiceId, signal) {
368
+ const result = await client.GET("/v1/compute-services/{computeServiceId}/domains", {
369
+ params: { path: { computeServiceId } },
370
+ signal
371
+ });
317
372
  if (result.error || !result.data) throw domainApiCallError("Failed to list custom domains", result.response, result.error);
318
373
  return result.data.data.map((domain) => normalizeDomainRecord(domain));
319
374
  }
@@ -355,19 +410,24 @@ function normalizeHostnameForComparison(hostname) {
355
410
  async function createBranchApp(client, options) {
356
411
  const branch = await resolveOrCreateBranch(client, {
357
412
  projectId: options.projectId,
358
- gitName: options.branchName
413
+ gitName: options.branchName,
414
+ signal: options.signal
415
+ });
416
+ const result = await client.POST("/v1/compute-services", {
417
+ body: {
418
+ projectId: options.projectId,
419
+ branchId: branch.id,
420
+ displayName: options.appName,
421
+ ...options.region ? { regionId: options.region } : {}
422
+ },
423
+ signal: options.signal
359
424
  });
360
- const result = await client.POST("/v1/compute-services", { body: {
361
- projectId: options.projectId,
362
- branchId: branch.id,
363
- displayName: options.appName,
364
- ...options.region ? { regionId: options.region } : {}
365
- } });
366
425
  if (result.error || !result.data) {
367
426
  if (result.response.status === 409) {
368
427
  const matched = (await listComputeServices(client, {
369
428
  projectId: options.projectId,
370
- branchGitName: options.branchName
429
+ branchGitName: options.branchName,
430
+ signal: options.signal
371
431
  })).find((app) => app.name === options.appName);
372
432
  if (matched) return {
373
433
  appId: matched.id,
@@ -399,14 +459,20 @@ function domainApiCallError(summary, response, error) {
399
459
  hint: error.error?.hint ?? null
400
460
  });
401
461
  }
402
- async function findAppForDeployment(sdk, deploymentId) {
403
- const projectsResult = await sdk.listProjects();
462
+ async function findAppForDeployment(sdk, deploymentId, signal) {
463
+ const projectsResult = await sdk.listProjects({ signal });
404
464
  if (projectsResult.isErr()) throw new Error(projectsResult.error.message);
405
465
  for (const project of projectsResult.value) {
406
- const servicesResult = await sdk.listServices({ projectId: project.id });
466
+ const servicesResult = await sdk.listServices({
467
+ projectId: project.id,
468
+ signal
469
+ });
407
470
  if (servicesResult.isErr()) throw new Error(servicesResult.error.message);
408
471
  for (const service of servicesResult.value) {
409
- const detailResult = await sdk.showService({ serviceId: service.id });
472
+ const detailResult = await sdk.showService({
473
+ serviceId: service.id,
474
+ signal
475
+ });
410
476
  if (detailResult.isErr()) throw new Error(detailResult.error.message);
411
477
  const app = {
412
478
  id: detailResult.value.id,
@@ -416,7 +482,10 @@ async function findAppForDeployment(sdk, deploymentId) {
416
482
  liveUrl: toAbsoluteUrl(detailResult.value.serviceEndpointDomain ?? null)
417
483
  };
418
484
  if (app.liveDeploymentId === deploymentId) return app;
419
- const versionsResult = await sdk.listVersions({ serviceId: service.id });
485
+ const versionsResult = await sdk.listVersions({
486
+ serviceId: service.id,
487
+ signal
488
+ });
420
489
  if (versionsResult.isErr()) throw new Error(versionsResult.error.message);
421
490
  if (versionsResult.value.some((version) => version.id === deploymentId)) return app;
422
491
  }