@prisma/compute-sdk 0.27.0 → 0.29.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 (76) hide show
  1. package/dist/astro-build.d.ts +2 -0
  2. package/dist/astro-build.d.ts.map +1 -1
  3. package/dist/astro-build.js +31 -39
  4. package/dist/astro-build.js.map +1 -1
  5. package/dist/build-settings.d.ts +3 -0
  6. package/dist/build-settings.d.ts.map +1 -1
  7. package/dist/build-settings.js +23 -0
  8. package/dist/build-settings.js.map +1 -1
  9. package/dist/build-strategy.d.ts +20 -0
  10. package/dist/build-strategy.d.ts.map +1 -1
  11. package/dist/build-strategy.js +33 -1
  12. package/dist/build-strategy.js.map +1 -1
  13. package/dist/build.d.ts +2 -1
  14. package/dist/build.d.ts.map +1 -1
  15. package/dist/build.js +19 -4
  16. package/dist/build.js.map +1 -1
  17. package/dist/bun-build.d.ts +1 -1
  18. package/dist/bun-build.d.ts.map +1 -1
  19. package/dist/bun-build.js +25 -9
  20. package/dist/bun-build.js.map +1 -1
  21. package/dist/config/frameworks.d.ts +4 -8
  22. package/dist/config/frameworks.d.ts.map +1 -1
  23. package/dist/config/frameworks.js +29 -5
  24. package/dist/config/frameworks.js.map +1 -1
  25. package/dist/config/normalize.d.ts +6 -0
  26. package/dist/config/normalize.d.ts.map +1 -1
  27. package/dist/config/normalize.js +53 -12
  28. package/dist/config/normalize.js.map +1 -1
  29. package/dist/config/types.d.ts +3 -1
  30. package/dist/config/types.d.ts.map +1 -1
  31. package/dist/config/types.js +2 -0
  32. package/dist/config/types.js.map +1 -1
  33. package/dist/configured-artifact.d.ts +20 -0
  34. package/dist/configured-artifact.d.ts.map +1 -0
  35. package/dist/configured-artifact.js +104 -0
  36. package/dist/configured-artifact.js.map +1 -0
  37. package/dist/custom-build.d.ts +20 -0
  38. package/dist/custom-build.d.ts.map +1 -0
  39. package/dist/custom-build.js +53 -0
  40. package/dist/custom-build.js.map +1 -0
  41. package/dist/index.d.ts +2 -0
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +2 -0
  44. package/dist/index.js.map +1 -1
  45. package/dist/nestjs-build.d.ts +23 -0
  46. package/dist/nestjs-build.d.ts.map +1 -0
  47. package/dist/nestjs-build.js +316 -0
  48. package/dist/nestjs-build.js.map +1 -0
  49. package/dist/nextjs-build.d.ts +1 -1
  50. package/dist/nextjs-build.d.ts.map +1 -1
  51. package/dist/nextjs-build.js +25 -14
  52. package/dist/nextjs-build.js.map +1 -1
  53. package/dist/nuxt-build.d.ts +2 -0
  54. package/dist/nuxt-build.d.ts.map +1 -1
  55. package/dist/nuxt-build.js +31 -39
  56. package/dist/nuxt-build.js.map +1 -1
  57. package/dist/tanstack-start-build.d.ts +1 -1
  58. package/dist/tanstack-start-build.d.ts.map +1 -1
  59. package/dist/tanstack-start-build.js +26 -47
  60. package/dist/tanstack-start-build.js.map +1 -1
  61. package/package.json +2 -1
  62. package/src/astro-build.ts +39 -46
  63. package/src/build-settings.ts +34 -0
  64. package/src/build-strategy.ts +56 -1
  65. package/src/build.ts +19 -4
  66. package/src/bun-build.ts +39 -10
  67. package/src/config/frameworks.ts +31 -5
  68. package/src/config/normalize.ts +74 -13
  69. package/src/config/types.ts +4 -0
  70. package/src/configured-artifact.ts +169 -0
  71. package/src/custom-build.ts +73 -0
  72. package/src/index.ts +2 -0
  73. package/src/nestjs-build.ts +428 -0
  74. package/src/nextjs-build.ts +31 -14
  75. package/src/nuxt-build.ts +39 -46
  76. package/src/tanstack-start-build.ts +31 -53
@@ -2,7 +2,7 @@ import path from "node:path";
2
2
 
3
3
  import { Result, TaggedError } from "better-result";
4
4
 
5
- import { frameworkByKey, isConfigBackedBuildType } from "./frameworks.ts";
5
+ import { frameworkByKey } from "./frameworks.ts";
6
6
  import { COMPUTE_FRAMEWORKS, type ComputeFramework } from "./types.ts";
7
7
 
8
8
  export interface ComputeDeployTargetBuild {
@@ -10,6 +10,11 @@ export interface ComputeDeployTargetBuild {
10
10
  command: string | null | undefined;
11
11
  /** Normalized output path relative to the app root, undefined when not configured. */
12
12
  outputDirectory: string | undefined;
13
+ /**
14
+ * Normalized configured entrypoint. With outputDirectory it is relative to
15
+ * that output directory; otherwise it is relative to the app root.
16
+ */
17
+ entrypoint: string | undefined;
13
18
  }
14
19
 
15
20
  export interface ComputeDeployTarget {
@@ -19,6 +24,7 @@ export interface ComputeDeployTarget {
19
24
  /** Normalized app directory relative to the config file, or null for the config directory. */
20
25
  root: string | null;
21
26
  framework: ComputeFramework | null;
27
+ /** Runtime/source entrypoint normalized from `entry` or an entrypoint-backed `build.entrypoint`. */
22
28
  entry: string | null;
23
29
  httpPort: number | null;
24
30
  /** Env inputs in deploy order: dotenv file paths first, then NAME=VALUE assignments. */
@@ -104,7 +110,7 @@ const KNOWN_APP_KEYS = [
104
110
  "build",
105
111
  ] as const;
106
112
  const KNOWN_ENV_KEYS = ["file", "vars"] as const;
107
- const KNOWN_BUILD_KEYS = ["command", "outputDirectory"] as const;
113
+ const KNOWN_BUILD_KEYS = ["command", "outputDirectory", "entrypoint"] as const;
108
114
 
109
115
  /**
110
116
  * Validates and normalizes a config module's default export. Reports every
@@ -343,7 +349,8 @@ function normalizeAppEntry(
343
349
  }
344
350
  }
345
351
 
346
- if (entry && framework && !frameworkByKey(framework).usesEntrypoint) {
352
+ const frameworkDescriptor = framework ? frameworkByKey(framework) : null;
353
+ if (entry && frameworkDescriptor && !frameworkDescriptor.usesEntrypoint) {
347
354
  issues.push(
348
355
  `\`${label}.entry\` is not supported with the ${framework} framework; it derives its entrypoint from build output.`,
349
356
  );
@@ -367,22 +374,53 @@ function normalizeAppEntry(
367
374
 
368
375
  const envInputs = normalizeEnvConfig(value.env, `${label}.env`, issues);
369
376
  const build = normalizeBuildConfig(value.build, `${label}.build`, issues);
377
+ if (framework === "custom") {
378
+ if (!build) {
379
+ issues.push(
380
+ `\`${label}.build\` is required with the custom framework and must set \`outputDirectory\` and \`entrypoint\`.`,
381
+ );
382
+ } else {
383
+ if (!build.outputDirectory) {
384
+ issues.push(
385
+ `\`${label}.build.outputDirectory\` is required with the custom framework.`,
386
+ );
387
+ }
388
+ if (!build.entrypoint) {
389
+ issues.push(
390
+ `\`${label}.build.entrypoint\` is required with the custom framework.`,
391
+ );
392
+ }
393
+ }
394
+ } else if (
395
+ build?.outputDirectory &&
396
+ build.outputDirectory !== "." &&
397
+ !build.entrypoint &&
398
+ frameworkDescriptor?.usesEntrypoint
399
+ ) {
400
+ issues.push(
401
+ `\`${label}.build.entrypoint\` is required when \`${label}.build.outputDirectory\` is set with the ${framework} framework.`,
402
+ );
403
+ }
370
404
  if (
371
- build &&
372
- framework &&
373
- !isConfigBackedBuildType(frameworkByKey(framework).buildType)
405
+ entry &&
406
+ build?.entrypoint &&
407
+ frameworkDescriptor?.usesEntrypoint &&
408
+ entry !== build.entrypoint
374
409
  ) {
375
410
  issues.push(
376
- `\`${label}.build\` is not supported with the ${framework} framework; its build runs automatically during deploy.`,
411
+ `\`${label}.entry\` and \`${label}.build.entrypoint\` both configure the runtime entrypoint; set only one, or make them match.`,
377
412
  );
378
413
  }
379
-
380
414
  return {
381
415
  key,
382
416
  name,
383
417
  root,
384
418
  framework,
385
- entry,
419
+ entry:
420
+ entry ??
421
+ (frameworkDescriptor?.usesEntrypoint || !frameworkDescriptor
422
+ ? (build?.entrypoint ?? null)
423
+ : null),
386
424
  httpPort,
387
425
  envInputs,
388
426
  build,
@@ -400,7 +438,7 @@ function normalizeBuildConfig(
400
438
 
401
439
  if (!isPlainObject(value)) {
402
440
  issues.push(
403
- `\`${label}\` must be an object with \`command\` and/or \`outputDirectory\`.`,
441
+ `\`${label}\` must be an object with \`command\`, \`outputDirectory\`, and/or \`entrypoint\`.`,
404
442
  );
405
443
  return null;
406
444
  }
@@ -444,14 +482,37 @@ function normalizeBuildConfig(
444
482
  }
445
483
  }
446
484
 
447
- if (command === undefined && outputDirectory === undefined) {
485
+ let entrypoint: string | undefined;
486
+ if (value.entrypoint !== undefined) {
487
+ const normalized =
488
+ typeof value.entrypoint === "string"
489
+ ? normalizeRelativePath(value.entrypoint)
490
+ : undefined;
491
+ if (!normalized) {
492
+ issues.push(
493
+ `\`${label}.entrypoint\` must be a relative path that stays inside its base directory.`,
494
+ );
495
+ } else if (normalized === ".") {
496
+ issues.push(
497
+ `\`${label}.entrypoint\` must be a relative file path inside its base directory.`,
498
+ );
499
+ } else {
500
+ entrypoint = normalized;
501
+ }
502
+ }
503
+
504
+ if (
505
+ command === undefined &&
506
+ outputDirectory === undefined &&
507
+ entrypoint === undefined
508
+ ) {
448
509
  issues.push(
449
- `\`${label}\` must set \`command\` and/or \`outputDirectory\`.`,
510
+ `\`${label}\` must set \`command\`, \`outputDirectory\`, and/or \`entrypoint\`.`,
450
511
  );
451
512
  return null;
452
513
  }
453
514
 
454
- return { command, outputDirectory };
515
+ return { command, outputDirectory, entrypoint };
455
516
  }
456
517
 
457
518
  function normalizeEnvConfig(
@@ -11,7 +11,9 @@ export const COMPUTE_FRAMEWORKS = [
11
11
  "nuxt",
12
12
  "astro",
13
13
  "hono",
14
+ "nestjs",
14
15
  "tanstack-start",
16
+ "custom",
15
17
  "bun",
16
18
  ] as const;
17
19
 
@@ -33,6 +35,8 @@ export interface ComputeBuildConfig {
33
35
  command?: string | null;
34
36
  /** Framework output path relative to the app root, e.g. ".next/standalone". */
35
37
  outputDirectory?: string;
38
+ /** Entrypoint for the built artifact, relative to `outputDirectory` when one is set. */
39
+ entrypoint?: string;
36
40
  }
37
41
 
38
42
  export interface ComputeAppConfig {
@@ -0,0 +1,169 @@
1
+ import { cp, mkdtemp, realpath, 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";
6
+ import type { BuildArtifact } from "./build-strategy.ts";
7
+ import {
8
+ defaultHttpPortForBuildType,
9
+ type FrameworkBuildType,
10
+ } from "./config/frameworks.ts";
11
+
12
+ export async function stageConfiguredArtifact(options: {
13
+ appPath: string;
14
+ outputDirectory: string;
15
+ entrypoint: string;
16
+ buildType: FrameworkBuildType;
17
+ label: string;
18
+ missingEntrypointMessage?: string;
19
+ signal?: AbortSignal;
20
+ }): Promise<BuildArtifact> {
21
+ options.signal?.throwIfAborted();
22
+ const outputDir = resolvePathInside(
23
+ options.appPath,
24
+ options.outputDirectory,
25
+ `${options.label} build output directory must be a relative path inside the app directory`,
26
+ );
27
+ const outputStat = await stat(outputDir).catch(() => null);
28
+ if (!outputStat?.isDirectory()) {
29
+ throw new Error(
30
+ `${options.label} build output directory does not exist: ${options.outputDirectory}`,
31
+ );
32
+ }
33
+
34
+ const [realAppPath, realOutputDir] = await Promise.all([
35
+ realpath(path.resolve(options.appPath)),
36
+ realpath(outputDir),
37
+ ]);
38
+ if (!isPathWithin(realAppPath, realOutputDir)) {
39
+ throw new Error(
40
+ `${options.label} build output directory must stay inside the app directory`,
41
+ );
42
+ }
43
+
44
+ const entrypoint = await resolveConfiguredArtifactEntrypoint({
45
+ directory: realOutputDir,
46
+ entrypoint: options.entrypoint,
47
+ label: options.label,
48
+ displayDirectory: options.outputDirectory,
49
+ missingEntrypointMessage: options.missingEntrypointMessage,
50
+ });
51
+
52
+ const outDir = await mkdtemp(path.join(os.tmpdir(), "compute-build-"));
53
+ try {
54
+ const artifactDir = path.join(outDir, "app");
55
+ await cp(realOutputDir, artifactDir, {
56
+ recursive: true,
57
+ verbatimSymlinks: true,
58
+ });
59
+ await normalizeArtifactSymlinks(
60
+ artifactDir,
61
+ options.appPath,
62
+ options.signal,
63
+ );
64
+
65
+ return {
66
+ directory: artifactDir,
67
+ entrypoint,
68
+ defaultPortMapping: {
69
+ http: defaultHttpPortForBuildType(options.buildType),
70
+ },
71
+ cleanup: () => rm(outDir, { recursive: true, force: true }),
72
+ };
73
+ } catch (error) {
74
+ await rm(outDir, { recursive: true, force: true });
75
+ throw error;
76
+ }
77
+ }
78
+
79
+ export async function resolveConfiguredArtifactEntrypoint(options: {
80
+ directory: string;
81
+ entrypoint: string;
82
+ label: string;
83
+ displayDirectory?: string;
84
+ missingEntrypointMessage?: string;
85
+ }): Promise<string> {
86
+ const entrypoint = normalizeConfiguredArtifactEntrypoint(
87
+ options.entrypoint,
88
+ options.label,
89
+ );
90
+ const entryPath = path.join(options.directory, entrypoint);
91
+ const entryStat = await stat(entryPath).catch(() => null);
92
+ if (!entryStat?.isFile()) {
93
+ if (options.missingEntrypointMessage) {
94
+ throw new Error(options.missingEntrypointMessage);
95
+ }
96
+ const directory = options.displayDirectory ?? options.directory;
97
+ throw new Error(
98
+ `${options.label} build entrypoint does not exist: ${directory}/${entrypoint}`,
99
+ );
100
+ }
101
+ return entrypoint;
102
+ }
103
+
104
+ export function normalizeConfiguredArtifactEntrypoint(
105
+ entrypoint: string,
106
+ label: string,
107
+ ): string {
108
+ const normalized = path.posix.normalize(
109
+ entrypoint.trim().replace(/\\/g, "/"),
110
+ );
111
+ if (
112
+ normalized.length === 0 ||
113
+ normalized === "." ||
114
+ normalized === ".." ||
115
+ normalized.startsWith("../") ||
116
+ normalized.includes("/../") ||
117
+ path.win32.isAbsolute(entrypoint) ||
118
+ path.posix.isAbsolute(normalized) ||
119
+ /^[A-Za-z]:/.test(normalized)
120
+ ) {
121
+ throw new Error(
122
+ `${label} build entrypoint must be a relative path inside the output directory`,
123
+ );
124
+ }
125
+
126
+ return normalized;
127
+ }
128
+
129
+ function resolvePathInside(
130
+ root: string,
131
+ relativePath: string,
132
+ errorMessage: string,
133
+ ): string {
134
+ const raw = relativePath.trim().replace(/\\/g, "/");
135
+ const normalized = path.posix.normalize(raw);
136
+ if (
137
+ raw.length === 0 ||
138
+ normalized === ".." ||
139
+ normalized.startsWith("../") ||
140
+ normalized.includes("/../") ||
141
+ path.win32.isAbsolute(relativePath) ||
142
+ path.posix.isAbsolute(normalized) ||
143
+ /^[A-Za-z]:/.test(normalized)
144
+ ) {
145
+ throw new Error(errorMessage);
146
+ }
147
+
148
+ const resolved = path.resolve(root, normalized);
149
+ const relative = path.relative(root, resolved);
150
+ if (
151
+ relative === ".." ||
152
+ relative.startsWith(`..${path.sep}`) ||
153
+ path.isAbsolute(relative)
154
+ ) {
155
+ throw new Error(errorMessage);
156
+ }
157
+
158
+ return resolved;
159
+ }
160
+
161
+ function isPathWithin(root: string, candidate: string): boolean {
162
+ const relative = path.relative(root, candidate);
163
+ return (
164
+ relative === "" ||
165
+ (!relative.startsWith(`..${path.sep}`) &&
166
+ relative !== ".." &&
167
+ !path.isAbsolute(relative))
168
+ );
169
+ }
@@ -0,0 +1,73 @@
1
+ import { type BuildSettings, resolveBuildSettings } from "./build-settings.ts";
2
+ import {
3
+ type BuildArtifact,
4
+ type BuildStrategy,
5
+ runBuildSettingsCommand,
6
+ } from "./build-strategy.ts";
7
+ import {
8
+ normalizeConfiguredArtifactEntrypoint,
9
+ stageConfiguredArtifact,
10
+ } from "./configured-artifact.ts";
11
+ import type { BuildCommandIo } from "./workspace.ts";
12
+
13
+ /**
14
+ * Generic artifact strategy for frameworks the SDK does not know about yet.
15
+ * Runs an optional command, stages a configured output directory as-is, and
16
+ * starts the configured entrypoint from inside that artifact.
17
+ */
18
+ export class CustomBuild implements BuildStrategy {
19
+ readonly #appPath: string;
20
+ readonly #entrypoint?: string;
21
+ readonly #buildSettings?: BuildSettings;
22
+ readonly #io?: BuildCommandIo;
23
+
24
+ constructor(options: {
25
+ appPath: string;
26
+ entrypoint?: string;
27
+ buildSettings?: BuildSettings;
28
+ io?: BuildCommandIo;
29
+ }) {
30
+ this.#appPath = options.appPath;
31
+ this.#entrypoint = options.entrypoint;
32
+ this.#buildSettings = options.buildSettings;
33
+ this.#io = options.io;
34
+ }
35
+
36
+ async canBuild(_signal?: AbortSignal): Promise<boolean> {
37
+ return Boolean(this.#entrypoint ?? this.#buildSettings?.entrypoint);
38
+ }
39
+
40
+ async execute(signal?: AbortSignal): Promise<BuildArtifact> {
41
+ signal?.throwIfAborted();
42
+ const settings =
43
+ this.#buildSettings ??
44
+ (await resolveBuildSettings({
45
+ appPath: this.#appPath,
46
+ buildType: "custom",
47
+ signal,
48
+ }));
49
+
50
+ const entrypoint = this.#entrypoint ?? settings.entrypoint;
51
+ if (!entrypoint) {
52
+ throw new Error("Custom build entrypoint is required");
53
+ }
54
+ normalizeConfiguredArtifactEntrypoint(entrypoint, "Custom");
55
+
56
+ await runBuildSettingsCommand({
57
+ appPath: this.#appPath,
58
+ settings,
59
+ failurePrefix: "Custom",
60
+ io: this.#io,
61
+ signal,
62
+ });
63
+
64
+ return stageConfiguredArtifact({
65
+ appPath: this.#appPath,
66
+ outputDirectory: settings.outputDirectory,
67
+ entrypoint,
68
+ buildType: "custom",
69
+ label: "Custom",
70
+ signal,
71
+ });
72
+ }
73
+ }
package/src/index.ts CHANGED
@@ -70,6 +70,7 @@ export type {
70
70
  UpdateEnvResult,
71
71
  } from "./compute-client.ts";
72
72
  export { ComputeClient } from "./compute-client.ts";
73
+ export { CustomBuild } from "./custom-build.ts";
73
74
  export type {
74
75
  DetectedSchema,
75
76
  MigrationCommand,
@@ -112,6 +113,7 @@ export type {
112
113
  TerminalRecord,
113
114
  } from "./log-stream.ts";
114
115
  export { streamLogs } from "./log-stream.ts";
116
+ export { NestjsBuild } from "./nestjs-build.ts";
115
117
  export { NextjsBuild } from "./nextjs-build.ts";
116
118
  export { NuxtBuild } from "./nuxt-build.ts";
117
119
  export { TanstackStartBuild } from "./tanstack-start-build.ts";