@prisma/cli 3.0.0-beta.8 → 3.0.0-dev.101.1
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/README.md +0 -13
- package/dist/adapters/mock-api.js +75 -0
- package/dist/adapters/token-storage.js +311 -33
- package/dist/cli.js +7 -7
- package/dist/cli2.js +5 -3
- package/dist/commands/app/index.js +65 -52
- package/dist/commands/auth/index.js +55 -2
- package/dist/commands/database/index.js +159 -0
- package/dist/controllers/app-env.js +10 -7
- package/dist/controllers/app.js +550 -208
- package/dist/controllers/auth.js +211 -2
- package/dist/controllers/branch.js +5 -6
- package/dist/controllers/database.js +318 -0
- package/dist/controllers/project.js +49 -20
- package/dist/lib/app/app-interaction.js +5 -0
- package/dist/lib/app/{preview-provider.js → app-provider.js} +32 -28
- package/dist/lib/app/{preview-branch-database.js → branch-database-api.js} +1 -1
- package/dist/lib/app/branch-database-deploy.js +13 -60
- package/dist/lib/app/branch-database.js +1 -102
- package/dist/lib/app/build-settings.js +93 -0
- package/dist/lib/app/build.js +82 -0
- package/dist/lib/app/bun-project.js +2 -3
- package/dist/lib/app/compute-config.js +147 -0
- package/dist/lib/app/deploy-plan.js +58 -0
- package/dist/lib/app/{preview-progress.js → deploy-progress.js} +5 -5
- package/dist/lib/app/local-dev.js +3 -60
- package/dist/lib/app/production-deploy-gate.js +3 -3
- package/dist/lib/app/read-branch.js +30 -0
- package/dist/lib/auth/auth-ops.js +10 -4
- package/dist/lib/auth/guard.js +4 -1
- package/dist/lib/auth/login.js +32 -25
- package/dist/lib/database/provider.js +167 -0
- package/dist/lib/diagnostics.js +1 -1
- package/dist/lib/fs/home-path.js +24 -0
- package/dist/lib/git/local-branch.js +15 -3
- package/dist/lib/project/local-pin.js +170 -40
- package/dist/lib/project/resolution.js +166 -61
- package/dist/lib/project/setup.js +65 -19
- package/dist/output/patterns.js +1 -1
- package/dist/presenters/app.js +44 -39
- package/dist/presenters/auth.js +98 -1
- package/dist/presenters/branch.js +1 -1
- package/dist/presenters/database.js +274 -0
- package/dist/presenters/project.js +3 -9
- package/dist/shell/command-meta.js +153 -2
- package/dist/shell/command-runner.js +39 -21
- package/dist/shell/diagnostics-output.js +2 -8
- package/dist/shell/errors.js +50 -1
- package/dist/shell/help.js +30 -20
- package/dist/shell/runtime.js +8 -4
- package/dist/shell/ui.js +19 -2
- package/dist/use-cases/auth.js +68 -1
- package/package.json +18 -3
- package/dist/lib/app/preview-build-settings.js +0 -385
- package/dist/lib/app/preview-build.js +0 -475
- package/dist/lib/app/preview-interaction.js +0 -5
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { CliError } from "../../shell/errors.js";
|
|
2
|
+
//#region src/lib/database/provider.ts
|
|
3
|
+
function createManagementDatabaseProvider(client) {
|
|
4
|
+
return {
|
|
5
|
+
async listDatabases(options) {
|
|
6
|
+
const databases = [];
|
|
7
|
+
let cursor;
|
|
8
|
+
while (true) {
|
|
9
|
+
const result = await client.GET("/v1/databases", {
|
|
10
|
+
params: { query: {
|
|
11
|
+
projectId: options.projectId,
|
|
12
|
+
branchGitName: options.branchName,
|
|
13
|
+
cursor
|
|
14
|
+
} },
|
|
15
|
+
signal: options.signal
|
|
16
|
+
});
|
|
17
|
+
if (result.error || !result.data) throw databaseApiError("Failed to list databases", result.response, result.error);
|
|
18
|
+
databases.push(...result.data.data);
|
|
19
|
+
if (!result.data.pagination.hasMore || !result.data.pagination.nextCursor) break;
|
|
20
|
+
cursor = result.data.pagination.nextCursor;
|
|
21
|
+
}
|
|
22
|
+
return databases.map((database) => normalizeDatabase(database, options.projectId));
|
|
23
|
+
},
|
|
24
|
+
async showDatabase(databaseId, options) {
|
|
25
|
+
const result = await client.GET("/v1/databases/{databaseId}", {
|
|
26
|
+
params: { path: { databaseId } },
|
|
27
|
+
signal: options?.signal
|
|
28
|
+
});
|
|
29
|
+
if (result.response?.status === 404) return null;
|
|
30
|
+
if (result.error || !result.data) throw databaseApiError("Failed to show database", result.response, result.error);
|
|
31
|
+
const database = result.data.data;
|
|
32
|
+
return normalizeDatabase(database, requireDatabaseProjectId(database, options?.projectId));
|
|
33
|
+
},
|
|
34
|
+
async createDatabase(options) {
|
|
35
|
+
const result = await client.POST("/v1/databases", {
|
|
36
|
+
body: {
|
|
37
|
+
projectId: options.projectId,
|
|
38
|
+
name: options.name,
|
|
39
|
+
source: { type: "empty" },
|
|
40
|
+
...options.branchName ? { branchGitName: options.branchName } : {},
|
|
41
|
+
...options.region ? { region: options.region } : {}
|
|
42
|
+
},
|
|
43
|
+
signal: options.signal
|
|
44
|
+
});
|
|
45
|
+
if (result.error || !result.data) throw databaseApiError("Failed to create database", result.response, result.error);
|
|
46
|
+
return normalizeCreatedDatabase(result.data.data, options.projectId);
|
|
47
|
+
},
|
|
48
|
+
async removeDatabase(databaseId, options) {
|
|
49
|
+
const result = await client.DELETE("/v1/databases/{databaseId}", {
|
|
50
|
+
params: { path: { databaseId } },
|
|
51
|
+
signal: options?.signal
|
|
52
|
+
});
|
|
53
|
+
if (result.error) throw databaseApiError("Failed to remove database", result.response, result.error);
|
|
54
|
+
},
|
|
55
|
+
async listConnections(databaseId, options) {
|
|
56
|
+
const result = await client.GET("/v1/databases/{databaseId}/connections", {
|
|
57
|
+
params: { path: { databaseId } },
|
|
58
|
+
signal: options?.signal
|
|
59
|
+
});
|
|
60
|
+
if (result.error || !result.data) throw databaseApiError("Failed to list database connections", result.response, result.error);
|
|
61
|
+
return result.data.data.map((connection) => normalizeConnection(connection, databaseId));
|
|
62
|
+
},
|
|
63
|
+
async createConnection(options) {
|
|
64
|
+
const result = await client.POST("/v1/databases/{databaseId}/connections", {
|
|
65
|
+
params: { path: { databaseId: options.databaseId } },
|
|
66
|
+
body: { name: options.name },
|
|
67
|
+
signal: options.signal
|
|
68
|
+
});
|
|
69
|
+
if (result.error || !result.data) throw databaseApiError("Failed to create database connection", result.response, result.error);
|
|
70
|
+
return normalizeCreatedConnection(result.data.data, options.databaseId);
|
|
71
|
+
},
|
|
72
|
+
async removeConnection(connectionId, options) {
|
|
73
|
+
const result = await client.DELETE("/v1/connections/{id}", {
|
|
74
|
+
params: { path: { id: connectionId } },
|
|
75
|
+
signal: options?.signal
|
|
76
|
+
});
|
|
77
|
+
if (result.error) throw databaseApiError("Failed to remove database connection", result.response, result.error);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function normalizeDatabase(database, fallbackProjectId) {
|
|
82
|
+
return {
|
|
83
|
+
id: database.id,
|
|
84
|
+
name: database.name,
|
|
85
|
+
projectId: database.projectId ?? fallbackProjectId,
|
|
86
|
+
branchId: database.branchId ?? database.branch?.id ?? null,
|
|
87
|
+
branchName: database.branchGitName ?? database.branchName ?? database.branch?.gitName ?? database.branch?.name ?? null,
|
|
88
|
+
region: normalizeRegion(database),
|
|
89
|
+
status: database.status ?? null,
|
|
90
|
+
isDefault: database.isDefault ?? null,
|
|
91
|
+
createdAt: database.createdAt ?? null
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function normalizeConnection(connection, fallbackDatabaseId) {
|
|
95
|
+
return {
|
|
96
|
+
id: connection.id,
|
|
97
|
+
name: connection.name ?? connection.id,
|
|
98
|
+
databaseId: connection.databaseId ?? fallbackDatabaseId,
|
|
99
|
+
createdAt: connection.createdAt ?? null
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function normalizeCreatedDatabase(database, fallbackProjectId) {
|
|
103
|
+
const rawConnection = database.connections?.[0];
|
|
104
|
+
if (!rawConnection) throw new CliError({
|
|
105
|
+
code: "DATABASE_CONNECTION_MISSING",
|
|
106
|
+
domain: "database",
|
|
107
|
+
summary: "Created database did not return a connection string",
|
|
108
|
+
why: "The Management API created the database but did not include the one-time connection payload.",
|
|
109
|
+
fix: "Create a connection explicitly with prisma-cli database connection create <database>.",
|
|
110
|
+
exitCode: 1,
|
|
111
|
+
nextSteps: [`prisma-cli database connection create ${database.id}`]
|
|
112
|
+
});
|
|
113
|
+
return {
|
|
114
|
+
database: normalizeDatabase(database, fallbackProjectId),
|
|
115
|
+
...normalizeCreatedConnection(rawConnection, database.id)
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function normalizeCreatedConnection(connection, fallbackDatabaseId) {
|
|
119
|
+
const connectionString = extractConnectionString(connection);
|
|
120
|
+
if (!connectionString) throw new CliError({
|
|
121
|
+
code: "DATABASE_CONNECTION_STRING_MISSING",
|
|
122
|
+
domain: "database",
|
|
123
|
+
summary: "Created connection did not return a connection string",
|
|
124
|
+
why: "Database connection strings are one-time-view secrets, but the Management API did not include one in this create response.",
|
|
125
|
+
fix: "Create another database connection and store the returned URL immediately.",
|
|
126
|
+
exitCode: 1,
|
|
127
|
+
nextSteps: [`prisma-cli database connection create ${fallbackDatabaseId}`]
|
|
128
|
+
});
|
|
129
|
+
return {
|
|
130
|
+
connection: normalizeConnection(connection, fallbackDatabaseId),
|
|
131
|
+
connectionString
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function normalizeRegion(database) {
|
|
135
|
+
if (typeof database.region === "string") return database.region;
|
|
136
|
+
return database.region?.id ?? database.regionId ?? null;
|
|
137
|
+
}
|
|
138
|
+
function requireDatabaseProjectId(database, fallbackProjectId) {
|
|
139
|
+
const projectId = database.projectId ?? fallbackProjectId;
|
|
140
|
+
if (projectId) return projectId;
|
|
141
|
+
throw new CliError({
|
|
142
|
+
code: "DATABASE_API_ERROR",
|
|
143
|
+
domain: "database",
|
|
144
|
+
summary: "Database response did not include a project id",
|
|
145
|
+
why: "The Management API returned database metadata without project context.",
|
|
146
|
+
fix: "Re-run with --trace for the underlying API response details.",
|
|
147
|
+
exitCode: 1,
|
|
148
|
+
nextSteps: []
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
function extractConnectionString(connection) {
|
|
152
|
+
return connection.endpoints?.pooled?.connectionString ?? connection.connectionString ?? connection.endpoints?.direct?.connectionString ?? connection.endpoints?.accelerate?.connectionString ?? null;
|
|
153
|
+
}
|
|
154
|
+
function databaseApiError(summary, response, error) {
|
|
155
|
+
const status = response?.status ?? 0;
|
|
156
|
+
return new CliError({
|
|
157
|
+
code: error?.error?.code ?? "DATABASE_API_ERROR",
|
|
158
|
+
domain: "database",
|
|
159
|
+
summary,
|
|
160
|
+
why: error?.error?.message ?? `The Management API returned status ${status || "unknown"}.`,
|
|
161
|
+
fix: error?.error?.hint ?? "Re-run with --trace for the underlying API response details.",
|
|
162
|
+
exitCode: 1,
|
|
163
|
+
nextSteps: []
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
//#endregion
|
|
167
|
+
export { createManagementDatabaseProvider, normalizeConnection, normalizeDatabase };
|
package/dist/lib/diagnostics.js
CHANGED
|
@@ -3,7 +3,7 @@ import { resolveStateDir } from "../shell/runtime.js";
|
|
|
3
3
|
import { readLocalGitState } from "./git/local-status.js";
|
|
4
4
|
//#region src/lib/diagnostics.ts
|
|
5
5
|
async function collectCommandDiagnostics(context, options = {}) {
|
|
6
|
-
const stateDir = resolveStateDir(context.runtime);
|
|
6
|
+
const stateDir = await resolveStateDir(context.runtime);
|
|
7
7
|
return {
|
|
8
8
|
cwd: context.runtime.cwd,
|
|
9
9
|
stateFilePath: resolveLocalStateFilePath(stateDir),
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
//#region src/lib/fs/home-path.ts
|
|
3
|
+
/**
|
|
4
|
+
* Shortens a path under the user's home directory to `~/...` for display,
|
|
5
|
+
* posix-style on every platform. Falls back to the Windows home variables
|
|
6
|
+
* when `HOME` is unset (native cmd/PowerShell sessions).
|
|
7
|
+
*/
|
|
8
|
+
function shortenHomePath(value, env) {
|
|
9
|
+
const resolved = path.resolve(value);
|
|
10
|
+
const home = resolveHomeDirectory(env);
|
|
11
|
+
if (home && (resolved === home || resolved.startsWith(`${home}${path.sep}`))) {
|
|
12
|
+
const relative = path.relative(home, resolved).split(path.sep).join("/");
|
|
13
|
+
return relative ? `~/${relative}` : "~";
|
|
14
|
+
}
|
|
15
|
+
return resolved;
|
|
16
|
+
}
|
|
17
|
+
function resolveHomeDirectory(env) {
|
|
18
|
+
if (env.HOME) return path.resolve(env.HOME);
|
|
19
|
+
if (env.USERPROFILE) return path.resolve(env.USERPROFILE);
|
|
20
|
+
if (env.HOMEDRIVE && env.HOMEPATH) return path.resolve(`${env.HOMEDRIVE}${env.HOMEPATH}`);
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
export { shortenHomePath };
|
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
import { access, readFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
//#region src/lib/git/local-branch.ts
|
|
4
|
+
/**
|
|
5
|
+
* Resolves the checked-out branch the way git does: the nearest `.git`
|
|
6
|
+
* (directory or worktree file) from `cwd` upward owns the answer, so
|
|
7
|
+
* monorepo commands run from inside a package see the repository branch.
|
|
8
|
+
* Returns null for detached HEAD or when no repository contains `cwd`.
|
|
9
|
+
*/
|
|
4
10
|
async function readLocalGitBranch(cwd, signal) {
|
|
5
|
-
|
|
6
|
-
|
|
11
|
+
for (let directory = path.resolve(cwd);;) {
|
|
12
|
+
const headPath = await resolveGitHeadPath(path.join(directory, ".git"), signal);
|
|
13
|
+
if (headPath) return readBranchFromHead(headPath, signal);
|
|
14
|
+
const parent = path.dirname(directory);
|
|
15
|
+
if (parent === directory) return null;
|
|
16
|
+
directory = parent;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async function readBranchFromHead(headPath, signal) {
|
|
7
20
|
try {
|
|
8
21
|
const head = (await readFile(headPath, {
|
|
9
22
|
encoding: "utf8",
|
|
@@ -12,7 +25,6 @@ async function readLocalGitBranch(cwd, signal) {
|
|
|
12
25
|
if (head.startsWith("ref: refs/heads/")) return head.slice(16);
|
|
13
26
|
} catch (error) {
|
|
14
27
|
if (signal.aborted) throw error;
|
|
15
|
-
return null;
|
|
16
28
|
}
|
|
17
29
|
return null;
|
|
18
30
|
}
|
|
@@ -1,62 +1,192 @@
|
|
|
1
1
|
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { Result, TaggedError, UnhandledException } from "better-result";
|
|
3
4
|
//#region src/lib/project/local-pin.ts
|
|
4
5
|
const LOCAL_RESOLUTION_PIN_RELATIVE_PATH = ".prisma/local.json";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
var LocalResolutionPinInvalidJsonError = class extends TaggedError("LocalResolutionPinInvalidJsonError")() {
|
|
7
|
+
constructor(cause) {
|
|
8
|
+
super({
|
|
9
|
+
message: `${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} contains invalid JSON.`,
|
|
10
|
+
cause,
|
|
11
|
+
pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
var LocalResolutionPinInvalidShapeError = class extends TaggedError("LocalResolutionPinInvalidShapeError")() {
|
|
16
|
+
constructor() {
|
|
17
|
+
super({
|
|
18
|
+
message: `${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} must contain workspaceId and projectId string fields only.`,
|
|
19
|
+
pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
var LocalResolutionPinReadAbortedError = class extends TaggedError("LocalResolutionPinReadAbortedError")() {
|
|
24
|
+
constructor(cause) {
|
|
25
|
+
super({
|
|
26
|
+
message: `Reading ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} was aborted.`,
|
|
27
|
+
cause,
|
|
28
|
+
pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
var LocalResolutionPinSerializationError = class extends TaggedError("LocalResolutionPinSerializationError")() {
|
|
33
|
+
constructor(cause) {
|
|
34
|
+
super({
|
|
35
|
+
message: `Could not serialize ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH}.`,
|
|
36
|
+
cause,
|
|
37
|
+
pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH
|
|
11
38
|
});
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var LocalResolutionPinWriteAbortedError = class extends TaggedError("LocalResolutionPinWriteAbortedError")() {
|
|
42
|
+
constructor(cause) {
|
|
43
|
+
super({
|
|
44
|
+
message: `Writing ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH} was aborted.`,
|
|
45
|
+
cause,
|
|
46
|
+
pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
var LocalResolutionPinWriteFailedError = class extends TaggedError("LocalResolutionPinWriteFailedError")() {
|
|
51
|
+
constructor(operation, cause) {
|
|
52
|
+
super({
|
|
53
|
+
message: `Could not write ${LOCAL_RESOLUTION_PIN_RELATIVE_PATH}.`,
|
|
54
|
+
cause,
|
|
55
|
+
operation,
|
|
56
|
+
pinPath: LOCAL_RESOLUTION_PIN_RELATIVE_PATH
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
var LocalResolutionPinGitignoreUpdateAbortedError = class extends TaggedError("LocalResolutionPinGitignoreUpdateAbortedError")() {
|
|
61
|
+
constructor(cause) {
|
|
62
|
+
super({
|
|
63
|
+
message: "Updating .gitignore for the local Project binding was aborted.",
|
|
64
|
+
cause,
|
|
65
|
+
gitignorePath: ".gitignore"
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
var LocalResolutionPinGitignoreUpdateFailedError = class extends TaggedError("LocalResolutionPinGitignoreUpdateFailedError")() {
|
|
70
|
+
constructor(operation, cause) {
|
|
71
|
+
super({
|
|
72
|
+
message: "Could not update .gitignore for the local Project binding.",
|
|
73
|
+
cause,
|
|
74
|
+
operation,
|
|
75
|
+
gitignorePath: ".gitignore"
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
async function readLocalResolutionPin(cwd, signal) {
|
|
80
|
+
return Result.gen(async function* () {
|
|
81
|
+
yield* ensureLocalResolutionPinReadNotAborted(signal);
|
|
82
|
+
const file = yield* Result.await(readLocalResolutionPinFile(cwd, signal));
|
|
83
|
+
if (file.kind === "missing") return Result.ok({ kind: "missing" });
|
|
84
|
+
const parsed = yield* parseLocalResolutionPin(file.raw);
|
|
85
|
+
if (!isLocalResolutionPin(parsed)) return Result.err(new LocalResolutionPinInvalidShapeError());
|
|
86
|
+
return Result.ok({
|
|
15
87
|
kind: "present",
|
|
16
88
|
pin: parsed
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
function ensureLocalResolutionPinReadNotAborted(signal) {
|
|
93
|
+
return Result.try({
|
|
94
|
+
try: () => signal?.throwIfAborted(),
|
|
95
|
+
catch: (cause) => new LocalResolutionPinReadAbortedError(cause)
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
async function readLocalResolutionPinFile(cwd, signal) {
|
|
99
|
+
const readResult = await Result.tryPromise({
|
|
100
|
+
try: () => readFile(path.join(cwd, LOCAL_RESOLUTION_PIN_RELATIVE_PATH), {
|
|
101
|
+
encoding: "utf8",
|
|
102
|
+
signal
|
|
103
|
+
}),
|
|
104
|
+
catch: (cause) => signal?.aborted ? new LocalResolutionPinReadAbortedError(cause) : new UnhandledException({ cause })
|
|
105
|
+
});
|
|
106
|
+
if (readResult.isErr()) {
|
|
107
|
+
if (readResult.error instanceof UnhandledException && readResult.error.cause.code === "ENOENT") return Result.ok({ kind: "missing" });
|
|
108
|
+
return Result.err(readResult.error);
|
|
22
109
|
}
|
|
110
|
+
return Result.ok({
|
|
111
|
+
kind: "present",
|
|
112
|
+
raw: readResult.value
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
function parseLocalResolutionPin(raw) {
|
|
116
|
+
return Result.try({
|
|
117
|
+
try: () => JSON.parse(raw),
|
|
118
|
+
catch: (cause) => cause instanceof SyntaxError ? new LocalResolutionPinInvalidJsonError(cause) : new UnhandledException({ cause })
|
|
119
|
+
});
|
|
23
120
|
}
|
|
24
121
|
async function writeLocalResolutionPin(cwd, pin, signal) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
122
|
+
return Result.gen(async function* () {
|
|
123
|
+
const prismaDir = path.join(cwd, ".prisma");
|
|
124
|
+
yield* ensureLocalResolutionPinWriteNotAborted(signal);
|
|
125
|
+
yield* Result.await(writeLocalResolutionPinBoundary(() => mkdir(prismaDir, { recursive: true }), "create-directory", signal));
|
|
126
|
+
const pinPath = path.join(cwd, LOCAL_RESOLUTION_PIN_RELATIVE_PATH);
|
|
127
|
+
const tmpPath = path.join(prismaDir, `local.${process.pid}.${Date.now()}.tmp`);
|
|
128
|
+
const serialized = yield* serializeLocalResolutionPin(pin);
|
|
129
|
+
yield* Result.await(writeLocalResolutionPinBoundary(() => writeFile(tmpPath, serialized, {
|
|
130
|
+
encoding: "utf8",
|
|
131
|
+
signal
|
|
132
|
+
}), "write-temp-file", signal));
|
|
133
|
+
yield* ensureLocalResolutionPinWriteNotAborted(signal);
|
|
134
|
+
yield* Result.await(writeLocalResolutionPinBoundary(() => rename(tmpPath, pinPath), "rename-temp-file", signal));
|
|
135
|
+
return Result.ok(void 0);
|
|
33
136
|
});
|
|
34
|
-
signal?.throwIfAborted();
|
|
35
|
-
await rename(tmpPath, pinPath);
|
|
36
137
|
}
|
|
37
138
|
async function ensureLocalResolutionPinGitignore(cwd, signal) {
|
|
38
139
|
const gitignorePath = path.join(cwd, ".gitignore");
|
|
39
140
|
let existing = null;
|
|
40
|
-
signal
|
|
41
|
-
|
|
42
|
-
|
|
141
|
+
const notAborted = ensureLocalResolutionPinGitignoreUpdateNotAborted(signal);
|
|
142
|
+
if (notAborted.isErr()) return Result.err(notAborted.error);
|
|
143
|
+
const existingResult = await Result.tryPromise({
|
|
144
|
+
try: () => readFile(gitignorePath, {
|
|
43
145
|
encoding: "utf8",
|
|
44
146
|
signal
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
147
|
+
}),
|
|
148
|
+
catch: (cause) => signal?.aborted ? new LocalResolutionPinGitignoreUpdateAbortedError(cause) : new LocalResolutionPinGitignoreUpdateFailedError("read", cause)
|
|
149
|
+
});
|
|
150
|
+
if (existingResult.isErr()) if (existingResult.error instanceof LocalResolutionPinGitignoreUpdateFailedError && existingResult.error.cause.code === "ENOENT") existing = null;
|
|
151
|
+
else return Result.err(existingResult.error);
|
|
152
|
+
else existing = existingResult.value;
|
|
153
|
+
if (existing === null) return writeLocalResolutionPinGitignore(gitignorePath, ".prisma/\n", signal);
|
|
154
|
+
if (existing.split(/\r?\n/).map((line) => line.trim()).some((line) => line === ".prisma/" || line === ".prisma/local.json")) return Result.ok(void 0);
|
|
155
|
+
return writeLocalResolutionPinGitignore(gitignorePath, existing.endsWith("\n") ? `${existing}.prisma/\n` : `${existing}\n.prisma/\n`, signal);
|
|
156
|
+
}
|
|
157
|
+
function ensureLocalResolutionPinWriteNotAborted(signal) {
|
|
158
|
+
return Result.try({
|
|
159
|
+
try: () => signal?.throwIfAborted(),
|
|
160
|
+
catch: (cause) => new LocalResolutionPinWriteAbortedError(cause)
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
function serializeLocalResolutionPin(pin) {
|
|
164
|
+
return Result.try({
|
|
165
|
+
try: () => `${JSON.stringify(pin, null, 2)}\n`,
|
|
166
|
+
catch: (cause) => new LocalResolutionPinSerializationError(cause)
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
function writeLocalResolutionPinBoundary(run, operation, signal) {
|
|
170
|
+
return Result.tryPromise({
|
|
171
|
+
try: async () => {
|
|
172
|
+
await run();
|
|
173
|
+
},
|
|
174
|
+
catch: (cause) => signal?.aborted ? new LocalResolutionPinWriteAbortedError(cause) : new LocalResolutionPinWriteFailedError(operation, cause)
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
function ensureLocalResolutionPinGitignoreUpdateNotAborted(signal) {
|
|
178
|
+
return Result.try({
|
|
179
|
+
try: () => signal?.throwIfAborted(),
|
|
180
|
+
catch: (cause) => new LocalResolutionPinGitignoreUpdateAbortedError(cause)
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
function writeLocalResolutionPinGitignore(gitignorePath, contents, signal) {
|
|
184
|
+
return Result.tryPromise({
|
|
185
|
+
try: () => writeFile(gitignorePath, contents, {
|
|
51
186
|
encoding: "utf8",
|
|
52
187
|
signal
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
if (existing.split(/\r?\n/).map((line) => line.trim()).some((line) => line === ".prisma/" || line === ".prisma/local.json")) return;
|
|
57
|
-
await writeFile(gitignorePath, existing.endsWith("\n") ? `${existing}.prisma/\n` : `${existing}\n.prisma/\n`, {
|
|
58
|
-
encoding: "utf8",
|
|
59
|
-
signal
|
|
188
|
+
}),
|
|
189
|
+
catch: (cause) => signal?.aborted ? new LocalResolutionPinGitignoreUpdateAbortedError(cause) : new LocalResolutionPinGitignoreUpdateFailedError("write", cause)
|
|
60
190
|
});
|
|
61
191
|
}
|
|
62
192
|
function isLocalResolutionPin(value) {
|