@prisma/compute-sdk 0.24.0 → 0.25.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/apply-migrations.d.ts +61 -0
- package/dist/apply-migrations.d.ts.map +1 -0
- package/dist/apply-migrations.js +314 -0
- package/dist/apply-migrations.js.map +1 -0
- package/dist/detect-schema.d.ts +31 -0
- package/dist/detect-schema.d.ts.map +1 -0
- package/dist/detect-schema.js +212 -0
- package/dist/detect-schema.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/apply-migrations.ts +454 -0
- package/src/detect-schema.ts +384 -0
- package/src/index.ts +14 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { Err, matchError, matchErrorPartial, Ok, type Result, TaggedError, } from "better-result";
|
|
2
|
+
export type { ApplyMigrationsOptions, ApplyMigrationsResult, } from "./apply-migrations.ts";
|
|
3
|
+
export { applyMigrations } from "./apply-migrations.ts";
|
|
2
4
|
export { createArchive } from "./archive.ts";
|
|
3
5
|
export { normalizeArtifactSymlinks } from "./artifact-postprocess.ts";
|
|
4
6
|
export { AstroBuild } from "./astro-build.ts";
|
|
@@ -9,6 +11,8 @@ export { BunBuild } from "./bun-build.ts";
|
|
|
9
11
|
export type { DeployInteraction, DeployProgress, DestroyServiceProgress, DestroyVersionInteraction, DestroyVersionProgress, PromoteProgress, UpdateEnvProgress, } from "./callbacks.ts";
|
|
10
12
|
export type { CreateProjectOptions, CreateServiceOptions, DeleteServiceOptions, DeleteVersionOptions, DeployOptions, DeployResult, DestroyServiceOptions, DestroyServiceResult, DestroyVersionOptions, DestroyVersionResult, ListProjectsOptions, ListServicesOptions, ListVersionsOptions, PromoteOptions, PromoteResult, ShowServiceOptions, ShowVersionOptions, StartVersionOptions, StopVersionOptions, UpdateEnvOptions, UpdateEnvResult, } from "./compute-client.ts";
|
|
11
13
|
export { ComputeClient } from "./compute-client.ts";
|
|
14
|
+
export type { DetectedSchema, MigrationCommand, SchemaDetection, SchemaEngine, UnsupportedSchema, UnsupportedSchemaTarget, } from "./detect-schema.ts";
|
|
15
|
+
export { detectAppSchema } from "./detect-schema.ts";
|
|
12
16
|
export type { ApiRequestError, DeployError, DestroyServiceError, DestroyVersionError, PromoteError, UpdateEnvError, VersionOperationError, } from "./errors.ts";
|
|
13
17
|
export { ApiError, ArtifactError, AuthenticationError, BuildError, CancelledError, DestroyAggregateError, InvalidOptionsError, LogStreamError, MissingArgumentError, NoExistingVersionError, NoVersionsFoundError, TimeoutError, VersionFailedError, } from "./errors.ts";
|
|
14
18
|
export type { LogRecord, LogStreamOptions, StreamLogsError, StreamRecord, StreamResult, TerminalRecord, } from "./log-stream.ts";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,GAAG,EACH,UAAU,EACV,iBAAiB,EACjB,EAAE,EACF,KAAK,MAAM,EACX,WAAW,GACZ,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,YAAY,EACV,iBAAiB,EACjB,cAAc,EACd,sBAAsB,EACtB,yBAAyB,EACzB,sBAAsB,EACtB,eAAe,EACf,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,aAAa,EACb,YAAY,EACZ,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EACV,eAAe,EACf,WAAW,EACX,mBAAmB,EACnB,mBAAmB,EACnB,YAAY,EACZ,cAAc,EACd,qBAAqB,GACtB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,QAAQ,EACR,aAAa,EACb,mBAAmB,EACnB,UAAU,EACV,cAAc,EACd,qBAAqB,EACrB,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACpB,YAAY,EACZ,kBAAkB,GACnB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,SAAS,EACT,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,cAAc,GACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,YAAY,EACV,mBAAmB,EACnB,YAAY,EACZ,WAAW,EACX,WAAW,EACX,UAAU,EACV,cAAc,EACd,aAAa,EACb,WAAW,EACX,aAAa,EACb,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,GAAG,EACH,UAAU,EACV,iBAAiB,EACjB,EAAE,EACF,KAAK,MAAM,EACX,WAAW,GACZ,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,YAAY,EACV,iBAAiB,EACjB,cAAc,EACd,sBAAsB,EACtB,yBAAyB,EACzB,sBAAsB,EACtB,eAAe,EACf,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,aAAa,EACb,YAAY,EACZ,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,YAAY,EACV,eAAe,EACf,WAAW,EACX,mBAAmB,EACnB,mBAAmB,EACnB,YAAY,EACZ,cAAc,EACd,qBAAqB,GACtB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,QAAQ,EACR,aAAa,EACb,mBAAmB,EACnB,UAAU,EACV,cAAc,EACd,qBAAqB,EACrB,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACpB,YAAY,EACZ,kBAAkB,GACnB,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,SAAS,EACT,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,cAAc,GACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,YAAY,EACV,mBAAmB,EACnB,YAAY,EACZ,WAAW,EACX,WAAW,EACX,UAAU,EACV,cAAc,EACd,aAAa,EACb,WAAW,EACX,aAAa,EACb,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Re-export Result utilities so consumers don't need a direct better-result dependency
|
|
2
2
|
export { Err, matchError, matchErrorPartial, Ok, TaggedError, } from "better-result";
|
|
3
|
+
export { applyMigrations } from "./apply-migrations.js";
|
|
3
4
|
export { createArchive } from "./archive.js";
|
|
4
5
|
export { normalizeArtifactSymlinks } from "./artifact-postprocess.js";
|
|
5
6
|
export { AstroBuild } from "./astro-build.js";
|
|
@@ -7,6 +8,7 @@ export { AutoBuild } from "./auto-build.js";
|
|
|
7
8
|
export { PreBuilt } from "./build-strategy.js";
|
|
8
9
|
export { BunBuild } from "./bun-build.js";
|
|
9
10
|
export { ComputeClient } from "./compute-client.js";
|
|
11
|
+
export { detectAppSchema } from "./detect-schema.js";
|
|
10
12
|
export { ApiError, ArtifactError, AuthenticationError, BuildError, CancelledError, DestroyAggregateError, InvalidOptionsError, LogStreamError, MissingArgumentError, NoExistingVersionError, NoVersionsFoundError, TimeoutError, VersionFailedError, } from "./errors.js";
|
|
11
13
|
export { streamLogs } from "./log-stream.js";
|
|
12
14
|
export { NextjsBuild } from "./nextjs-build.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,OAAO,EACL,GAAG,EACH,UAAU,EACV,iBAAiB,EACjB,EAAE,EAEF,WAAW,GACZ,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,OAAO,EACL,GAAG,EACH,UAAU,EACV,iBAAiB,EACjB,EAAE,EAEF,WAAW,GACZ,MAAM,eAAe,CAAC;AAKvB,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAiC1C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AASpD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAUrD,OAAO,EACL,QAAQ,EACR,aAAa,EACb,mBAAmB,EACnB,UAAU,EACV,cAAc,EACd,qBAAqB,EACrB,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACpB,sBAAsB,EACtB,oBAAoB,EACpB,YAAY,EACZ,kBAAkB,GACnB,MAAM,aAAa,CAAC;AASrB,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAa/D,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Applies an application's database schema against a database, owning the
|
|
3
|
+
* prisma CLI resolution so every deploy front door (git-push build-runner,
|
|
4
|
+
* `prisma app deploy`) runs migrations the same way.
|
|
5
|
+
*
|
|
6
|
+
* The resolver locates the project's own prisma binary by walking
|
|
7
|
+
* `node_modules` from the schema directory up to the source root, the way
|
|
8
|
+
* Node resolves a dependency. That covers npm, pnpm, yarn, and monorepo
|
|
9
|
+
* layouts where prisma is installed but the flat `node_modules/.bin/prisma`
|
|
10
|
+
* symlink is absent at the app root.
|
|
11
|
+
*/
|
|
12
|
+
import { spawn } from "node:child_process";
|
|
13
|
+
import { readFile } from "node:fs/promises";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import type { Readable } from "node:stream";
|
|
16
|
+
import { sourceRootLineage } from "./config/source-root.ts";
|
|
17
|
+
import type {
|
|
18
|
+
DetectedSchema,
|
|
19
|
+
MigrationCommand,
|
|
20
|
+
SchemaEngine,
|
|
21
|
+
} from "./detect-schema.ts";
|
|
22
|
+
import { detectAppSchema } from "./detect-schema.ts";
|
|
23
|
+
import { BuildError } from "./errors.ts";
|
|
24
|
+
|
|
25
|
+
export interface ApplyMigrationsOptions {
|
|
26
|
+
/** Application root the build runs in; also the CLI working directory. */
|
|
27
|
+
appPath: string;
|
|
28
|
+
/** Connection used for migrations: the direct/admin database URL. */
|
|
29
|
+
databaseUrl: string;
|
|
30
|
+
/** Auto-detected from `appPath` when omitted. */
|
|
31
|
+
schema?: DetectedSchema;
|
|
32
|
+
/** Extra environment for the CLI child, e.g. a user-set `DIRECT_URL`. */
|
|
33
|
+
env?: Record<string, string>;
|
|
34
|
+
signal?: AbortSignal;
|
|
35
|
+
onLog?: (line: string, stream: "stdout" | "stderr") => void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type ApplyMigrationsResult =
|
|
39
|
+
| { applied: true; via: MigrationCommand }
|
|
40
|
+
| { applied: false; reason: "no-schema" };
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Last resort for repos that ship a schema with no prisma installed at all.
|
|
44
|
+
* Pinned to the 6.x line: Prisma 7 rejects the classic `url = env(...)`
|
|
45
|
+
* datasource form (P1012), which is exactly the schema shape such repos have.
|
|
46
|
+
* Bump deliberately, never to `latest`.
|
|
47
|
+
*/
|
|
48
|
+
export const FALLBACK_PRISMA_CLI_VERSION = "6.19.3";
|
|
49
|
+
|
|
50
|
+
export interface CliLauncher {
|
|
51
|
+
command: string;
|
|
52
|
+
argsPrefix: string[];
|
|
53
|
+
/** Human-readable command prefix used in failure messages. */
|
|
54
|
+
display: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Applies the app's schema and returns how it was applied, or `no-schema`
|
|
59
|
+
* when the app has none. Throws {@link BuildError} on failure: a non-zero
|
|
60
|
+
* CLI exit carries `exited with code N` (a user-fixable error), while an
|
|
61
|
+
* unrunnable launcher does not (an infrastructure error).
|
|
62
|
+
*/
|
|
63
|
+
export async function applyMigrations(
|
|
64
|
+
opts: ApplyMigrationsOptions,
|
|
65
|
+
): Promise<ApplyMigrationsResult> {
|
|
66
|
+
opts.signal?.throwIfAborted();
|
|
67
|
+
|
|
68
|
+
const schema =
|
|
69
|
+
opts.schema ?? (await detectAppSchema(opts.appPath, opts.signal)).schema;
|
|
70
|
+
if (!schema) {
|
|
71
|
+
return { applied: false, reason: "no-schema" };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const launchers = await resolveSchemaEngineCli(
|
|
75
|
+
schema,
|
|
76
|
+
opts.appPath,
|
|
77
|
+
opts.signal,
|
|
78
|
+
);
|
|
79
|
+
const relativeSchemaPath =
|
|
80
|
+
path.relative(opts.appPath, schema.path) ||
|
|
81
|
+
defaultSchemaSourcePath(schema.engine);
|
|
82
|
+
const env = {
|
|
83
|
+
...process.env,
|
|
84
|
+
...opts.env,
|
|
85
|
+
DATABASE_URL: opts.databaseUrl,
|
|
86
|
+
// Update nags pollute the migration log and failure snippets.
|
|
87
|
+
PRISMA_HIDE_UPDATE_MESSAGE: "1",
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
for (const subcommand of subcommandsFor(
|
|
91
|
+
schema,
|
|
92
|
+
relativeSchemaPath,
|
|
93
|
+
opts.databaseUrl,
|
|
94
|
+
)) {
|
|
95
|
+
await runWithLaunchers({
|
|
96
|
+
launchers,
|
|
97
|
+
subcommand: subcommand.args,
|
|
98
|
+
subcommandDisplay: subcommand.display,
|
|
99
|
+
cwd: opts.appPath,
|
|
100
|
+
env,
|
|
101
|
+
signal: opts.signal,
|
|
102
|
+
onLog: opts.onLog,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return { applied: true, via: schema.command };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Resolves the launcher ladder for a schema's engine: the project's own
|
|
111
|
+
* binary first (run via `node`), then download fallbacks tried in order when
|
|
112
|
+
* the binary is not installed.
|
|
113
|
+
*/
|
|
114
|
+
export async function resolveSchemaEngineCli(
|
|
115
|
+
schema: DetectedSchema,
|
|
116
|
+
appPath: string,
|
|
117
|
+
signal?: AbortSignal,
|
|
118
|
+
): Promise<CliLauncher[]> {
|
|
119
|
+
const startDir = path.dirname(schema.path) || appPath;
|
|
120
|
+
|
|
121
|
+
if (schema.engine === "prisma-next") {
|
|
122
|
+
const localBin = await resolveLocalCliBin(
|
|
123
|
+
startDir,
|
|
124
|
+
"@prisma-next/cli",
|
|
125
|
+
"prisma-next",
|
|
126
|
+
signal,
|
|
127
|
+
);
|
|
128
|
+
if (localBin) {
|
|
129
|
+
return [
|
|
130
|
+
{ command: "node", argsPrefix: [localBin], display: "prisma-next" },
|
|
131
|
+
];
|
|
132
|
+
}
|
|
133
|
+
// `@prisma-next/cli` is not published to npm, so there is no download
|
|
134
|
+
// fallback; `--no-install` keeps today's behaviour when it is missing.
|
|
135
|
+
return [
|
|
136
|
+
{
|
|
137
|
+
command: "npx",
|
|
138
|
+
argsPrefix: ["--no-install", "prisma-next"],
|
|
139
|
+
display: "npx --no-install prisma-next",
|
|
140
|
+
},
|
|
141
|
+
];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const localBin = await resolveLocalCliBin(
|
|
145
|
+
startDir,
|
|
146
|
+
"prisma",
|
|
147
|
+
"prisma",
|
|
148
|
+
signal,
|
|
149
|
+
);
|
|
150
|
+
if (localBin) {
|
|
151
|
+
return [{ command: "node", argsPrefix: [localBin], display: "prisma" }];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const pinned =
|
|
155
|
+
(await readResolvedPrismaVersion(startDir, signal)) ??
|
|
156
|
+
FALLBACK_PRISMA_CLI_VERSION;
|
|
157
|
+
return [
|
|
158
|
+
{
|
|
159
|
+
command: "npx",
|
|
160
|
+
argsPrefix: ["--yes", "--package", `prisma@${pinned}`, "prisma"],
|
|
161
|
+
display: `npx prisma@${pinned}`,
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
command: "bunx",
|
|
165
|
+
argsPrefix: [`prisma@${pinned}`],
|
|
166
|
+
display: `bunx prisma@${pinned}`,
|
|
167
|
+
},
|
|
168
|
+
];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async function resolveLocalCliBin(
|
|
172
|
+
startDir: string,
|
|
173
|
+
packageName: string,
|
|
174
|
+
binName: string,
|
|
175
|
+
signal?: AbortSignal,
|
|
176
|
+
): Promise<string | null> {
|
|
177
|
+
for (const dir of await sourceRootLineage(startDir, signal)) {
|
|
178
|
+
const packageDir = path.join(dir, "node_modules", packageName);
|
|
179
|
+
const bin = await readBinFromPackageJson(
|
|
180
|
+
path.join(packageDir, "package.json"),
|
|
181
|
+
binName,
|
|
182
|
+
signal,
|
|
183
|
+
);
|
|
184
|
+
if (bin) {
|
|
185
|
+
return path.resolve(packageDir, bin);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async function readBinFromPackageJson(
|
|
192
|
+
packageJsonPath: string,
|
|
193
|
+
binName: string,
|
|
194
|
+
signal?: AbortSignal,
|
|
195
|
+
): Promise<string | null> {
|
|
196
|
+
let raw: string;
|
|
197
|
+
try {
|
|
198
|
+
raw = await readFile(packageJsonPath, { encoding: "utf8", signal });
|
|
199
|
+
} catch (error) {
|
|
200
|
+
if (signal?.aborted) throw error;
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
let parsed: unknown;
|
|
205
|
+
try {
|
|
206
|
+
parsed = JSON.parse(raw);
|
|
207
|
+
} catch {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const bin = (parsed as { bin?: unknown }).bin;
|
|
212
|
+
if (typeof bin === "string") {
|
|
213
|
+
return bin;
|
|
214
|
+
}
|
|
215
|
+
if (bin && typeof bin === "object") {
|
|
216
|
+
const entry = (bin as Record<string, unknown>)[binName];
|
|
217
|
+
return typeof entry === "string" ? entry : null;
|
|
218
|
+
}
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async function readResolvedPrismaVersion(
|
|
223
|
+
startDir: string,
|
|
224
|
+
signal?: AbortSignal,
|
|
225
|
+
): Promise<string | null> {
|
|
226
|
+
for (const dir of await sourceRootLineage(startDir, signal)) {
|
|
227
|
+
const version = await readPackageVersion(
|
|
228
|
+
path.join(dir, "node_modules", "@prisma", "client", "package.json"),
|
|
229
|
+
signal,
|
|
230
|
+
);
|
|
231
|
+
if (version) {
|
|
232
|
+
return version;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async function readPackageVersion(
|
|
239
|
+
packageJsonPath: string,
|
|
240
|
+
signal?: AbortSignal,
|
|
241
|
+
): Promise<string | null> {
|
|
242
|
+
let raw: string;
|
|
243
|
+
try {
|
|
244
|
+
raw = await readFile(packageJsonPath, { encoding: "utf8", signal });
|
|
245
|
+
} catch (error) {
|
|
246
|
+
if (signal?.aborted) throw error;
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
try {
|
|
251
|
+
const parsed: unknown = JSON.parse(raw);
|
|
252
|
+
const version = (parsed as { version?: unknown }).version;
|
|
253
|
+
return typeof version === "string" && version.length > 0 ? version : null;
|
|
254
|
+
} catch {
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function subcommandsFor(
|
|
260
|
+
schema: DetectedSchema,
|
|
261
|
+
relativeSchemaPath: string,
|
|
262
|
+
databaseUrl: string,
|
|
263
|
+
): Array<{ args: string[]; display: string }> {
|
|
264
|
+
switch (schema.command) {
|
|
265
|
+
case "migrate-deploy":
|
|
266
|
+
return [
|
|
267
|
+
{
|
|
268
|
+
args: ["migrate", "deploy", "--schema", relativeSchemaPath],
|
|
269
|
+
display: "migrate deploy",
|
|
270
|
+
},
|
|
271
|
+
];
|
|
272
|
+
case "db-push":
|
|
273
|
+
return [
|
|
274
|
+
{
|
|
275
|
+
args: ["db", "push", "--schema", relativeSchemaPath],
|
|
276
|
+
display: "db push",
|
|
277
|
+
},
|
|
278
|
+
];
|
|
279
|
+
case "prisma-next-init":
|
|
280
|
+
return [
|
|
281
|
+
{
|
|
282
|
+
args: ["contract", "emit", "--config", relativeSchemaPath],
|
|
283
|
+
display: "contract emit",
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
args: [
|
|
287
|
+
"db",
|
|
288
|
+
"init",
|
|
289
|
+
"--config",
|
|
290
|
+
relativeSchemaPath,
|
|
291
|
+
"--db",
|
|
292
|
+
databaseUrl,
|
|
293
|
+
],
|
|
294
|
+
display: "db init",
|
|
295
|
+
},
|
|
296
|
+
];
|
|
297
|
+
default: {
|
|
298
|
+
const exhaustive: never = schema.command;
|
|
299
|
+
throw new BuildError({
|
|
300
|
+
message: `Unknown migration command: ${String(exhaustive)}`,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function defaultSchemaSourcePath(engine: SchemaEngine): string {
|
|
307
|
+
return engine === "prisma-next" ? "prisma-next.config.ts" : "schema.prisma";
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
type LauncherOutcome =
|
|
311
|
+
| { kind: "ok" }
|
|
312
|
+
| { kind: "unavailable" }
|
|
313
|
+
| { kind: "failed"; code: number };
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Runs `subcommand` against the launcher ladder, falling through to the next
|
|
317
|
+
* launcher when one cannot run (spawn ENOENT or exit 127) and throwing on the
|
|
318
|
+
* first launcher that runs the tool to a real non-zero exit.
|
|
319
|
+
*/
|
|
320
|
+
export async function runWithLaunchers(opts: {
|
|
321
|
+
launchers: CliLauncher[];
|
|
322
|
+
subcommand: string[];
|
|
323
|
+
subcommandDisplay: string;
|
|
324
|
+
cwd: string;
|
|
325
|
+
env: NodeJS.ProcessEnv;
|
|
326
|
+
signal?: AbortSignal;
|
|
327
|
+
onLog?: (line: string, stream: "stdout" | "stderr") => void;
|
|
328
|
+
}): Promise<void> {
|
|
329
|
+
for (const launcher of opts.launchers) {
|
|
330
|
+
opts.signal?.throwIfAborted();
|
|
331
|
+
const outcome = await runOnce(launcher, opts);
|
|
332
|
+
if (outcome.kind === "ok") {
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (outcome.kind === "failed") {
|
|
336
|
+
throw new BuildError({
|
|
337
|
+
message: `${launcher.display} ${opts.subcommandDisplay} exited with code ${outcome.code}`,
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
throw new BuildError({
|
|
343
|
+
message: `Could not run ${opts.subcommandDisplay}: no prisma launcher was runnable (tried ${opts.launchers
|
|
344
|
+
.map((launcher) => launcher.display)
|
|
345
|
+
.join(", ")})`,
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function runOnce(
|
|
350
|
+
launcher: CliLauncher,
|
|
351
|
+
opts: {
|
|
352
|
+
subcommand: string[];
|
|
353
|
+
cwd: string;
|
|
354
|
+
env: NodeJS.ProcessEnv;
|
|
355
|
+
signal?: AbortSignal;
|
|
356
|
+
onLog?: (line: string, stream: "stdout" | "stderr") => void;
|
|
357
|
+
},
|
|
358
|
+
): Promise<LauncherOutcome> {
|
|
359
|
+
return new Promise<LauncherOutcome>((resolve, reject) => {
|
|
360
|
+
const child = spawn(
|
|
361
|
+
launcher.command,
|
|
362
|
+
[...launcher.argsPrefix, ...opts.subcommand],
|
|
363
|
+
{
|
|
364
|
+
cwd: opts.cwd,
|
|
365
|
+
env: opts.env,
|
|
366
|
+
signal: opts.signal,
|
|
367
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
368
|
+
},
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
const flushStdout = streamLines(child.stdout, "stdout", opts.onLog);
|
|
372
|
+
const flushStderr = streamLines(child.stderr, "stderr", opts.onLog);
|
|
373
|
+
|
|
374
|
+
child.once("error", (error: NodeJS.ErrnoException) => {
|
|
375
|
+
if (opts.signal?.aborted) {
|
|
376
|
+
reject(error);
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
// A launcher that is not installed (e.g. `bunx` absent) should fall
|
|
380
|
+
// through to the next, not surface as a migration failure.
|
|
381
|
+
if (error.code === "ENOENT") {
|
|
382
|
+
resolve({ kind: "unavailable" });
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
reject(
|
|
386
|
+
new BuildError({
|
|
387
|
+
message: `Failed to start ${launcher.display}: ${error.message}`,
|
|
388
|
+
}),
|
|
389
|
+
);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
child.once("close", (code, terminationSignal) => {
|
|
393
|
+
flushStdout();
|
|
394
|
+
flushStderr();
|
|
395
|
+
|
|
396
|
+
if (terminationSignal) {
|
|
397
|
+
if (opts.signal?.aborted) {
|
|
398
|
+
reject(opts.signal.reason ?? new Error("aborted"));
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
reject(
|
|
402
|
+
new BuildError({
|
|
403
|
+
message: `${launcher.display} was terminated by ${terminationSignal}`,
|
|
404
|
+
}),
|
|
405
|
+
);
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (code === 0) {
|
|
410
|
+
resolve({ kind: "ok" });
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
// Exit 127 is the shell's "command not found": the launcher could not
|
|
414
|
+
// exec the tool. Treat it like ENOENT and try the next launcher rather
|
|
415
|
+
// than blaming the user's schema.
|
|
416
|
+
if (code === 127) {
|
|
417
|
+
resolve({ kind: "unavailable" });
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
resolve({ kind: "failed", code: code ?? 1 });
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function streamLines(
|
|
426
|
+
stream: Readable | null,
|
|
427
|
+
channel: "stdout" | "stderr",
|
|
428
|
+
onLog?: (line: string, stream: "stdout" | "stderr") => void,
|
|
429
|
+
): () => void {
|
|
430
|
+
if (!stream) {
|
|
431
|
+
return () => {};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
let buffer = "";
|
|
435
|
+
stream.on("data", (chunk: Buffer) => {
|
|
436
|
+
buffer += chunk.toString("utf8");
|
|
437
|
+
let newline = buffer.indexOf("\n");
|
|
438
|
+
while (newline !== -1) {
|
|
439
|
+
const line = buffer.slice(0, newline);
|
|
440
|
+
if (line.length > 0) {
|
|
441
|
+
onLog?.(line, channel);
|
|
442
|
+
}
|
|
443
|
+
buffer = buffer.slice(newline + 1);
|
|
444
|
+
newline = buffer.indexOf("\n");
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
return () => {
|
|
449
|
+
if (buffer.length > 0) {
|
|
450
|
+
onLog?.(buffer, channel);
|
|
451
|
+
buffer = "";
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
}
|