@prisma/compute-sdk 0.21.0 → 0.23.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/README.md +3 -1
  2. package/dist/api-client.d.ts +105 -0
  3. package/dist/api-client.d.ts.map +1 -1
  4. package/dist/api-client.js +39 -0
  5. package/dist/api-client.js.map +1 -1
  6. package/dist/compute-client.d.ts.map +1 -1
  7. package/dist/compute-client.js +69 -12
  8. package/dist/compute-client.js.map +1 -1
  9. package/dist/config/frameworks.d.ts +46 -0
  10. package/dist/config/frameworks.d.ts.map +1 -0
  11. package/dist/config/frameworks.js +129 -0
  12. package/dist/config/frameworks.js.map +1 -0
  13. package/dist/config/index.d.ts +15 -0
  14. package/dist/config/index.d.ts.map +1 -0
  15. package/dist/config/index.js +15 -0
  16. package/dist/config/index.js.map +1 -0
  17. package/dist/config/load.d.ts +48 -0
  18. package/dist/config/load.d.ts.map +1 -0
  19. package/dist/config/load.js +141 -0
  20. package/dist/config/load.js.map +1 -0
  21. package/dist/config/normalize.d.ts +77 -0
  22. package/dist/config/normalize.d.ts.map +1 -0
  23. package/dist/config/normalize.js +366 -0
  24. package/dist/config/normalize.js.map +1 -0
  25. package/dist/config/serialize.d.ts +11 -0
  26. package/dist/config/serialize.d.ts.map +1 -0
  27. package/dist/config/serialize.js +55 -0
  28. package/dist/config/serialize.js.map +1 -0
  29. package/dist/config/source-root.d.ts +10 -0
  30. package/dist/config/source-root.d.ts.map +1 -0
  31. package/dist/config/source-root.js +72 -0
  32. package/dist/config/source-root.js.map +1 -0
  33. package/dist/config/types.d.ts +67 -0
  34. package/dist/config/types.d.ts.map +1 -0
  35. package/dist/config/types.js +30 -0
  36. package/dist/config/types.js.map +1 -0
  37. package/dist/errors.d.ts +1 -1
  38. package/dist/errors.d.ts.map +1 -1
  39. package/package.json +11 -3
  40. package/src/api-client.ts +89 -0
  41. package/src/compute-client.ts +138 -13
  42. package/src/config/frameworks.ts +187 -0
  43. package/src/config/index.ts +59 -0
  44. package/src/config/load.ts +201 -0
  45. package/src/config/normalize.ts +569 -0
  46. package/src/config/serialize.ts +76 -0
  47. package/src/config/source-root.ts +91 -0
  48. package/src/config/types.ts +78 -0
  49. package/src/errors.ts +1 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/config/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,QAAQ;IACR,MAAM;IACN,OAAO;IACP,MAAM;IACN,gBAAgB;IAChB,KAAK;CACG,CAAC;AAiDX;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAqB;IACvD,OAAO,MAAM,CAAC;AAChB,CAAC"}
package/dist/errors.d.ts CHANGED
@@ -114,7 +114,7 @@ declare const LogStreamError_base: import("better-result").TaggedErrorClass<"Log
114
114
  export declare class LogStreamError extends LogStreamError_base {
115
115
  }
116
116
  export type DeployError = AuthenticationError | ApiError | MissingArgumentError | InvalidOptionsError | BuildError | ArtifactError | TimeoutError | VersionFailedError | CancelledError;
117
- export type UpdateEnvError = AuthenticationError | ApiError | MissingArgumentError | NoExistingVersionError | TimeoutError | VersionFailedError | CancelledError;
117
+ export type UpdateEnvError = AuthenticationError | ApiError | MissingArgumentError | InvalidOptionsError | NoExistingVersionError | TimeoutError | VersionFailedError | CancelledError;
118
118
  export type DestroyVersionError = CancelledError | MissingArgumentError | NoVersionsFoundError | AuthenticationError | ApiError | TimeoutError | VersionFailedError;
119
119
  export type DestroyServiceError = CancelledError | AuthenticationError | ApiError | DestroyAggregateError;
120
120
  export type PromoteError = AuthenticationError | ApiError | MissingArgumentError | NoVersionsFoundError | TimeoutError | VersionFailedError | CancelledError;
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma/compute-sdk",
3
- "version": "0.21.0",
3
+ "version": "0.23.0",
4
4
  "description": "TypeScript SDK for deploying and managing applications on Prisma Compute",
5
5
  "license": "Apache-2.0",
6
6
  "repository": "prisma/project-compute",
@@ -12,6 +12,13 @@
12
12
  "types": "./dist/index.d.ts",
13
13
  "default": "./dist/index.js"
14
14
  }
15
+ },
16
+ "./config": {
17
+ "bun": "./src/config/index.ts",
18
+ "import": {
19
+ "types": "./dist/config/index.d.ts",
20
+ "default": "./dist/config/index.js"
21
+ }
15
22
  }
16
23
  },
17
24
  "main": "./dist/index.js",
@@ -33,10 +40,11 @@
33
40
  "better-result": "^2.7.0",
34
41
  "tar-stream": "^3.1.8",
35
42
  "tiny-invariant": "1.3.3",
36
- "ws": "^8.20.0"
43
+ "ws": "^8.20.0",
44
+ "jiti": "^2.7.0"
37
45
  },
38
46
  "peerDependencies": {
39
- "@prisma/management-api-sdk": ">=1.23.0"
47
+ "@prisma/management-api-sdk": ">=1.36.0"
40
48
  },
41
49
  "devDependencies": {
42
50
  "@types/bun": "latest",
package/src/api-client.ts CHANGED
@@ -95,6 +95,17 @@ export class InternalApiClient {
95
95
  ).then((r) => r.map((body) => body.data));
96
96
  }
97
97
 
98
+ async getBranch(branchId: string, signal?: AbortSignal) {
99
+ return this.#request(
100
+ this.#client.GET("/v1/branches/{branchId}", {
101
+ params: { path: { branchId } },
102
+ signal,
103
+ }),
104
+ "/v1/branches/{branchId}",
105
+ signal,
106
+ ).then((r) => r.map((body) => body.data));
107
+ }
108
+
98
109
  async deleteService(
99
110
  computeServiceId: string,
100
111
  signal?: AbortSignal,
@@ -215,6 +226,72 @@ export class InternalApiClient {
215
226
  ).then((r) => r.map((body) => body.data));
216
227
  }
217
228
 
229
+ async listEnvironmentVariables(
230
+ query: ListEnvironmentVariablesQuery,
231
+ signal?: AbortSignal,
232
+ ) {
233
+ return this.#requestPaginated(
234
+ (cursor) =>
235
+ this.#client.GET("/v1/environment-variables", {
236
+ params: {
237
+ query: cursor ? { ...query, cursor } : query,
238
+ },
239
+ signal,
240
+ }),
241
+ "/v1/environment-variables",
242
+ signal,
243
+ );
244
+ }
245
+
246
+ async createEnvironmentVariable(
247
+ body: CreateEnvironmentVariableBody,
248
+ signal?: AbortSignal,
249
+ ) {
250
+ signal?.throwIfAborted();
251
+ return this.#request(
252
+ this.#client.POST("/v1/environment-variables", {
253
+ body,
254
+ // Intentionally do not pass AbortSignal for mutating operations.
255
+ // Aborting transport can leave remote completion ambiguous.
256
+ }),
257
+ "/v1/environment-variables",
258
+ signal,
259
+ ).then((r) => r.map((body) => body.data));
260
+ }
261
+
262
+ async updateEnvironmentVariable(
263
+ envVarId: string,
264
+ body: UpdateEnvironmentVariableBody,
265
+ signal?: AbortSignal,
266
+ ) {
267
+ signal?.throwIfAborted();
268
+ return this.#request(
269
+ this.#client.PATCH("/v1/environment-variables/{envVarId}", {
270
+ params: { path: { envVarId } },
271
+ body,
272
+ // Intentionally do not pass AbortSignal for mutating operations.
273
+ // Aborting transport can leave remote completion ambiguous.
274
+ }),
275
+ "/v1/environment-variables/{envVarId}",
276
+ signal,
277
+ ).then((r) => r.map((body) => body.data));
278
+ }
279
+
280
+ async deleteEnvironmentVariable(
281
+ envVarId: string,
282
+ signal?: AbortSignal,
283
+ ): Promise<Result<void, ApiClientError>> {
284
+ signal?.throwIfAborted();
285
+ return this.#unwrapVoid(
286
+ this.#client.DELETE("/v1/environment-variables/{envVarId}", {
287
+ params: { path: { envVarId } },
288
+ // Intentionally do not pass AbortSignal for mutating operations.
289
+ // Aborting transport can leave remote completion ambiguous.
290
+ }),
291
+ signal,
292
+ );
293
+ }
294
+
218
295
  async #request<TData>(
219
296
  request: Promise<RequestResult<TData>>,
220
297
  endpoint: string,
@@ -322,6 +399,18 @@ export type PromoteServiceBody = NonNullable<
322
399
  paths["/v1/compute-services/{computeServiceId}/promote"]["post"]["requestBody"]
323
400
  >["content"]["application/json"];
324
401
 
402
+ type ListEnvironmentVariablesQuery = NonNullable<
403
+ paths["/v1/environment-variables"]["get"]["parameters"]["query"]
404
+ >;
405
+
406
+ type CreateEnvironmentVariableBody = NonNullable<
407
+ paths["/v1/environment-variables"]["post"]["requestBody"]
408
+ >["content"]["application/json"];
409
+
410
+ type UpdateEnvironmentVariableBody = NonNullable<
411
+ paths["/v1/environment-variables/{envVarId}"]["patch"]["requestBody"]
412
+ >["content"]["application/json"];
413
+
325
414
  const TRACE_HEADER_NAMES = [
326
415
  "x-request-id",
327
416
  "x-correlation-id",
@@ -285,13 +285,19 @@ export class ComputeClient {
285
285
  ? artifact.defaultPortMapping
286
286
  : undefined);
287
287
 
288
+ yield* Result.await(
289
+ self.#applyEnvVars(
290
+ target.projectId,
291
+ target.branchId,
292
+ options.envVars,
293
+ options.signal,
294
+ ),
295
+ );
296
+
288
297
  const createResponse = yield* Result.await(
289
298
  self.#api.createServiceVersion(
290
299
  target.serviceId,
291
- {
292
- envVars: options.envVars,
293
- portMapping,
294
- },
300
+ portMapping ? { portMapping } : {},
295
301
  options.signal,
296
302
  ),
297
303
  );
@@ -413,14 +419,25 @@ export class ComputeClient {
413
419
  );
414
420
  }
415
421
 
422
+ yield* self.#checkAborted(options.signal);
423
+ yield* Result.await(
424
+ self.#applyEnvVars(
425
+ target.projectId,
426
+ target.branchId,
427
+ options.envVars,
428
+ options.signal,
429
+ ),
430
+ );
431
+
416
432
  yield* self.#checkAborted(options.signal);
417
433
 
418
434
  const createResponse = yield* Result.await(
419
435
  self.#api.createServiceVersion(
420
436
  target.serviceId,
421
437
  {
422
- envVars: options.envVars,
423
- portMapping: options.portMapping,
438
+ ...(options.portMapping !== undefined
439
+ ? { portMapping: options.portMapping }
440
+ : {}),
424
441
  skipCodeUpload: true,
425
442
  },
426
443
  options.signal,
@@ -1180,9 +1197,14 @@ export class ComputeClient {
1180
1197
  serviceId: string;
1181
1198
  serviceName: string;
1182
1199
  region: string;
1200
+ branchId: string | null;
1183
1201
  isNewService: boolean;
1184
1202
  },
1185
- MissingArgumentError | CancelledError | AuthenticationError | ApiError
1203
+ | MissingArgumentError
1204
+ | CancelledError
1205
+ | AuthenticationError
1206
+ | ApiError
1207
+ | InvalidOptionsError
1186
1208
  >
1187
1209
  > {
1188
1210
  const self = this;
@@ -1191,15 +1213,27 @@ export class ComputeClient {
1191
1213
  const service = yield* Result.await(
1192
1214
  self.#api.getService(options.serviceId, options.signal),
1193
1215
  );
1194
- const projectId = options.projectId ?? service.projectId;
1195
- if (!projectId) {
1216
+ if (!service.projectId) {
1196
1217
  return Result.err(new MissingArgumentError({ field: "projectId" }));
1197
1218
  }
1219
+ const requestedProjectId = options.projectId?.replace(/^proj_/, "");
1220
+ const serviceProjectId = service.projectId.replace(/^proj_/, "");
1221
+ if (
1222
+ requestedProjectId !== undefined &&
1223
+ requestedProjectId !== serviceProjectId
1224
+ ) {
1225
+ return Result.err(
1226
+ new InvalidOptionsError({
1227
+ message: `Service ${service.id} belongs to project ${service.projectId}, but project ${options.projectId} was provided`,
1228
+ }),
1229
+ );
1230
+ }
1198
1231
  return Result.ok({
1199
- projectId,
1232
+ projectId: service.projectId,
1200
1233
  serviceId: service.id,
1201
1234
  serviceName: service.name ?? service.id,
1202
1235
  region: service.region.id,
1236
+ branchId: service.branchId ?? null,
1203
1237
  isNewService: false,
1204
1238
  });
1205
1239
  }
@@ -1240,6 +1274,7 @@ export class ComputeClient {
1240
1274
  serviceId: response.id,
1241
1275
  serviceName: response.name,
1242
1276
  region: response.region.id,
1277
+ branchId: response.branchId ?? null,
1243
1278
  isNewService: true,
1244
1279
  });
1245
1280
  }
@@ -1264,13 +1299,20 @@ export class ComputeClient {
1264
1299
  await options.interaction.selectService(serviceInfos);
1265
1300
 
1266
1301
  if (selectedServiceId !== null) {
1267
- const service = services.find((s) => s.id === selectedServiceId);
1268
- invariant(service, "selectService returned service ID not in the list");
1302
+ const selected = services.find((s) => s.id === selectedServiceId);
1303
+ invariant(
1304
+ selected,
1305
+ "selectService returned service ID not in the list",
1306
+ );
1307
+ const service = yield* Result.await(
1308
+ self.#api.getService(selectedServiceId, options.signal),
1309
+ );
1269
1310
  return Result.ok({
1270
1311
  projectId,
1271
1312
  serviceId: selectedServiceId,
1272
- serviceName: service.name,
1313
+ serviceName: service.name ?? selected.name,
1273
1314
  region: service.region.id,
1315
+ branchId: service.branchId ?? null,
1274
1316
  isNewService: false,
1275
1317
  });
1276
1318
  }
@@ -1307,11 +1349,94 @@ export class ComputeClient {
1307
1349
  serviceId: response.id,
1308
1350
  serviceName: response.name,
1309
1351
  region: response.region.id,
1352
+ branchId: response.branchId ?? null,
1310
1353
  isNewService: true,
1311
1354
  });
1312
1355
  });
1313
1356
  }
1314
1357
 
1358
+ async #applyEnvVars(
1359
+ projectId: string,
1360
+ branchId: string | null,
1361
+ envVars: Record<string, string | null> | undefined,
1362
+ signal?: AbortSignal,
1363
+ ): Promise<Result<void, ApiRequestError>> {
1364
+ if (!envVars || Object.keys(envVars).length === 0) {
1365
+ return Result.ok(undefined);
1366
+ }
1367
+
1368
+ const self = this;
1369
+ return Result.gen(async function* () {
1370
+ const scope = yield* Result.await(
1371
+ self.#resolveEnvironmentVariableScope(projectId, branchId, signal),
1372
+ );
1373
+ for (const [key, value] of Object.entries(envVars)) {
1374
+ yield* self.#checkAborted(signal);
1375
+ const existing = yield* Result.await(
1376
+ self.#api.listEnvironmentVariables({ ...scope, key }, signal),
1377
+ );
1378
+ const current = existing[0];
1379
+
1380
+ if (value === null) {
1381
+ if (current) {
1382
+ yield* self.#checkAborted(signal);
1383
+ // Do not pass signal to mutating transport; aborting it can make remote completion ambiguous.
1384
+ yield* Result.await(
1385
+ self.#api.deleteEnvironmentVariable(current.id),
1386
+ );
1387
+ }
1388
+ continue;
1389
+ }
1390
+
1391
+ if (current) {
1392
+ yield* self.#checkAborted(signal);
1393
+ // Do not pass signal to mutating transport; aborting it can make remote completion ambiguous.
1394
+ yield* Result.await(
1395
+ self.#api.updateEnvironmentVariable(current.id, { value }),
1396
+ );
1397
+ continue;
1398
+ }
1399
+
1400
+ yield* self.#checkAborted(signal);
1401
+ // Do not pass signal to mutating transport; aborting it can make remote completion ambiguous.
1402
+ yield* Result.await(
1403
+ self.#api.createEnvironmentVariable({ ...scope, key, value }),
1404
+ );
1405
+ }
1406
+
1407
+ return Result.ok(undefined);
1408
+ });
1409
+ }
1410
+
1411
+ async #resolveEnvironmentVariableScope(
1412
+ projectId: string,
1413
+ branchId: string | null,
1414
+ signal?: AbortSignal,
1415
+ ): Promise<
1416
+ Result<
1417
+ | { projectId: string; class: "production" }
1418
+ | {
1419
+ projectId: string;
1420
+ class: "preview";
1421
+ branchId: string;
1422
+ },
1423
+ ApiRequestError
1424
+ >
1425
+ > {
1426
+ if (!branchId) {
1427
+ return Result.ok({ projectId, class: "production" });
1428
+ }
1429
+
1430
+ const branch = await this.#api.getBranch(branchId, signal);
1431
+ if (branch.isErr()) return branch;
1432
+
1433
+ if (branch.value.role === "preview") {
1434
+ return Result.ok({ projectId, class: "preview", branchId });
1435
+ }
1436
+
1437
+ return Result.ok({ projectId, class: "production" });
1438
+ }
1439
+
1315
1440
  async #uploadArtifactResult(
1316
1441
  url: string,
1317
1442
  body: Uint8Array,
@@ -0,0 +1,187 @@
1
+ import type { ComputeFramework } from "./types.ts";
2
+
3
+ /**
4
+ * Framework capability registry — the single source of truth for what each
5
+ * supported framework is and can do. Commands, config validation, detection,
6
+ * and prompts all query this table; adding a framework means adding one
7
+ * entry here plus its build/run strategy implementation.
8
+ */
9
+
10
+ export type FrameworkBuildType =
11
+ | "nextjs"
12
+ | "nuxt"
13
+ | "astro"
14
+ | "tanstack-start"
15
+ | "bun";
16
+
17
+ export interface FrameworkDescriptor {
18
+ readonly key: ComputeFramework;
19
+ readonly displayName: string;
20
+ /** Build/deploy strategy this framework uses. */
21
+ readonly buildType: FrameworkBuildType;
22
+ /** Accepted user-facing spellings, lowercased, including the key. */
23
+ readonly aliases: readonly string[];
24
+ /** Dependencies whose presence detects this framework. */
25
+ readonly detectPackages: readonly string[];
26
+ /** Config files whose presence detects this framework. */
27
+ readonly detectConfigFiles: readonly string[];
28
+ /** Consumes a user-provided source entrypoint instead of build output. */
29
+ readonly usesEntrypoint: boolean;
30
+ /** Entrypoint assumed when the package defines none. */
31
+ readonly defaultEntrypoint: string | null;
32
+ /** Has a local dev server (`app run`) in the current preview. */
33
+ readonly hasLocalDevServer: boolean;
34
+ }
35
+
36
+ export const NEXT_CONFIG_FILENAMES = [
37
+ "next.config.js",
38
+ "next.config.mjs",
39
+ "next.config.ts",
40
+ "next.config.mts",
41
+ ] as const;
42
+
43
+ export const NUXT_CONFIG_FILENAMES = [
44
+ "nuxt.config.js",
45
+ "nuxt.config.mjs",
46
+ "nuxt.config.cjs",
47
+ "nuxt.config.ts",
48
+ "nuxt.config.mts",
49
+ ] as const;
50
+
51
+ export const ASTRO_CONFIG_FILENAMES = [
52
+ "astro.config.js",
53
+ "astro.config.mjs",
54
+ "astro.config.cjs",
55
+ "astro.config.ts",
56
+ "astro.config.mts",
57
+ ] as const;
58
+
59
+ // Detection checks frameworks in this order; keep more specific signals first.
60
+ export const FRAMEWORKS: readonly FrameworkDescriptor[] = [
61
+ {
62
+ key: "nextjs",
63
+ displayName: "Next.js",
64
+ buildType: "nextjs",
65
+ aliases: ["nextjs", "next", "next.js"],
66
+ detectPackages: ["next"],
67
+ detectConfigFiles: NEXT_CONFIG_FILENAMES,
68
+ usesEntrypoint: false,
69
+ defaultEntrypoint: null,
70
+ hasLocalDevServer: true,
71
+ },
72
+ {
73
+ key: "nuxt",
74
+ displayName: "Nuxt",
75
+ buildType: "nuxt",
76
+ aliases: ["nuxt", "nuxtjs", "nuxt.js"],
77
+ detectPackages: ["nuxt"],
78
+ detectConfigFiles: NUXT_CONFIG_FILENAMES,
79
+ usesEntrypoint: false,
80
+ defaultEntrypoint: null,
81
+ hasLocalDevServer: false,
82
+ },
83
+ {
84
+ key: "astro",
85
+ displayName: "Astro",
86
+ buildType: "astro",
87
+ aliases: ["astro"],
88
+ detectPackages: ["astro"],
89
+ detectConfigFiles: ASTRO_CONFIG_FILENAMES,
90
+ usesEntrypoint: false,
91
+ defaultEntrypoint: null,
92
+ hasLocalDevServer: false,
93
+ },
94
+ {
95
+ key: "hono",
96
+ displayName: "Hono",
97
+ buildType: "bun",
98
+ aliases: ["hono"],
99
+ detectPackages: ["hono"],
100
+ detectConfigFiles: [],
101
+ usesEntrypoint: true,
102
+ defaultEntrypoint: "src/index.ts",
103
+ hasLocalDevServer: true,
104
+ },
105
+ {
106
+ key: "tanstack-start",
107
+ displayName: "TanStack Start",
108
+ buildType: "tanstack-start",
109
+ aliases: [
110
+ "tanstack-start",
111
+ "tanstack",
112
+ "@tanstack/react-start",
113
+ "@tanstack/solid-start",
114
+ ],
115
+ detectPackages: ["@tanstack/react-start", "@tanstack/solid-start"],
116
+ detectConfigFiles: [],
117
+ usesEntrypoint: false,
118
+ defaultEntrypoint: null,
119
+ hasLocalDevServer: false,
120
+ },
121
+ {
122
+ key: "bun",
123
+ displayName: "Bun",
124
+ buildType: "bun",
125
+ aliases: ["bun"],
126
+ detectPackages: [],
127
+ detectConfigFiles: [],
128
+ usesEntrypoint: true,
129
+ defaultEntrypoint: null,
130
+ hasLocalDevServer: true,
131
+ },
132
+ ];
133
+
134
+ export const FRAMEWORK_KEYS = FRAMEWORKS.map((framework) => framework.key);
135
+
136
+ /**
137
+ * Build types whose preview build consumes committed build settings. The
138
+ * others (nuxt, astro) run their framework CLI and stage fixed output, so a
139
+ * config `build` block has nothing to apply to.
140
+ */
141
+ export const CONFIG_BACKED_BUILD_TYPES = [
142
+ "nextjs",
143
+ "tanstack-start",
144
+ "bun",
145
+ ] as const satisfies readonly FrameworkBuildType[];
146
+
147
+ export type ConfigBackedBuildType = (typeof CONFIG_BACKED_BUILD_TYPES)[number];
148
+
149
+ /** Build types that consume a user-provided source entrypoint. */
150
+ export const ENTRYPOINT_BUILD_TYPES: readonly FrameworkBuildType[] = [
151
+ ...new Set(
152
+ FRAMEWORKS.filter((framework) => framework.usesEntrypoint).map(
153
+ (framework) => framework.buildType,
154
+ ),
155
+ ),
156
+ ];
157
+
158
+ /** Build types `app run` can start a local dev server for. */
159
+ export const LOCAL_DEV_BUILD_TYPES: readonly FrameworkBuildType[] = [
160
+ ...new Set(
161
+ FRAMEWORKS.filter((framework) => framework.hasLocalDevServer).map(
162
+ (framework) => framework.buildType,
163
+ ),
164
+ ),
165
+ ];
166
+
167
+ export function frameworkByKey(key: ComputeFramework): FrameworkDescriptor {
168
+ const framework = FRAMEWORKS.find((candidate) => candidate.key === key);
169
+ if (!framework) {
170
+ throw new Error(`Unknown framework key "${key}".`);
171
+ }
172
+ return framework;
173
+ }
174
+
175
+ export function frameworkFromAlias(value: string): FrameworkDescriptor | null {
176
+ const normalized = value.trim().toLowerCase();
177
+ return (
178
+ FRAMEWORKS.find((framework) => framework.aliases.includes(normalized)) ??
179
+ null
180
+ );
181
+ }
182
+
183
+ export function isConfigBackedBuildType(
184
+ value: string,
185
+ ): value is ConfigBackedBuildType {
186
+ return (CONFIG_BACKED_BUILD_TYPES as readonly string[]).includes(value);
187
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * The compute config contract and its tooling: types, validation, discovery,
3
+ * loading, and serialization for `prisma.compute.ts`.
4
+ *
5
+ * Consumers (CLI, build-runner, scaffolding) share this module so every part
6
+ * of the platform reads the same deploy graph. Consumer-specific concerns —
7
+ * project/branch resolution, prompts, flag precedence — stay with consumers.
8
+ */
9
+
10
+ export {
11
+ ASTRO_CONFIG_FILENAMES,
12
+ CONFIG_BACKED_BUILD_TYPES,
13
+ type ConfigBackedBuildType,
14
+ ENTRYPOINT_BUILD_TYPES,
15
+ FRAMEWORK_KEYS,
16
+ FRAMEWORKS,
17
+ type FrameworkBuildType,
18
+ type FrameworkDescriptor,
19
+ frameworkByKey,
20
+ frameworkFromAlias,
21
+ isConfigBackedBuildType,
22
+ LOCAL_DEV_BUILD_TYPES,
23
+ NEXT_CONFIG_FILENAMES,
24
+ NUXT_CONFIG_FILENAMES,
25
+ } from "./frameworks.ts";
26
+ export {
27
+ COMPUTE_CONFIG_FILENAME,
28
+ COMPUTE_CONFIG_FILENAMES,
29
+ ComputeConfigAmbiguousError,
30
+ type ComputeConfigError,
31
+ ComputeConfigLoadError,
32
+ findComputeConfigCandidates,
33
+ findComputeConfigDir,
34
+ loadComputeConfig,
35
+ } from "./load.ts";
36
+ export {
37
+ ComputeConfigInvalidError,
38
+ type ComputeConfigTargetError,
39
+ ComputeConfigTargetRequiredError,
40
+ ComputeConfigTargetUnknownError,
41
+ type ComputeDeployTarget,
42
+ type ComputeDeployTargetBuild,
43
+ computeTargetAppDir,
44
+ inferComputeTargetFromCwd,
45
+ type LoadedComputeConfig,
46
+ normalizeComputeConfig,
47
+ selectComputeDeployTarget,
48
+ } from "./normalize.ts";
49
+ export { serializeComputeConfig } from "./serialize.ts";
50
+ export { resolveSourceRoot, sourceRootLineage } from "./source-root.ts";
51
+ export {
52
+ COMPUTE_FRAMEWORKS,
53
+ type ComputeAppConfig,
54
+ type ComputeBuildConfig,
55
+ type ComputeConfig,
56
+ type ComputeEnvConfig,
57
+ type ComputeFramework,
58
+ defineComputeConfig,
59
+ } from "./types.ts";