@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.
- package/dist/api-client.d.ts +38 -38
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +44 -43
- package/dist/api-client.js.map +1 -1
- package/dist/astro-build.d.ts +2 -0
- package/dist/astro-build.d.ts.map +1 -1
- package/dist/astro-build.js +31 -39
- package/dist/astro-build.js.map +1 -1
- package/dist/build-settings.d.ts +3 -0
- package/dist/build-settings.d.ts.map +1 -1
- package/dist/build-settings.js +13 -0
- package/dist/build-settings.js.map +1 -1
- package/dist/build-strategy.d.ts +20 -0
- package/dist/build-strategy.d.ts.map +1 -1
- package/dist/build-strategy.js +33 -1
- package/dist/build-strategy.js.map +1 -1
- package/dist/build.d.ts +2 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +16 -4
- package/dist/build.js.map +1 -1
- package/dist/bun-build.d.ts +1 -1
- package/dist/bun-build.d.ts.map +1 -1
- package/dist/bun-build.js +25 -9
- package/dist/bun-build.js.map +1 -1
- package/dist/callbacks.d.ts +33 -33
- package/dist/callbacks.d.ts.map +1 -1
- package/dist/compute-client.d.ts +66 -66
- package/dist/compute-client.d.ts.map +1 -1
- package/dist/compute-client.js +274 -272
- package/dist/compute-client.js.map +1 -1
- package/dist/config/frameworks.d.ts +4 -8
- package/dist/config/frameworks.d.ts.map +1 -1
- package/dist/config/frameworks.js +17 -5
- package/dist/config/frameworks.js.map +1 -1
- package/dist/config/normalize.d.ts +6 -0
- package/dist/config/normalize.d.ts.map +1 -1
- package/dist/config/normalize.js +53 -12
- package/dist/config/normalize.js.map +1 -1
- package/dist/config/types.d.ts +3 -1
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +1 -0
- package/dist/config/types.js.map +1 -1
- package/dist/configured-artifact.d.ts +20 -0
- package/dist/configured-artifact.d.ts.map +1 -0
- package/dist/configured-artifact.js +104 -0
- package/dist/configured-artifact.js.map +1 -0
- package/dist/custom-build.d.ts +20 -0
- package/dist/custom-build.d.ts.map +1 -0
- package/dist/custom-build.js +53 -0
- package/dist/custom-build.js.map +1 -0
- package/dist/errors.d.ts +28 -28
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +12 -12
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/log-stream.d.ts +1 -1
- package/dist/log-stream.d.ts.map +1 -1
- package/dist/log-stream.js +1 -1
- package/dist/log-stream.js.map +1 -1
- package/dist/nestjs-build.d.ts.map +1 -1
- package/dist/nestjs-build.js +28 -29
- package/dist/nestjs-build.js.map +1 -1
- package/dist/nextjs-build.d.ts +1 -1
- package/dist/nextjs-build.d.ts.map +1 -1
- package/dist/nextjs-build.js +25 -14
- package/dist/nextjs-build.js.map +1 -1
- package/dist/nuxt-build.d.ts +2 -0
- package/dist/nuxt-build.d.ts.map +1 -1
- package/dist/nuxt-build.js +31 -39
- package/dist/nuxt-build.js.map +1 -1
- package/dist/polling.d.ts +4 -4
- package/dist/polling.d.ts.map +1 -1
- package/dist/polling.js +15 -15
- package/dist/polling.js.map +1 -1
- package/dist/tanstack-start-build.d.ts +1 -1
- package/dist/tanstack-start-build.d.ts.map +1 -1
- package/dist/tanstack-start-build.js +26 -47
- package/dist/tanstack-start-build.js.map +1 -1
- package/dist/types.d.ts +8 -8
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/api-client.ts +57 -63
- package/src/astro-build.ts +39 -46
- package/src/build-settings.ts +16 -0
- package/src/build-strategy.ts +56 -1
- package/src/build.ts +16 -4
- package/src/bun-build.ts +39 -10
- package/src/callbacks.ts +34 -34
- package/src/compute-client.ts +433 -413
- package/src/config/frameworks.ts +18 -5
- package/src/config/normalize.ts +74 -13
- package/src/config/types.ts +3 -0
- package/src/configured-artifact.ts +169 -0
- package/src/custom-build.ts +73 -0
- package/src/errors.ts +42 -38
- package/src/index.ts +27 -26
- package/src/log-stream.ts +2 -2
- package/src/nestjs-build.ts +36 -33
- package/src/nextjs-build.ts +31 -14
- package/src/nuxt-build.ts +39 -46
- package/src/polling.ts +19 -18
- package/src/tanstack-start-build.ts +31 -53
- package/src/types.ts +8 -8
package/src/config/frameworks.ts
CHANGED
|
@@ -13,6 +13,7 @@ export type FrameworkBuildType =
|
|
|
13
13
|
| "astro"
|
|
14
14
|
| "nestjs"
|
|
15
15
|
| "tanstack-start"
|
|
16
|
+
| "custom"
|
|
16
17
|
| "bun";
|
|
17
18
|
|
|
18
19
|
export interface FrameworkDescriptor {
|
|
@@ -138,6 +139,18 @@ export const FRAMEWORKS: readonly FrameworkDescriptor[] = [
|
|
|
138
139
|
hasLocalDevServer: false,
|
|
139
140
|
defaultHttpPort: 3000,
|
|
140
141
|
},
|
|
142
|
+
{
|
|
143
|
+
key: "custom",
|
|
144
|
+
displayName: "Custom",
|
|
145
|
+
buildType: "custom",
|
|
146
|
+
aliases: ["custom"],
|
|
147
|
+
detectPackages: [],
|
|
148
|
+
detectConfigFiles: [],
|
|
149
|
+
usesEntrypoint: false,
|
|
150
|
+
defaultEntrypoint: null,
|
|
151
|
+
hasLocalDevServer: false,
|
|
152
|
+
defaultHttpPort: 3000,
|
|
153
|
+
},
|
|
141
154
|
{
|
|
142
155
|
key: "bun",
|
|
143
156
|
displayName: "Bun",
|
|
@@ -154,14 +167,14 @@ export const FRAMEWORKS: readonly FrameworkDescriptor[] = [
|
|
|
154
167
|
|
|
155
168
|
export const FRAMEWORK_KEYS = FRAMEWORKS.map((framework) => framework.key);
|
|
156
169
|
|
|
157
|
-
/**
|
|
158
|
-
* Build types whose preview build consumes committed build settings. The
|
|
159
|
-
* others (nuxt, astro) run their framework CLI and stage fixed output, so a
|
|
160
|
-
* config `build` block has nothing to apply to.
|
|
161
|
-
*/
|
|
170
|
+
/** Build types whose preview build consumes committed build settings. */
|
|
162
171
|
export const CONFIG_BACKED_BUILD_TYPES = [
|
|
163
172
|
"nextjs",
|
|
173
|
+
"nuxt",
|
|
174
|
+
"astro",
|
|
175
|
+
"nestjs",
|
|
164
176
|
"tanstack-start",
|
|
177
|
+
"custom",
|
|
165
178
|
"bun",
|
|
166
179
|
] as const satisfies readonly FrameworkBuildType[];
|
|
167
180
|
|
package/src/config/normalize.ts
CHANGED
|
@@ -2,7 +2,7 @@ import path from "node:path";
|
|
|
2
2
|
|
|
3
3
|
import { Result, TaggedError } from "better-result";
|
|
4
4
|
|
|
5
|
-
import { frameworkByKey
|
|
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
|
-
|
|
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
|
-
|
|
372
|
-
|
|
373
|
-
|
|
405
|
+
entry &&
|
|
406
|
+
build?.entrypoint &&
|
|
407
|
+
frameworkDescriptor?.usesEntrypoint &&
|
|
408
|
+
entry !== build.entrypoint
|
|
374
409
|
) {
|
|
375
410
|
issues.push(
|
|
376
|
-
`\`${label}.
|
|
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 \`
|
|
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
|
-
|
|
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 \`
|
|
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(
|
package/src/config/types.ts
CHANGED
|
@@ -13,6 +13,7 @@ export const COMPUTE_FRAMEWORKS = [
|
|
|
13
13
|
"hono",
|
|
14
14
|
"nestjs",
|
|
15
15
|
"tanstack-start",
|
|
16
|
+
"custom",
|
|
16
17
|
"bun",
|
|
17
18
|
] as const;
|
|
18
19
|
|
|
@@ -34,6 +35,8 @@ export interface ComputeBuildConfig {
|
|
|
34
35
|
command?: string | null;
|
|
35
36
|
/** Framework output path relative to the app root, e.g. ".next/standalone". */
|
|
36
37
|
outputDirectory?: string;
|
|
38
|
+
/** Entrypoint for the built artifact, relative to `outputDirectory` when one is set. */
|
|
39
|
+
entrypoint?: string;
|
|
37
40
|
}
|
|
38
41
|
|
|
39
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/errors.ts
CHANGED
|
@@ -37,57 +37,61 @@ export class ArtifactError extends TaggedError("ArtifactError")<{
|
|
|
37
37
|
}>() {}
|
|
38
38
|
|
|
39
39
|
export class TimeoutError extends TaggedError("TimeoutError")<{
|
|
40
|
-
|
|
40
|
+
deploymentId: string;
|
|
41
41
|
elapsedMs: number;
|
|
42
42
|
lastStatus: string;
|
|
43
43
|
message: string;
|
|
44
44
|
}>() {
|
|
45
45
|
constructor(args: {
|
|
46
|
-
|
|
46
|
+
deploymentId: string;
|
|
47
47
|
elapsedMs: number;
|
|
48
48
|
lastStatus: string;
|
|
49
49
|
}) {
|
|
50
50
|
super({
|
|
51
51
|
...args,
|
|
52
|
-
message: `Timed out waiting for
|
|
52
|
+
message: `Timed out waiting for deployment ${args.deploymentId} to reach target state (last status: ${args.lastStatus}, elapsed: ${Math.round(args.elapsedMs / 1000)}s)`,
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
export class
|
|
58
|
-
|
|
57
|
+
export class DeploymentFailedError extends TaggedError(
|
|
58
|
+
"DeploymentFailedError",
|
|
59
|
+
)<{
|
|
60
|
+
deploymentId: string;
|
|
59
61
|
message: string;
|
|
60
62
|
}>() {
|
|
61
|
-
constructor(args: {
|
|
63
|
+
constructor(args: { deploymentId: string }) {
|
|
62
64
|
super({
|
|
63
|
-
|
|
64
|
-
message: `
|
|
65
|
+
deploymentId: args.deploymentId,
|
|
66
|
+
message: `Deployment ${args.deploymentId} transitioned to failed status`,
|
|
65
67
|
});
|
|
66
68
|
}
|
|
67
69
|
}
|
|
68
70
|
|
|
69
|
-
export class
|
|
70
|
-
"
|
|
71
|
+
export class NoExistingDeploymentError extends TaggedError(
|
|
72
|
+
"NoExistingDeploymentError",
|
|
71
73
|
)<{
|
|
72
|
-
|
|
74
|
+
appId: string;
|
|
73
75
|
message: string;
|
|
74
76
|
}>() {
|
|
75
|
-
constructor(args: {
|
|
77
|
+
constructor(args: { appId: string }) {
|
|
76
78
|
super({
|
|
77
|
-
|
|
78
|
-
message: `
|
|
79
|
+
appId: args.appId,
|
|
80
|
+
message: `App ${args.appId} has no existing deployments. Environment-only updates require at least one prior deployment.`,
|
|
79
81
|
});
|
|
80
82
|
}
|
|
81
83
|
}
|
|
82
84
|
|
|
83
|
-
export class
|
|
84
|
-
|
|
85
|
+
export class NoDeploymentsFoundError extends TaggedError(
|
|
86
|
+
"NoDeploymentsFoundError",
|
|
87
|
+
)<{
|
|
88
|
+
appId: string;
|
|
85
89
|
message: string;
|
|
86
90
|
}>() {
|
|
87
|
-
constructor(args: {
|
|
91
|
+
constructor(args: { appId: string }) {
|
|
88
92
|
super({
|
|
89
|
-
|
|
90
|
-
message: `
|
|
93
|
+
appId: args.appId,
|
|
94
|
+
message: `App ${args.appId} has no deployments`,
|
|
91
95
|
});
|
|
92
96
|
}
|
|
93
97
|
}
|
|
@@ -104,19 +108,19 @@ export class DestroyAggregateError extends TaggedError(
|
|
|
104
108
|
"DestroyAggregateError",
|
|
105
109
|
)<{
|
|
106
110
|
message: string;
|
|
107
|
-
|
|
108
|
-
failures: Array<{
|
|
109
|
-
|
|
111
|
+
succeededDeploymentIds: string[];
|
|
112
|
+
failures: Array<{ deploymentId: string; error: DeploymentOperationError }>;
|
|
113
|
+
appDeleted: boolean;
|
|
110
114
|
}>() {
|
|
111
115
|
constructor(args: {
|
|
112
|
-
|
|
113
|
-
failures: Array<{
|
|
114
|
-
|
|
116
|
+
succeededDeploymentIds: string[];
|
|
117
|
+
failures: Array<{ deploymentId: string; error: DeploymentOperationError }>;
|
|
118
|
+
appDeleted: boolean;
|
|
115
119
|
}) {
|
|
116
|
-
const failedIds = args.failures.map((f) => f.
|
|
120
|
+
const failedIds = args.failures.map((f) => f.deploymentId).join(", ");
|
|
117
121
|
super({
|
|
118
122
|
...args,
|
|
119
|
-
message: `Failed to destroy ${args.failures.length}
|
|
123
|
+
message: `Failed to destroy ${args.failures.length} deployment(s): ${failedIds}`,
|
|
120
124
|
});
|
|
121
125
|
}
|
|
122
126
|
}
|
|
@@ -138,7 +142,7 @@ export type DeployError =
|
|
|
138
142
|
| BuildError
|
|
139
143
|
| ArtifactError
|
|
140
144
|
| TimeoutError
|
|
141
|
-
|
|
|
145
|
+
| DeploymentFailedError
|
|
142
146
|
| CancelledError;
|
|
143
147
|
|
|
144
148
|
export type UpdateEnvError =
|
|
@@ -146,21 +150,21 @@ export type UpdateEnvError =
|
|
|
146
150
|
| ApiError
|
|
147
151
|
| MissingArgumentError
|
|
148
152
|
| InvalidOptionsError
|
|
149
|
-
|
|
|
153
|
+
| NoExistingDeploymentError
|
|
150
154
|
| TimeoutError
|
|
151
|
-
|
|
|
155
|
+
| DeploymentFailedError
|
|
152
156
|
| CancelledError;
|
|
153
157
|
|
|
154
|
-
export type
|
|
158
|
+
export type DestroyDeploymentError =
|
|
155
159
|
| CancelledError
|
|
156
160
|
| MissingArgumentError
|
|
157
|
-
|
|
|
161
|
+
| NoDeploymentsFoundError
|
|
158
162
|
| AuthenticationError
|
|
159
163
|
| ApiError
|
|
160
164
|
| TimeoutError
|
|
161
|
-
|
|
|
165
|
+
| DeploymentFailedError;
|
|
162
166
|
|
|
163
|
-
export type
|
|
167
|
+
export type DestroyAppError =
|
|
164
168
|
| CancelledError
|
|
165
169
|
| AuthenticationError
|
|
166
170
|
| ApiError
|
|
@@ -170,16 +174,16 @@ export type PromoteError =
|
|
|
170
174
|
| AuthenticationError
|
|
171
175
|
| ApiError
|
|
172
176
|
| MissingArgumentError
|
|
173
|
-
|
|
|
177
|
+
| NoDeploymentsFoundError
|
|
174
178
|
| TimeoutError
|
|
175
|
-
|
|
|
179
|
+
| DeploymentFailedError
|
|
176
180
|
| CancelledError;
|
|
177
181
|
|
|
178
182
|
export type ApiRequestError = CancelledError | AuthenticationError | ApiError;
|
|
179
183
|
|
|
180
|
-
export type
|
|
184
|
+
export type DeploymentOperationError =
|
|
181
185
|
| CancelledError
|
|
182
186
|
| AuthenticationError
|
|
183
187
|
| ApiError
|
|
184
188
|
| TimeoutError
|
|
185
|
-
|
|
|
189
|
+
| DeploymentFailedError;
|