@prisma/compute-sdk 0.19.0 → 0.20.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.
Files changed (49) hide show
  1. package/dist/api-client.d.ts.map +1 -1
  2. package/dist/api-client.js +24 -8
  3. package/dist/api-client.js.map +1 -1
  4. package/dist/archive.d.ts +1 -1
  5. package/dist/archive.d.ts.map +1 -1
  6. package/dist/archive.js +34 -15
  7. package/dist/archive.js.map +1 -1
  8. package/dist/astro-build.d.ts +2 -2
  9. package/dist/astro-build.d.ts.map +1 -1
  10. package/dist/astro-build.js +6 -4
  11. package/dist/astro-build.js.map +1 -1
  12. package/dist/auto-build.d.ts +2 -2
  13. package/dist/auto-build.d.ts.map +1 -1
  14. package/dist/auto-build.js +7 -5
  15. package/dist/auto-build.js.map +1 -1
  16. package/dist/build-strategy.d.ts +7 -6
  17. package/dist/build-strategy.d.ts.map +1 -1
  18. package/dist/build-strategy.js +11 -7
  19. package/dist/build-strategy.js.map +1 -1
  20. package/dist/bun-build.d.ts +2 -2
  21. package/dist/bun-build.d.ts.map +1 -1
  22. package/dist/bun-build.js +9 -7
  23. package/dist/bun-build.js.map +1 -1
  24. package/dist/compute-client.d.ts.map +1 -1
  25. package/dist/compute-client.js +16 -9
  26. package/dist/compute-client.js.map +1 -1
  27. package/dist/nextjs-build.d.ts +2 -2
  28. package/dist/nextjs-build.d.ts.map +1 -1
  29. package/dist/nextjs-build.js +6 -4
  30. package/dist/nextjs-build.js.map +1 -1
  31. package/dist/nuxt-build.d.ts +2 -2
  32. package/dist/nuxt-build.d.ts.map +1 -1
  33. package/dist/nuxt-build.js +6 -4
  34. package/dist/nuxt-build.js.map +1 -1
  35. package/dist/tanstack-start-build.d.ts +2 -2
  36. package/dist/tanstack-start-build.d.ts.map +1 -1
  37. package/dist/tanstack-start-build.js +5 -3
  38. package/dist/tanstack-start-build.js.map +1 -1
  39. package/package.json +1 -1
  40. package/src/api-client.ts +24 -8
  41. package/src/archive.ts +42 -10
  42. package/src/astro-build.ts +6 -4
  43. package/src/auto-build.ts +7 -5
  44. package/src/build-strategy.ts +14 -6
  45. package/src/bun-build.ts +13 -6
  46. package/src/compute-client.ts +21 -12
  47. package/src/nextjs-build.ts +6 -4
  48. package/src/nuxt-build.ts +6 -4
  49. package/src/tanstack-start-build.ts +5 -3
package/src/archive.ts CHANGED
@@ -17,12 +17,14 @@ const COMPUTE_MANIFEST_VERSION = "1" as const;
17
17
  export async function createArchive(
18
18
  directory: string,
19
19
  entrypoint: string,
20
+ signal?: AbortSignal,
20
21
  ): Promise<Uint8Array> {
22
+ signal?.throwIfAborted();
21
23
  const packer = pack();
22
24
  const rootPath = path.resolve(directory);
23
25
 
24
26
  // Walk the directory and add all files
25
- await addDirectory(packer, rootPath, rootPath, "bundle");
27
+ await addDirectory(packer, rootPath, rootPath, "bundle", signal);
26
28
 
27
29
  // Inject the manifest as a synthetic entry
28
30
  const manifest = JSON.stringify(
@@ -39,7 +41,7 @@ export async function createArchive(
39
41
  packer.finalize();
40
42
 
41
43
  // Pipe through gzip and collect into buffer
42
- return await collectGzipped(packer);
44
+ return await collectGzipped(packer, signal);
43
45
  }
44
46
 
45
47
  async function addDirectory(
@@ -47,22 +49,25 @@ async function addDirectory(
47
49
  rootPath: string,
48
50
  fsPath: string,
49
51
  tarPrefix: string,
52
+ signal?: AbortSignal,
50
53
  ): Promise<void> {
54
+ signal?.throwIfAborted();
51
55
  const directoryPath = resolvePathWithinRoot(rootPath, fsPath);
52
56
  const entries = await readdir(directoryPath, { withFileTypes: true });
53
57
 
54
58
  for (const entry of entries) {
59
+ signal?.throwIfAborted();
55
60
  const fullPath = path.join(directoryPath, entry.name);
56
61
  const tarPath = `${tarPrefix}/${entry.name}`;
57
62
 
58
63
  if (entry.isSymbolicLink()) {
59
64
  const linkStat = await lstat(fullPath);
60
- await addSymlink(packer, rootPath, fullPath, tarPath, linkStat);
65
+ await addSymlink(packer, rootPath, fullPath, tarPath, linkStat, signal);
61
66
  } else if (entry.isDirectory()) {
62
- await addDirectory(packer, rootPath, fullPath, tarPath);
67
+ await addDirectory(packer, rootPath, fullPath, tarPath, signal);
63
68
  } else if (entry.isFile()) {
64
69
  const fileStat = await lstat(fullPath);
65
- await addFile(packer, rootPath, fullPath, tarPath, fileStat);
70
+ await addFile(packer, rootPath, fullPath, tarPath, fileStat, signal);
66
71
  }
67
72
  }
68
73
  }
@@ -73,7 +78,9 @@ async function addFile(
73
78
  fsPath: string,
74
79
  tarPath: string,
75
80
  fileStat: { size: number; mode: number; mtime: Date },
81
+ signal?: AbortSignal,
76
82
  ): Promise<void> {
83
+ signal?.throwIfAborted();
77
84
  const filePath = resolvePathWithinRoot(rootPath, fsPath);
78
85
  const content = await readFile(filePath);
79
86
  packer.entry(
@@ -93,13 +100,16 @@ async function addSymlink(
93
100
  fsPath: string,
94
101
  tarPath: string,
95
102
  linkStat: { mode: number; mtime: Date },
103
+ signal?: AbortSignal,
96
104
  ): Promise<void> {
105
+ signal?.throwIfAborted();
97
106
  const symlinkPath = resolvePathWithinRoot(rootPath, fsPath);
98
107
  const target = await readlink(symlinkPath);
99
108
  const linkname = await resolveArchiveSymlinkTarget(
100
109
  rootPath,
101
110
  symlinkPath,
102
111
  target,
112
+ signal,
103
113
  );
104
114
 
105
115
  packer.entry({
@@ -115,7 +125,9 @@ async function resolveArchiveSymlinkTarget(
115
125
  rootPath: string,
116
126
  symlinkPath: string,
117
127
  target: string,
128
+ signal?: AbortSignal,
118
129
  ): Promise<string> {
130
+ signal?.throwIfAborted();
119
131
  const symlinkDir = path.dirname(symlinkPath);
120
132
  const resolvedTarget = resolvePathWithinRoot(
121
133
  rootPath,
@@ -163,11 +175,27 @@ function resolvePathWithinRoot(rootPath: string, fsPath: string): string {
163
175
  return resolvedPath;
164
176
  }
165
177
 
166
- function collectGzipped(packer: Pack): Promise<Uint8Array> {
167
- return new Promise<Uint8Array>((resolve, reject) => {
168
- const chunks: Buffer[] = [];
169
- const gzip = createGzip();
170
-
178
+ function collectGzipped(
179
+ packer: Pack,
180
+ signal?: AbortSignal,
181
+ ): Promise<Uint8Array> {
182
+ signal?.throwIfAborted();
183
+
184
+ const chunks: Buffer[] = [];
185
+ const gzip = createGzip();
186
+ const onAbort = () => {
187
+ // AbortSignal.abort() sets signal.reason; fallback keeps a stream error path if a non-standard signal omits it.
188
+ const abortError =
189
+ signal?.reason instanceof Error
190
+ ? signal.reason
191
+ : new DOMException("The operation was aborted", "AbortError");
192
+ gzip.destroy(abortError);
193
+ packer.destroy(abortError);
194
+ };
195
+
196
+ signal?.addEventListener("abort", onAbort, { once: true });
197
+
198
+ const promise = new Promise<Uint8Array>((resolve, reject) => {
171
199
  packer.pipe(gzip);
172
200
 
173
201
  gzip.on("data", (chunk: Buffer) => {
@@ -181,4 +209,8 @@ function collectGzipped(packer: Pack): Promise<Uint8Array> {
181
209
  gzip.on("error", reject);
182
210
  packer.on("error", reject);
183
211
  });
212
+
213
+ return promise.finally(() => {
214
+ signal?.removeEventListener("abort", onAbort);
215
+ });
184
216
  }
@@ -27,14 +27,15 @@ export class AstroBuild implements BuildStrategy {
27
27
  this.#appPath = options.appPath;
28
28
  }
29
29
 
30
- async canBuild(): Promise<boolean> {
30
+ async canBuild(signal?: AbortSignal): Promise<boolean> {
31
31
  return (
32
- (await hasRootFile(this.#appPath, ASTRO_CONFIG_FILENAMES)) ||
33
- (await hasPackageDependency(this.#appPath, ["astro"]))
32
+ (await hasRootFile(this.#appPath, ASTRO_CONFIG_FILENAMES, signal)) ||
33
+ (await hasPackageDependency(this.#appPath, ["astro"], signal))
34
34
  );
35
35
  }
36
36
 
37
- async execute(): Promise<BuildArtifact> {
37
+ async execute(signal?: AbortSignal): Promise<BuildArtifact> {
38
+ signal?.throwIfAborted();
38
39
  await runPackageCli({
39
40
  appPath: this.#appPath,
40
41
  cliName: "astro",
@@ -42,6 +43,7 @@ export class AstroBuild implements BuildStrategy {
42
43
  failurePrefix: "Astro",
43
44
  missingMessage:
44
45
  "Could not find the Astro CLI. Install it with `npm install astro` or ensure npx/bunx is available.",
46
+ signal,
45
47
  });
46
48
 
47
49
  const distDir = path.join(this.#appPath, "dist");
package/src/auto-build.ts CHANGED
@@ -25,19 +25,21 @@ export class AutoBuild implements BuildStrategy {
25
25
  ];
26
26
  }
27
27
 
28
- async canBuild(): Promise<boolean> {
28
+ async canBuild(signal?: AbortSignal): Promise<boolean> {
29
29
  for (const strategy of this.#strategies) {
30
- if (await strategy.canBuild()) {
30
+ signal?.throwIfAborted();
31
+ if (await strategy.canBuild(signal)) {
31
32
  return true;
32
33
  }
33
34
  }
34
35
  return false;
35
36
  }
36
37
 
37
- async execute(): Promise<BuildArtifact> {
38
+ async execute(signal?: AbortSignal): Promise<BuildArtifact> {
38
39
  for (const strategy of this.#strategies) {
39
- if (await strategy.canBuild()) {
40
- return strategy.execute();
40
+ signal?.throwIfAborted();
41
+ if (await strategy.canBuild(signal)) {
42
+ return strategy.execute(signal);
41
43
  }
42
44
  }
43
45
  throw new Error("No suitable build strategy found for this application");
@@ -32,8 +32,8 @@ export interface BuildArtifact {
32
32
  * A build strategy produces a deployable artifact.
33
33
  */
34
34
  export interface BuildStrategy {
35
- canBuild(): Promise<boolean>;
36
- execute(): Promise<BuildArtifact>;
35
+ canBuild(signal?: AbortSignal): Promise<boolean>;
36
+ execute(signal?: AbortSignal): Promise<BuildArtifact>;
37
37
  }
38
38
 
39
39
  /**
@@ -49,11 +49,12 @@ export class PreBuilt implements BuildStrategy {
49
49
  this.#entrypoint = options.entrypoint;
50
50
  }
51
51
 
52
- async canBuild(): Promise<boolean> {
52
+ async canBuild(_signal?: AbortSignal): Promise<boolean> {
53
53
  return true;
54
54
  }
55
55
 
56
- async execute(): Promise<BuildArtifact> {
56
+ async execute(signal?: AbortSignal): Promise<BuildArtifact> {
57
+ signal?.throwIfAborted();
57
58
  const normalized = path.normalize(this.#entrypoint);
58
59
 
59
60
  if (path.isAbsolute(normalized)) {
@@ -92,7 +93,9 @@ export class PreBuilt implements BuildStrategy {
92
93
  export async function hasRootFile(
93
94
  appPath: string,
94
95
  filenames: readonly string[],
96
+ signal?: AbortSignal,
95
97
  ): Promise<boolean> {
98
+ signal?.throwIfAborted();
96
99
  let entries: string[];
97
100
  try {
98
101
  entries = await readdir(appPath);
@@ -110,7 +113,9 @@ export async function hasRootFile(
110
113
  export async function hasPackageDependency(
111
114
  appPath: string,
112
115
  packageNames: readonly string[],
116
+ signal?: AbortSignal,
113
117
  ): Promise<boolean> {
118
+ signal?.throwIfAborted();
114
119
  let content: string;
115
120
  try {
116
121
  content = await readFile(path.join(appPath, "package.json"), "utf-8");
@@ -154,6 +159,7 @@ export async function runPackageCli(opts: {
154
159
  failurePrefix: string;
155
160
  /** Thrown when no launcher is available. */
156
161
  missingMessage: string;
162
+ signal?: AbortSignal;
157
163
  }): Promise<void> {
158
164
  const localBin = path.join(
159
165
  opts.appPath,
@@ -168,8 +174,9 @@ export async function runPackageCli(opts: {
168
174
  ];
169
175
 
170
176
  for (const { command, args } of candidates) {
177
+ opts.signal?.throwIfAborted();
171
178
  try {
172
- await exec(command, args, opts.appPath, opts.failurePrefix);
179
+ await exec(command, args, opts.appPath, opts.failurePrefix, opts.signal);
173
180
  return;
174
181
  } catch (error) {
175
182
  if (isENOENT(error)) continue;
@@ -197,9 +204,10 @@ function exec(
197
204
  args: string[],
198
205
  cwd: string,
199
206
  failurePrefix: string,
207
+ signal?: AbortSignal,
200
208
  ): Promise<void> {
201
209
  return new Promise((resolve, reject) => {
202
- execFile(command, args, { cwd }, (error, _stdout, stderr) => {
210
+ execFile(command, args, { cwd, signal }, (error, _stdout, stderr) => {
203
211
  if (error) {
204
212
  if ("code" in error && error.code === "ENOENT") {
205
213
  reject(
package/src/bun-build.ts CHANGED
@@ -24,12 +24,13 @@ export class BunBuild implements BuildStrategy {
24
24
  this.#entrypoint = options.entrypoint;
25
25
  }
26
26
 
27
- async canBuild(): Promise<boolean> {
27
+ async canBuild(_signal?: AbortSignal): Promise<boolean> {
28
28
  return true;
29
29
  }
30
30
 
31
- async execute(): Promise<BuildArtifact> {
32
- const entrypoint = await this.#resolveEntrypoint();
31
+ async execute(signal?: AbortSignal): Promise<BuildArtifact> {
32
+ signal?.throwIfAborted();
33
+ const entrypoint = await this.#resolveEntrypoint(signal);
33
34
 
34
35
  const outDir = await mkdtemp(path.join(os.tmpdir(), "compute-build-"));
35
36
  const bundleDir = path.join(outDir, "bundle");
@@ -38,7 +39,7 @@ export class BunBuild implements BuildStrategy {
38
39
  const absoluteEntrypoint = path.join(this.#appPath, entrypoint);
39
40
 
40
41
  try {
41
- await this.#runBuild(absoluteEntrypoint, bundleDir);
42
+ await this.#runBuild(absoluteEntrypoint, bundleDir, signal);
42
43
  } catch (error) {
43
44
  await rm(outDir, { recursive: true, force: true });
44
45
  throw error;
@@ -62,7 +63,8 @@ export class BunBuild implements BuildStrategy {
62
63
  };
63
64
  }
64
65
 
65
- async #resolveEntrypoint(): Promise<string> {
66
+ async #resolveEntrypoint(signal?: AbortSignal): Promise<string> {
67
+ signal?.throwIfAborted();
66
68
  const candidate = this.#entrypoint ?? (await this.#readPackageJsonMain());
67
69
 
68
70
  if (!candidate) {
@@ -125,7 +127,11 @@ export class BunBuild implements BuildStrategy {
125
127
  return typeof parsed.main === "string" ? parsed.main : undefined;
126
128
  }
127
129
 
128
- #runBuild(absoluteEntrypoint: string, bundleDir: string): Promise<void> {
130
+ #runBuild(
131
+ absoluteEntrypoint: string,
132
+ bundleDir: string,
133
+ signal?: AbortSignal,
134
+ ): Promise<void> {
129
135
  return new Promise((resolve, reject) => {
130
136
  execFile(
131
137
  "bun",
@@ -138,6 +144,7 @@ export class BunBuild implements BuildStrategy {
138
144
  "bun",
139
145
  "--sourcemap=external",
140
146
  ],
147
+ { signal },
141
148
  (error, _stdout, stderr) => {
142
149
  if (error) {
143
150
  if ("code" in error && error.code === "ENOENT") {
@@ -243,7 +243,7 @@ export class ComputeClient {
243
243
  options.progress?.onBuildStart?.();
244
244
  const artifact = yield* Result.await(
245
245
  Result.tryPromise({
246
- try: () => options.strategy.execute(),
246
+ try: () => options.strategy.execute(options.signal),
247
247
  catch: (e) =>
248
248
  new BuildError({
249
249
  message: e instanceof Error ? e.message : String(e),
@@ -258,7 +258,12 @@ export class ComputeClient {
258
258
  options.progress?.onArchiveCreating?.();
259
259
  const archiveBytes = yield* Result.await(
260
260
  Result.tryPromise({
261
- try: () => createArchive(artifact.directory, artifact.entrypoint),
261
+ try: () =>
262
+ createArchive(
263
+ artifact.directory,
264
+ artifact.entrypoint,
265
+ options.signal,
266
+ ),
262
267
  catch: (e) =>
263
268
  new ArtifactError({
264
269
  message:
@@ -1152,10 +1157,12 @@ export class ComputeClient {
1152
1157
  }
1153
1158
 
1154
1159
  #checkAborted(signal?: AbortSignal): Result<void, CancelledError> {
1155
- if (signal?.aborted) {
1160
+ try {
1161
+ signal?.throwIfAborted();
1162
+ return Result.ok(undefined);
1163
+ } catch {
1156
1164
  return Result.err(new CancelledError());
1157
1165
  }
1158
- return Result.ok(undefined);
1159
1166
  }
1160
1167
 
1161
1168
  async #resolveDeployTarget(options: {
@@ -1311,11 +1318,13 @@ export class ComputeClient {
1311
1318
  ): Promise<Result<void, ArtifactError | CancelledError>> {
1312
1319
  return Result.tryPromise({
1313
1320
  try: async () => {
1321
+ signal?.throwIfAborted();
1322
+ // Intentionally do not pass AbortSignal to this mutating upload request.
1323
+ // Aborting transport can leave completion ambiguous server-side.
1314
1324
  const res = await fetch(url, {
1315
1325
  method: "PUT",
1316
1326
  headers: { "content-type": "application/gzip" },
1317
1327
  body,
1318
- signal,
1319
1328
  });
1320
1329
 
1321
1330
  if (!res.ok) {
@@ -1331,13 +1340,13 @@ export class ComputeClient {
1331
1340
  throw new Error(message);
1332
1341
  }
1333
1342
  },
1334
- catch: (e) =>
1335
- signal?.aborted
1336
- ? new CancelledError()
1337
- : new ArtifactError({
1338
- message: e instanceof Error ? e.message : String(e),
1339
- cause: e,
1340
- }),
1343
+ catch: (e) => {
1344
+ if (signal?.aborted) return new CancelledError();
1345
+ return new ArtifactError({
1346
+ message: e instanceof Error ? e.message : String(e),
1347
+ cause: e,
1348
+ });
1349
+ },
1341
1350
  });
1342
1351
  }
1343
1352
  }
@@ -26,14 +26,15 @@ export class NextjsBuild implements BuildStrategy {
26
26
  this.#appPath = options.appPath;
27
27
  }
28
28
 
29
- async canBuild(): Promise<boolean> {
29
+ async canBuild(signal?: AbortSignal): Promise<boolean> {
30
30
  return (
31
- (await hasRootFile(this.#appPath, NEXT_CONFIG_FILENAMES)) ||
32
- (await hasPackageDependency(this.#appPath, ["next"]))
31
+ (await hasRootFile(this.#appPath, NEXT_CONFIG_FILENAMES, signal)) ||
32
+ (await hasPackageDependency(this.#appPath, ["next"], signal))
33
33
  );
34
34
  }
35
35
 
36
- async execute(): Promise<BuildArtifact> {
36
+ async execute(signal?: AbortSignal): Promise<BuildArtifact> {
37
+ signal?.throwIfAborted();
37
38
  await runPackageCli({
38
39
  appPath: this.#appPath,
39
40
  cliName: "next",
@@ -41,6 +42,7 @@ export class NextjsBuild implements BuildStrategy {
41
42
  failurePrefix: "Next.js",
42
43
  missingMessage:
43
44
  "Could not find the Next.js CLI. Install it with `npm install next` or ensure npx/bunx is available.",
45
+ signal,
44
46
  });
45
47
 
46
48
  const standaloneDir = path.join(this.#appPath, ".next", "standalone");
package/src/nuxt-build.ts CHANGED
@@ -27,14 +27,15 @@ export class NuxtBuild implements BuildStrategy {
27
27
  this.#appPath = options.appPath;
28
28
  }
29
29
 
30
- async canBuild(): Promise<boolean> {
30
+ async canBuild(signal?: AbortSignal): Promise<boolean> {
31
31
  return (
32
- (await hasRootFile(this.#appPath, NUXT_CONFIG_FILENAMES)) ||
33
- (await hasPackageDependency(this.#appPath, ["nuxt"]))
32
+ (await hasRootFile(this.#appPath, NUXT_CONFIG_FILENAMES, signal)) ||
33
+ (await hasPackageDependency(this.#appPath, ["nuxt"], signal))
34
34
  );
35
35
  }
36
36
 
37
- async execute(): Promise<BuildArtifact> {
37
+ async execute(signal?: AbortSignal): Promise<BuildArtifact> {
38
+ signal?.throwIfAborted();
38
39
  await runPackageCli({
39
40
  appPath: this.#appPath,
40
41
  cliName: "nuxt",
@@ -42,6 +43,7 @@ export class NuxtBuild implements BuildStrategy {
42
43
  failurePrefix: "Nuxt",
43
44
  missingMessage:
44
45
  "Could not find the Nuxt CLI. Install it with `npm install nuxt` or ensure npx/bunx is available.",
46
+ signal,
45
47
  });
46
48
 
47
49
  const outputDir = path.join(this.#appPath, ".output");
@@ -20,11 +20,12 @@ export class TanstackStartBuild implements BuildStrategy {
20
20
  this.#appPath = options.appPath;
21
21
  }
22
22
 
23
- async canBuild(): Promise<boolean> {
24
- return hasPackageDependency(this.#appPath, TANSTACK_START_PACKAGES);
23
+ async canBuild(signal?: AbortSignal): Promise<boolean> {
24
+ return hasPackageDependency(this.#appPath, TANSTACK_START_PACKAGES, signal);
25
25
  }
26
26
 
27
- async execute(): Promise<BuildArtifact> {
27
+ async execute(signal?: AbortSignal): Promise<BuildArtifact> {
28
+ signal?.throwIfAborted();
28
29
  await runPackageCli({
29
30
  appPath: this.#appPath,
30
31
  cliName: "vite",
@@ -32,6 +33,7 @@ export class TanstackStartBuild implements BuildStrategy {
32
33
  failurePrefix: "TanStack Start",
33
34
  missingMessage:
34
35
  "Could not find the Vite CLI. Install it with `npm install vite` or ensure npx/bunx is available.",
36
+ signal,
35
37
  });
36
38
 
37
39
  const outputDir = path.join(this.#appPath, ".output");