@prisma/compute-sdk 0.28.0 → 0.30.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 (106) hide show
  1. package/dist/api-client.d.ts +38 -38
  2. package/dist/api-client.d.ts.map +1 -1
  3. package/dist/api-client.js +44 -43
  4. package/dist/api-client.js.map +1 -1
  5. package/dist/astro-build.d.ts +2 -0
  6. package/dist/astro-build.d.ts.map +1 -1
  7. package/dist/astro-build.js +31 -39
  8. package/dist/astro-build.js.map +1 -1
  9. package/dist/build-settings.d.ts +3 -0
  10. package/dist/build-settings.d.ts.map +1 -1
  11. package/dist/build-settings.js +13 -0
  12. package/dist/build-settings.js.map +1 -1
  13. package/dist/build-strategy.d.ts +20 -0
  14. package/dist/build-strategy.d.ts.map +1 -1
  15. package/dist/build-strategy.js +33 -1
  16. package/dist/build-strategy.js.map +1 -1
  17. package/dist/build.d.ts +2 -1
  18. package/dist/build.d.ts.map +1 -1
  19. package/dist/build.js +16 -4
  20. package/dist/build.js.map +1 -1
  21. package/dist/bun-build.d.ts +1 -1
  22. package/dist/bun-build.d.ts.map +1 -1
  23. package/dist/bun-build.js +25 -9
  24. package/dist/bun-build.js.map +1 -1
  25. package/dist/callbacks.d.ts +33 -33
  26. package/dist/callbacks.d.ts.map +1 -1
  27. package/dist/compute-client.d.ts +66 -66
  28. package/dist/compute-client.d.ts.map +1 -1
  29. package/dist/compute-client.js +274 -272
  30. package/dist/compute-client.js.map +1 -1
  31. package/dist/config/frameworks.d.ts +4 -8
  32. package/dist/config/frameworks.d.ts.map +1 -1
  33. package/dist/config/frameworks.js +17 -5
  34. package/dist/config/frameworks.js.map +1 -1
  35. package/dist/config/normalize.d.ts +6 -0
  36. package/dist/config/normalize.d.ts.map +1 -1
  37. package/dist/config/normalize.js +53 -12
  38. package/dist/config/normalize.js.map +1 -1
  39. package/dist/config/types.d.ts +3 -1
  40. package/dist/config/types.d.ts.map +1 -1
  41. package/dist/config/types.js +1 -0
  42. package/dist/config/types.js.map +1 -1
  43. package/dist/configured-artifact.d.ts +20 -0
  44. package/dist/configured-artifact.d.ts.map +1 -0
  45. package/dist/configured-artifact.js +104 -0
  46. package/dist/configured-artifact.js.map +1 -0
  47. package/dist/custom-build.d.ts +20 -0
  48. package/dist/custom-build.d.ts.map +1 -0
  49. package/dist/custom-build.js +53 -0
  50. package/dist/custom-build.js.map +1 -0
  51. package/dist/errors.d.ts +28 -28
  52. package/dist/errors.d.ts.map +1 -1
  53. package/dist/errors.js +12 -12
  54. package/dist/errors.js.map +1 -1
  55. package/dist/index.d.ts +6 -5
  56. package/dist/index.d.ts.map +1 -1
  57. package/dist/index.js +2 -1
  58. package/dist/index.js.map +1 -1
  59. package/dist/log-stream.d.ts +1 -1
  60. package/dist/log-stream.d.ts.map +1 -1
  61. package/dist/log-stream.js +1 -1
  62. package/dist/log-stream.js.map +1 -1
  63. package/dist/nestjs-build.d.ts.map +1 -1
  64. package/dist/nestjs-build.js +28 -29
  65. package/dist/nestjs-build.js.map +1 -1
  66. package/dist/nextjs-build.d.ts +1 -1
  67. package/dist/nextjs-build.d.ts.map +1 -1
  68. package/dist/nextjs-build.js +25 -14
  69. package/dist/nextjs-build.js.map +1 -1
  70. package/dist/nuxt-build.d.ts +2 -0
  71. package/dist/nuxt-build.d.ts.map +1 -1
  72. package/dist/nuxt-build.js +31 -39
  73. package/dist/nuxt-build.js.map +1 -1
  74. package/dist/polling.d.ts +4 -4
  75. package/dist/polling.d.ts.map +1 -1
  76. package/dist/polling.js +15 -15
  77. package/dist/polling.js.map +1 -1
  78. package/dist/tanstack-start-build.d.ts +1 -1
  79. package/dist/tanstack-start-build.d.ts.map +1 -1
  80. package/dist/tanstack-start-build.js +26 -47
  81. package/dist/tanstack-start-build.js.map +1 -1
  82. package/dist/types.d.ts +8 -8
  83. package/dist/types.d.ts.map +1 -1
  84. package/package.json +2 -2
  85. package/src/api-client.ts +57 -63
  86. package/src/astro-build.ts +39 -46
  87. package/src/build-settings.ts +16 -0
  88. package/src/build-strategy.ts +56 -1
  89. package/src/build.ts +16 -4
  90. package/src/bun-build.ts +39 -10
  91. package/src/callbacks.ts +34 -34
  92. package/src/compute-client.ts +433 -413
  93. package/src/config/frameworks.ts +18 -5
  94. package/src/config/normalize.ts +74 -13
  95. package/src/config/types.ts +3 -0
  96. package/src/configured-artifact.ts +169 -0
  97. package/src/custom-build.ts +73 -0
  98. package/src/errors.ts +42 -38
  99. package/src/index.ts +27 -26
  100. package/src/log-stream.ts +2 -2
  101. package/src/nestjs-build.ts +36 -33
  102. package/src/nextjs-build.ts +31 -14
  103. package/src/nuxt-build.ts +39 -46
  104. package/src/polling.ts +19 -18
  105. package/src/tanstack-start-build.ts +31 -53
  106. package/src/types.ts +8 -8
package/src/api-client.ts CHANGED
@@ -54,43 +54,44 @@ export class InternalApiClient {
54
54
  ).then((r) => r.map((body) => body.data));
55
55
  }
56
56
 
57
- async listProjectServices(projectId: string, signal?: AbortSignal) {
57
+ async listProjectApps(projectId: string, signal?: AbortSignal) {
58
58
  return this.#requestPaginated(
59
59
  (cursor) =>
60
- this.#client.GET("/v1/projects/{projectId}/compute-services", {
61
- params: { path: { projectId }, query: cursor ? { cursor } : {} },
60
+ this.#client.GET("/v1/apps", {
61
+ params: {
62
+ query: cursor ? { projectId, cursor } : { projectId },
63
+ },
62
64
  signal,
63
65
  }),
64
- "/v1/projects/{projectId}/compute-services",
66
+ "/v1/apps",
65
67
  signal,
66
68
  );
67
69
  }
68
70
 
69
- async createProjectService(
71
+ async createProjectApp(
70
72
  projectId: string,
71
- body: CreateProjectServiceBody,
73
+ body: CreateProjectAppBody,
72
74
  signal?: AbortSignal,
73
75
  ) {
74
76
  signal?.throwIfAborted();
75
77
  return this.#request(
76
- this.#client.POST("/v1/projects/{projectId}/compute-services", {
77
- params: { path: { projectId } },
78
- body: body as SdkCreateProjectServiceBody,
78
+ this.#client.POST("/v1/apps", {
79
+ body: { ...body, projectId } as SdkCreateAppBody,
79
80
  // Intentionally do not pass AbortSignal for mutating operations.
80
81
  // Aborting transport can leave remote completion ambiguous.
81
82
  }),
82
- "/v1/projects/{projectId}/compute-services",
83
+ "/v1/apps",
83
84
  signal,
84
85
  ).then((r) => r.map((body) => body.data));
85
86
  }
86
87
 
87
- async getService(computeServiceId: string, signal?: AbortSignal) {
88
+ async getApp(appId: string, signal?: AbortSignal) {
88
89
  return this.#request(
89
- this.#client.GET("/v1/compute-services/{computeServiceId}", {
90
- params: { path: { computeServiceId } },
90
+ this.#client.GET("/v1/apps/{appId}", {
91
+ params: { path: { appId } },
91
92
  signal,
92
93
  }),
93
- "/v1/compute-services/{computeServiceId}",
94
+ "/v1/apps/{appId}",
94
95
  signal,
95
96
  ).then((r) => r.map((body) => body.data));
96
97
  }
@@ -106,14 +107,14 @@ export class InternalApiClient {
106
107
  ).then((r) => r.map((body) => body.data));
107
108
  }
108
109
 
109
- async deleteService(
110
- computeServiceId: string,
110
+ async deleteApp(
111
+ appId: string,
111
112
  signal?: AbortSignal,
112
113
  ): Promise<Result<void, ApiClientError>> {
113
114
  signal?.throwIfAborted();
114
115
  return this.#unwrapVoid(
115
- this.#client.DELETE("/v1/compute-services/{computeServiceId}", {
116
- params: { path: { computeServiceId } },
116
+ this.#client.DELETE("/v1/apps/{appId}", {
117
+ params: { path: { appId } },
117
118
  // Intentionally do not pass AbortSignal for mutating operations.
118
119
  // Aborting transport can leave remote completion ambiguous.
119
120
  }),
@@ -121,71 +122,71 @@ export class InternalApiClient {
121
122
  );
122
123
  }
123
124
 
124
- async listServiceVersions(computeServiceId: string, signal?: AbortSignal) {
125
+ async listAppDeployments(appId: string, signal?: AbortSignal) {
125
126
  return this.#requestPaginated(
126
127
  (cursor) =>
127
- this.#client.GET("/v1/compute-services/{computeServiceId}/versions", {
128
+ this.#client.GET("/v1/apps/{appId}/deployments", {
128
129
  params: {
129
- path: { computeServiceId },
130
+ path: { appId },
130
131
  query: cursor ? { cursor } : {},
131
132
  },
132
133
  signal,
133
134
  }),
134
- "/v1/compute-services/{computeServiceId}/versions",
135
+ "/v1/apps/{appId}/deployments",
135
136
  signal,
136
137
  );
137
138
  }
138
139
 
139
- async createServiceVersion(
140
- computeServiceId: string,
141
- body?: CreateServiceVersionBody,
140
+ async createAppDeployment(
141
+ appId: string,
142
+ body?: CreateAppDeploymentBody,
142
143
  signal?: AbortSignal,
143
144
  ) {
144
145
  signal?.throwIfAborted();
145
146
  return this.#request(
146
- this.#client.POST("/v1/compute-services/{computeServiceId}/versions", {
147
- params: { path: { computeServiceId } },
147
+ this.#client.POST("/v1/apps/{appId}/deployments", {
148
+ params: { path: { appId } },
148
149
  body,
149
150
  // Intentionally do not pass AbortSignal for mutating operations.
150
151
  // Aborting transport can leave remote completion ambiguous.
151
152
  }),
152
- "/v1/compute-services/{computeServiceId}/versions",
153
+ "/v1/apps/{appId}/deployments",
153
154
  signal,
154
155
  ).then((r) => r.map((body) => body.data));
155
156
  }
156
157
 
157
- async getVersion(versionId: string, signal?: AbortSignal) {
158
+ async getDeployment(deploymentId: string, signal?: AbortSignal) {
158
159
  return this.#request(
159
- this.#client.GET("/v1/compute-services/versions/{versionId}", {
160
- params: { path: { versionId } },
160
+ this.#client.GET("/v1/deployments/{deploymentId}", {
161
+ params: { path: { deploymentId } },
161
162
  signal,
162
163
  }),
163
- "/v1/compute-services/versions/{versionId}",
164
+ "/v1/deployments/{deploymentId}",
164
165
  signal,
165
166
  ).then((r) => r.map((body) => body.data));
166
167
  }
167
168
 
168
- async startVersion(versionId: string, signal?: AbortSignal) {
169
+ async startDeployment(deploymentId: string, signal?: AbortSignal) {
169
170
  signal?.throwIfAborted();
170
171
  return this.#request(
171
- this.#client.POST("/v1/compute-services/versions/{versionId}/start", {
172
- params: { path: { versionId } },
172
+ this.#client.POST("/v1/deployments/{deploymentId}/start", {
173
+ params: { path: { deploymentId } },
173
174
  // Intentionally do not pass AbortSignal for mutating operations.
174
175
  // Aborting transport can leave remote completion ambiguous.
175
176
  }),
176
- "/v1/compute-services/versions/{versionId}/start",
177
+ "/v1/deployments/{deploymentId}/start",
177
178
  signal,
178
179
  ).then((r) => r.map((body) => body.data));
179
180
  }
180
181
 
181
- async stopVersion(
182
- versionId: string,
182
+ async stopDeployment(
183
+ deploymentId: string,
183
184
  signal?: AbortSignal,
184
185
  ): Promise<Result<void, ApiClientError>> {
185
186
  signal?.throwIfAborted();
186
187
  return this.#unwrapVoid(
187
- this.#client.POST("/v1/compute-services/versions/{versionId}/stop", {
188
- params: { path: { versionId } },
188
+ this.#client.POST("/v1/deployments/{deploymentId}/stop", {
189
+ params: { path: { deploymentId } },
189
190
  // Intentionally do not pass AbortSignal for mutating operations.
190
191
  // Aborting transport can leave remote completion ambiguous.
191
192
  }),
@@ -193,14 +194,14 @@ export class InternalApiClient {
193
194
  );
194
195
  }
195
196
 
196
- async deleteVersion(
197
- versionId: string,
197
+ async deleteDeployment(
198
+ deploymentId: string,
198
199
  signal?: AbortSignal,
199
200
  ): Promise<Result<void, ApiClientError>> {
200
201
  signal?.throwIfAborted();
201
202
  return this.#unwrapVoid(
202
- this.#client.DELETE("/v1/compute-services/versions/{versionId}", {
203
- params: { path: { versionId } },
203
+ this.#client.DELETE("/v1/deployments/{deploymentId}", {
204
+ params: { path: { deploymentId } },
204
205
  // Intentionally do not pass AbortSignal for mutating operations.
205
206
  // Aborting transport can leave remote completion ambiguous.
206
207
  }),
@@ -208,20 +209,16 @@ export class InternalApiClient {
208
209
  );
209
210
  }
210
211
 
211
- async promoteService(
212
- computeServiceId: string,
213
- body: PromoteServiceBody,
214
- signal?: AbortSignal,
215
- ) {
212
+ async promoteApp(appId: string, body: PromoteAppBody, signal?: AbortSignal) {
216
213
  signal?.throwIfAborted();
217
214
  return this.#request(
218
- this.#client.POST("/v1/compute-services/{computeServiceId}/promote", {
219
- params: { path: { computeServiceId } },
215
+ this.#client.POST("/v1/apps/{appId}/promote", {
216
+ params: { path: { appId } },
220
217
  body,
221
218
  // Intentionally do not pass AbortSignal for mutating operations.
222
219
  // Aborting transport can leave remote completion ambiguous.
223
220
  }),
224
- "/v1/compute-services/{computeServiceId}/promote",
221
+ "/v1/apps/{appId}/promote",
225
222
  signal,
226
223
  ).then((r) => r.map((body) => body.data));
227
224
  }
@@ -380,23 +377,20 @@ type CreateProjectBody = Omit<SdkCreateProjectBody, "region"> & {
380
377
  region?: string;
381
378
  };
382
379
 
383
- type SdkCreateProjectServiceBody = NonNullable<
384
- paths["/v1/projects/{projectId}/compute-services"]["post"]["requestBody"]
380
+ type SdkCreateAppBody = NonNullable<
381
+ paths["/v1/apps"]["post"]["requestBody"]
385
382
  >["content"]["application/json"];
386
383
 
387
- type CreateProjectServiceBody = Omit<
388
- SdkCreateProjectServiceBody,
389
- "regionId"
390
- > & {
384
+ type CreateProjectAppBody = Omit<SdkCreateAppBody, "regionId" | "projectId"> & {
391
385
  regionId?: string;
392
386
  };
393
387
 
394
- export type CreateServiceVersionBody = NonNullable<
395
- paths["/v1/compute-services/{computeServiceId}/versions"]["post"]["requestBody"]
388
+ export type CreateAppDeploymentBody = NonNullable<
389
+ paths["/v1/apps/{appId}/deployments"]["post"]["requestBody"]
396
390
  >["content"]["application/json"];
397
391
 
398
- export type PromoteServiceBody = NonNullable<
399
- paths["/v1/compute-services/{computeServiceId}/promote"]["post"]["requestBody"]
392
+ export type PromoteAppBody = NonNullable<
393
+ paths["/v1/apps/{appId}/promote"]["post"]["requestBody"]
400
394
  >["content"]["application/json"];
401
395
 
402
396
  type ListEnvironmentVariablesQuery = NonNullable<
@@ -489,7 +483,7 @@ function parseRequestError(
489
483
  return parseApiError(error, response, fallbackMessage);
490
484
  }
491
485
 
492
- /** Extract the upload URL from a version creation response. */
486
+ /** Extract the upload URL from a deployment creation response. */
493
487
  export function readUploadUrl(data: {
494
488
  uploadUrl?: string | null;
495
489
  }): string | null {
@@ -1,15 +1,11 @@
1
- import { cp, mkdtemp, rm, stat } from "node:fs/promises";
2
- import os from "node:os";
3
- import path from "node:path";
4
-
5
- import { normalizeArtifactSymlinks } from "./artifact-postprocess.ts";
1
+ import { type BuildSettings, resolveBuildSettings } from "./build-settings.ts";
6
2
  import type { BuildArtifact, BuildStrategy } from "./build-strategy.ts";
7
3
  import {
8
4
  hasPackageDependency,
9
5
  hasRootFile,
10
- runPackageCli,
6
+ runBuildSettingsCommand,
11
7
  } from "./build-strategy.ts";
12
- import { defaultHttpPortForBuildType } from "./config/frameworks.ts";
8
+ import { stageConfiguredArtifact } from "./configured-artifact.ts";
13
9
  import type { BuildCommandIo } from "./workspace.ts";
14
10
 
15
11
  const ASTRO_CONFIG_FILENAMES = [
@@ -26,10 +22,16 @@ const ASTRO_CONFIG_FILENAMES = [
26
22
  */
27
23
  export class AstroBuild implements BuildStrategy {
28
24
  readonly #appPath: string;
25
+ readonly #buildSettings?: BuildSettings;
29
26
  readonly #io?: BuildCommandIo;
30
27
 
31
- constructor(options: { appPath: string; io?: BuildCommandIo }) {
28
+ constructor(options: {
29
+ appPath: string;
30
+ buildSettings?: BuildSettings;
31
+ io?: BuildCommandIo;
32
+ }) {
32
33
  this.#appPath = options.appPath;
34
+ this.#buildSettings = options.buildSettings;
33
35
  this.#io = options.io;
34
36
  }
35
37
 
@@ -42,48 +44,39 @@ export class AstroBuild implements BuildStrategy {
42
44
 
43
45
  async execute(signal?: AbortSignal): Promise<BuildArtifact> {
44
46
  signal?.throwIfAborted();
45
- await runPackageCli({
47
+ const settings =
48
+ this.#buildSettings ??
49
+ (await resolveBuildSettings({
50
+ appPath: this.#appPath,
51
+ buildType: "astro",
52
+ signal,
53
+ }));
54
+
55
+ await runBuildSettingsCommand({
46
56
  appPath: this.#appPath,
47
- cliName: "astro",
48
- args: ["build"],
57
+ settings,
49
58
  failurePrefix: "Astro",
50
- missingMessage:
51
- "Could not find the Astro CLI. Install it with `npm install astro` or ensure npx/bunx is available.",
52
- env: this.#io?.env,
53
- onOutput: this.#io?.onOutput,
59
+ defaultCli: {
60
+ source: "Astro default",
61
+ cliName: "astro",
62
+ args: ["build"],
63
+ missingMessage:
64
+ "Could not find the Astro CLI. Install it with `npm install astro` or ensure npx/bunx is available.",
65
+ },
66
+ io: this.#io,
54
67
  signal,
55
68
  });
56
69
 
57
- const distDir = path.join(this.#appPath, "dist");
58
- const entryPath = path.join(distDir, "server", "entry.mjs");
59
- const entryStat = await stat(entryPath).catch(() => null);
60
- if (!entryStat?.isFile()) {
61
- throw new Error(
62
- 'Astro build did not produce a standalone server entrypoint. Install @astrojs/node and configure it with adapter: node({ mode: "standalone" }) in your astro.config file.',
63
- );
64
- }
65
-
66
- const outDir = await mkdtemp(path.join(os.tmpdir(), "compute-build-"));
67
-
68
- try {
69
- const artifactDir = path.join(outDir, "app");
70
- await cp(distDir, artifactDir, {
71
- recursive: true,
72
- verbatimSymlinks: true,
73
- });
74
- // Materialize any symlinks into the app/workspace node_modules so the
75
- // artifact is self-contained once unpacked elsewhere.
76
- await normalizeArtifactSymlinks(artifactDir, this.#appPath, signal);
77
-
78
- return {
79
- directory: artifactDir,
80
- entrypoint: "server/entry.mjs",
81
- defaultPortMapping: { http: defaultHttpPortForBuildType("astro") },
82
- cleanup: () => rm(outDir, { recursive: true, force: true }),
83
- };
84
- } catch (error) {
85
- await rm(outDir, { recursive: true, force: true });
86
- throw error;
87
- }
70
+ return stageConfiguredArtifact({
71
+ appPath: this.#appPath,
72
+ outputDirectory: settings.outputDirectory,
73
+ entrypoint: settings.entrypoint ?? "server/entry.mjs",
74
+ buildType: "astro",
75
+ label: "Astro",
76
+ missingEntrypointMessage: settings.entrypoint
77
+ ? undefined
78
+ : 'Astro build did not produce a standalone server entrypoint. Install @astrojs/node and configure it with adapter: node({ mode: "standalone" }) in your astro.config file.',
79
+ signal,
80
+ });
88
81
  }
89
82
  }
@@ -22,6 +22,8 @@ export interface BuildSettings {
22
22
  buildCommandSource: string | null;
23
23
  outputDirectory: string;
24
24
  outputDirectorySource: string | null;
25
+ entrypoint?: string;
26
+ entrypointSource?: string | null;
25
27
  }
26
28
 
27
29
  /**
@@ -111,6 +113,13 @@ export async function resolveBuildSettings(options: {
111
113
  outputDirectorySource: "TanStack Start output",
112
114
  };
113
115
  }
116
+ case "custom":
117
+ return {
118
+ buildCommand: null,
119
+ buildCommandSource: null,
120
+ outputDirectory: ".",
121
+ outputDirectorySource: "app root",
122
+ };
114
123
  case "bun": {
115
124
  const manifest = await readPackageManifest(
116
125
  options.appPath,
@@ -143,6 +152,7 @@ export async function resolveConfiguredBuildSettings(options: {
143
152
  configured: {
144
153
  command: string | null | undefined;
145
154
  outputDirectory: string | undefined;
155
+ entrypoint?: string | undefined;
146
156
  };
147
157
  /** Label for configured values, e.g. the config file basename. */
148
158
  source: string;
@@ -169,6 +179,12 @@ export async function resolveConfiguredBuildSettings(options: {
169
179
  options.configured.outputDirectory !== undefined
170
180
  ? options.source
171
181
  : (fallback as BuildSettings).outputDirectorySource,
182
+ ...(options.configured.entrypoint !== undefined
183
+ ? {
184
+ entrypoint: options.configured.entrypoint,
185
+ entrypointSource: options.source,
186
+ }
187
+ : {}),
172
188
  };
173
189
  }
174
190
 
@@ -1,7 +1,13 @@
1
1
  import { readdir, readFile, stat } from "node:fs/promises";
2
2
  import path from "node:path";
3
+ import type { BuildSettings } from "./build-settings.ts";
3
4
  import type { PortMapping } from "./types.ts";
4
- import { buildCommandEnv, runChildProcess } from "./workspace.ts";
5
+ import {
6
+ type BuildCommandIo,
7
+ buildCommandEnv,
8
+ runBuildCommand,
9
+ runChildProcess,
10
+ } from "./workspace.ts";
5
11
 
6
12
  /**
7
13
  * The result of executing a build strategy.
@@ -207,6 +213,55 @@ export async function runPackageCli(opts: {
207
213
  throw new Error(opts.missingMessage);
208
214
  }
209
215
 
216
+ /**
217
+ * Runs the command described by build settings. A configured shell command runs
218
+ * as-is; a framework default can use the launcher ladder so local binaries,
219
+ * npx, and bunx all work consistently. `null` means "skip build".
220
+ */
221
+ export async function runBuildSettingsCommand(options: {
222
+ appPath: string;
223
+ settings: Pick<BuildSettings, "buildCommand" | "buildCommandSource">;
224
+ failurePrefix: string;
225
+ defaultCli?: {
226
+ source: string;
227
+ cliName: string;
228
+ args: string[];
229
+ missingMessage: string;
230
+ };
231
+ io?: BuildCommandIo;
232
+ signal?: AbortSignal;
233
+ }): Promise<void> {
234
+ if (!options.settings.buildCommand) {
235
+ return;
236
+ }
237
+
238
+ if (
239
+ options.defaultCli &&
240
+ options.settings.buildCommandSource === options.defaultCli.source
241
+ ) {
242
+ await runPackageCli({
243
+ appPath: options.appPath,
244
+ cliName: options.defaultCli.cliName,
245
+ args: options.defaultCli.args,
246
+ failurePrefix: options.failurePrefix,
247
+ missingMessage: options.defaultCli.missingMessage,
248
+ env: options.io?.env,
249
+ onOutput: options.io?.onOutput,
250
+ signal: options.signal,
251
+ });
252
+ return;
253
+ }
254
+
255
+ await runBuildCommand({
256
+ appPath: options.appPath,
257
+ command: options.settings.buildCommand,
258
+ failurePrefix: options.failurePrefix,
259
+ env: options.io?.env,
260
+ onOutput: options.io?.onOutput,
261
+ signal: options.signal,
262
+ });
263
+ }
264
+
210
265
  function isRecord(value: unknown): value is Record<string, unknown> {
211
266
  return typeof value === "object" && value !== null;
212
267
  }
package/src/build.ts CHANGED
@@ -3,6 +3,7 @@ import type { BuildSettings } from "./build-settings.ts";
3
3
  import type { BuildStrategy } from "./build-strategy.ts";
4
4
  import { BunBuild } from "./bun-build.ts";
5
5
  import { FRAMEWORKS, type FrameworkBuildType } from "./config/frameworks.ts";
6
+ import { CustomBuild } from "./custom-build.ts";
6
7
  import { NestjsBuild } from "./nestjs-build.ts";
7
8
  import { NextjsBuild } from "./nextjs-build.ts";
8
9
  import { NuxtBuild } from "./nuxt-build.ts";
@@ -33,12 +34,16 @@ const DETECTION_ORDER: FrameworkBuildType[] = (() => {
33
34
  const buildTypes = [
34
35
  ...new Set(FRAMEWORKS.map((framework) => framework.buildType)),
35
36
  ];
36
- return [...buildTypes.filter((type) => type !== "bun"), "bun"];
37
+ return [
38
+ ...buildTypes.filter((type) => type !== "bun" && type !== "custom"),
39
+ "bun",
40
+ ];
37
41
  })();
38
42
 
39
43
  /**
40
44
  * Constructs the build strategy for an explicit framework build type, wiring
41
- * through optional committed build settings and (for bun) the entrypoint.
45
+ * through optional committed build settings and entrypoints for strategies
46
+ * that need one.
42
47
  */
43
48
  export function createBuildStrategy(
44
49
  options: {
@@ -58,13 +63,20 @@ export function createBuildStrategy(
58
63
  requireStandalone: options.requireStandalone,
59
64
  });
60
65
  case "nuxt":
61
- return new NuxtBuild({ appPath, io });
66
+ return new NuxtBuild({ appPath, buildSettings, io });
62
67
  case "astro":
63
- return new AstroBuild({ appPath, io });
68
+ return new AstroBuild({ appPath, buildSettings, io });
64
69
  case "nestjs":
65
70
  return new NestjsBuild({ appPath, buildSettings, io });
66
71
  case "tanstack-start":
67
72
  return new TanstackStartBuild({ appPath, buildSettings, io });
73
+ case "custom":
74
+ return new CustomBuild({
75
+ appPath,
76
+ entrypoint: options.entrypoint,
77
+ buildSettings,
78
+ io,
79
+ });
68
80
  case "bun":
69
81
  return new BunBuild({
70
82
  appPath,
package/src/bun-build.ts CHANGED
@@ -9,12 +9,16 @@ import {
9
9
  import os from "node:os";
10
10
  import path from "node:path";
11
11
  import type { BuildSettings } from "./build-settings.ts";
12
- import type { BuildArtifact, BuildStrategy } from "./build-strategy.ts";
12
+ import {
13
+ type BuildArtifact,
14
+ type BuildStrategy,
15
+ runBuildSettingsCommand,
16
+ } from "./build-strategy.ts";
13
17
  import { defaultHttpPortForBuildType } from "./config/frameworks.ts";
18
+ import { stageConfiguredArtifact } from "./configured-artifact.ts";
14
19
  import {
15
20
  type BuildCommandIo,
16
21
  buildCommandEnv,
17
- runBuildCommand,
18
22
  runChildProcess,
19
23
  } from "./workspace.ts";
20
24
 
@@ -50,17 +54,36 @@ export class BunBuild implements BuildStrategy {
50
54
 
51
55
  async execute(signal?: AbortSignal): Promise<BuildArtifact> {
52
56
  signal?.throwIfAborted();
53
- if (this.#buildSettings?.buildCommand) {
54
- await runBuildCommand({
57
+ if (this.#buildSettings) {
58
+ await runBuildSettingsCommand({
55
59
  appPath: this.#appPath,
56
- command: this.#buildSettings.buildCommand,
60
+ settings: this.#buildSettings,
57
61
  failurePrefix: "Bun",
58
- env: this.#io?.env,
59
- onOutput: this.#io?.onOutput,
62
+ io: this.#io,
63
+ signal,
64
+ });
65
+ }
66
+
67
+ if (this.#buildSettings && this.#buildSettings.outputDirectory !== ".") {
68
+ if (!this.#buildSettings.entrypoint) {
69
+ throw new Error(
70
+ "Bun build entrypoint is required when outputDirectory is configured",
71
+ );
72
+ }
73
+ return stageConfiguredArtifact({
74
+ appPath: this.#appPath,
75
+ outputDirectory: this.#buildSettings.outputDirectory,
76
+ entrypoint: this.#buildSettings.entrypoint,
77
+ buildType: "bun",
78
+ label: "Bun",
60
79
  signal,
61
80
  });
62
81
  }
63
- const entrypoint = await this.#resolveEntrypoint(signal);
82
+
83
+ const entrypoint = await this.#resolveEntrypoint(
84
+ signal,
85
+ this.#buildSettings?.entrypoint,
86
+ );
64
87
 
65
88
  const outDir = await mkdtemp(path.join(os.tmpdir(), "compute-build-"));
66
89
  const bundleDir = path.join(outDir, "bundle");
@@ -94,9 +117,15 @@ export class BunBuild implements BuildStrategy {
94
117
  };
95
118
  }
96
119
 
97
- async #resolveEntrypoint(signal?: AbortSignal): Promise<string> {
120
+ async #resolveEntrypoint(
121
+ signal?: AbortSignal,
122
+ configuredEntrypoint?: string,
123
+ ): Promise<string> {
98
124
  signal?.throwIfAborted();
99
- const candidate = this.#entrypoint ?? (await this.#readPackageJsonMain());
125
+ const candidate =
126
+ this.#entrypoint ??
127
+ configuredEntrypoint ??
128
+ (await this.#readPackageJsonMain());
100
129
 
101
130
  if (!candidate) {
102
131
  throw new Error(