@prisma/cli 3.0.0-beta.9 → 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/token-storage.js +311 -33
- package/dist/cli.js +7 -7
- package/dist/cli2.js +3 -3
- package/dist/commands/app/index.js +65 -52
- package/dist/commands/auth/index.js +55 -2
- package/dist/controllers/app-env.js +10 -7
- package/dist/controllers/app.js +536 -207
- package/dist/controllers/auth.js +211 -2
- package/dist/controllers/branch.js +5 -6
- package/dist/controllers/database.js +7 -5
- package/dist/controllers/project.js +33 -18
- 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 +12 -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/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 +106 -26
- package/dist/lib/project/resolution.js +162 -71
- 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 +2 -2
- package/dist/presenters/project.js +3 -9
- package/dist/shell/command-meta.js +52 -2
- package/dist/shell/command-runner.js +39 -28
- 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 +17 -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
|
@@ -1,25 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import { resolveBunEntrypoint } from "./bun-project.js";
|
|
2
|
+
import "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
5
5
|
//#region src/lib/app/local-dev.ts
|
|
6
|
-
const NEXT_CONFIG_FILENAMES = [
|
|
7
|
-
"next.config.js",
|
|
8
|
-
"next.config.mjs",
|
|
9
|
-
"next.config.ts",
|
|
10
|
-
"next.config.mts"
|
|
11
|
-
];
|
|
12
6
|
const DEFAULT_LOCAL_DEV_PORT = 3e3;
|
|
13
|
-
async function resolveLocalBuildType(appPath, buildType, signal) {
|
|
14
|
-
if (buildType === "bun" || buildType === "nextjs") return buildType;
|
|
15
|
-
if (buildType !== "auto") return null;
|
|
16
|
-
return detectLocalBuildType(appPath, signal);
|
|
17
|
-
}
|
|
18
|
-
async function detectLocalBuildType(appPath, signal) {
|
|
19
|
-
if (await isNextProject(appPath, signal)) return "nextjs";
|
|
20
|
-
if (await isBunProject(appPath, signal)) return "bun";
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
7
|
async function runLocalApp(options) {
|
|
24
8
|
const spawnImpl = options.spawnImpl ?? spawn;
|
|
25
9
|
if (options.buildType === "nextjs") {
|
|
@@ -92,47 +76,6 @@ async function runLocalApp(options) {
|
|
|
92
76
|
signal: command.signal
|
|
93
77
|
};
|
|
94
78
|
}
|
|
95
|
-
async function isNextProject(appPath, signal) {
|
|
96
|
-
for (const fileName of NEXT_CONFIG_FILENAMES) {
|
|
97
|
-
signal?.throwIfAborted();
|
|
98
|
-
try {
|
|
99
|
-
await access(path.join(appPath, fileName));
|
|
100
|
-
signal?.throwIfAborted();
|
|
101
|
-
return true;
|
|
102
|
-
} catch (error) {
|
|
103
|
-
if (signal?.aborted) throw error;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return hasDependency(await readBunPackageJson(appPath, signal), "next");
|
|
107
|
-
}
|
|
108
|
-
async function isBunProject(appPath, signal) {
|
|
109
|
-
signal?.throwIfAborted();
|
|
110
|
-
try {
|
|
111
|
-
await access(path.join(appPath, "bun.lock"));
|
|
112
|
-
signal?.throwIfAborted();
|
|
113
|
-
return true;
|
|
114
|
-
} catch (error) {
|
|
115
|
-
if (signal?.aborted) throw error;
|
|
116
|
-
}
|
|
117
|
-
signal?.throwIfAborted();
|
|
118
|
-
try {
|
|
119
|
-
await access(path.join(appPath, "bun.lockb"));
|
|
120
|
-
signal?.throwIfAborted();
|
|
121
|
-
return true;
|
|
122
|
-
} catch (error) {
|
|
123
|
-
if (signal?.aborted) throw error;
|
|
124
|
-
}
|
|
125
|
-
const packageJson = await readBunPackageJson(appPath, signal);
|
|
126
|
-
if (!packageJson) return false;
|
|
127
|
-
const hasEntrypoint = typeof readBunPackageEntrypoint(packageJson) === "string";
|
|
128
|
-
const hasBunDependency = hasDependency(packageJson, "@types/bun") || hasDependency(packageJson, "bun");
|
|
129
|
-
const usesBunScripts = (typeof packageJson.scripts === "object" && packageJson.scripts !== null ? Object.values(packageJson.scripts) : []).some((value) => typeof value === "string" && /\bbun\b/.test(value));
|
|
130
|
-
return hasEntrypoint && (hasBunDependency || usesBunScripts);
|
|
131
|
-
}
|
|
132
|
-
function hasDependency(packageJson, dependencyName) {
|
|
133
|
-
if (!packageJson) return false;
|
|
134
|
-
return [packageJson.dependencies, packageJson.devDependencies].some((group) => typeof group === "object" && group !== null && dependencyName in group);
|
|
135
|
-
}
|
|
136
79
|
async function runWithFallback(candidates, options, spawnImpl, missingCommandMessage) {
|
|
137
80
|
for (const candidate of candidates) try {
|
|
138
81
|
const result = await spawnCommand(candidate, options, spawnImpl);
|
|
@@ -163,4 +106,4 @@ function spawnCommand(candidate, options, spawnImpl) {
|
|
|
163
106
|
});
|
|
164
107
|
}
|
|
165
108
|
//#endregion
|
|
166
|
-
export { DEFAULT_LOCAL_DEV_PORT,
|
|
109
|
+
export { DEFAULT_LOCAL_DEV_PORT, runLocalApp };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CliError } from "../../shell/errors.js";
|
|
2
|
-
import { canPrompt } from "../../shell/runtime.js";
|
|
3
2
|
import { confirmPrompt } from "../../shell/prompt.js";
|
|
3
|
+
import { canPrompt } from "../../shell/runtime.js";
|
|
4
4
|
//#region src/lib/app/production-deploy-gate.ts
|
|
5
5
|
async function enforceProductionDeployGate(context, provider, options) {
|
|
6
6
|
if (options.branchKind !== "production") return { firstProductionDeploy: false };
|
|
@@ -104,12 +104,12 @@ function productionDeployRequiresFlagError() {
|
|
|
104
104
|
"",
|
|
105
105
|
"Production deploys require explicit intent. Re-run with:",
|
|
106
106
|
"",
|
|
107
|
-
" prisma app deploy --prod",
|
|
107
|
+
" prisma-cli app deploy --prod",
|
|
108
108
|
"",
|
|
109
109
|
"Or deploy a preview from a feature branch:",
|
|
110
110
|
"",
|
|
111
111
|
" git checkout -b <branch-name>",
|
|
112
|
-
" prisma app deploy"
|
|
112
|
+
" prisma-cli app deploy"
|
|
113
113
|
]
|
|
114
114
|
});
|
|
115
115
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//#region src/lib/app/read-branch.ts
|
|
2
|
+
/**
|
|
3
|
+
* Resolves the branch an app management command should read from, without ever
|
|
4
|
+
* creating one. Returns the branch whose `gitName` matches `branchName`, else
|
|
5
|
+
* the project's default branch, else null when the project has no branches.
|
|
6
|
+
*/
|
|
7
|
+
async function resolveReadBranch(client, options) {
|
|
8
|
+
const branches = [];
|
|
9
|
+
let cursor;
|
|
10
|
+
do {
|
|
11
|
+
const result = await client.GET("/v1/projects/{projectId}/branches", {
|
|
12
|
+
params: {
|
|
13
|
+
path: { projectId: options.projectId },
|
|
14
|
+
query: { cursor }
|
|
15
|
+
},
|
|
16
|
+
signal: options.signal
|
|
17
|
+
});
|
|
18
|
+
if (result.error || !result.data) throw new Error(`Failed to list branches for project ${options.projectId}: ${JSON.stringify(result.error)}`);
|
|
19
|
+
branches.push(...result.data.data);
|
|
20
|
+
cursor = result.data.pagination.hasMore ? result.data.pagination.nextCursor ?? void 0 : void 0;
|
|
21
|
+
} while (cursor);
|
|
22
|
+
const chosen = branches.find((branch) => branch.gitName === options.branchName) ?? branches.find((branch) => branch.isDefault) ?? null;
|
|
23
|
+
return chosen ? {
|
|
24
|
+
id: chosen.id,
|
|
25
|
+
name: chosen.gitName,
|
|
26
|
+
kind: chosen.role
|
|
27
|
+
} : null;
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
export { resolveReadBranch };
|
|
@@ -26,7 +26,7 @@ function workspaceIdFromClaims(claims) {
|
|
|
26
26
|
}
|
|
27
27
|
async function performLogin(env, signal) {
|
|
28
28
|
await login({
|
|
29
|
-
tokenStorage: new FileTokenStorage(env, signal),
|
|
29
|
+
tokenStorage: new FileTokenStorage(env, signal, { activateOnSetTokens: true }),
|
|
30
30
|
env,
|
|
31
31
|
signal
|
|
32
32
|
});
|
|
@@ -38,7 +38,8 @@ async function readAuthState(env, signal) {
|
|
|
38
38
|
if (serviceToken.length === 0) throw new Error(`${SERVICE_TOKEN_ENV_VAR} is set but empty. Provide a valid token or unset the variable.`);
|
|
39
39
|
return readServiceTokenAuthState(serviceToken, env, signal);
|
|
40
40
|
}
|
|
41
|
-
const
|
|
41
|
+
const tokenStorage = new FileTokenStorage(env, signal);
|
|
42
|
+
const tokens = await tokenStorage.getTokens();
|
|
42
43
|
if (!tokens) return {
|
|
43
44
|
authenticated: false,
|
|
44
45
|
provider: null,
|
|
@@ -48,15 +49,20 @@ async function readAuthState(env, signal) {
|
|
|
48
49
|
};
|
|
49
50
|
const client = await requireComputeAuth(env, signal);
|
|
50
51
|
const currentPrincipal = await readCurrentPrincipalAuthState(client, signal);
|
|
51
|
-
if (currentPrincipal)
|
|
52
|
+
if (currentPrincipal) {
|
|
53
|
+
if (currentPrincipal.authenticated && currentPrincipal.workspace) await tokenStorage.rememberWorkspace?.(tokens.workspaceId, currentPrincipal.workspace);
|
|
54
|
+
return currentPrincipal;
|
|
55
|
+
}
|
|
52
56
|
const claims = decodeJwtPayload(tokens.accessToken);
|
|
53
|
-
|
|
57
|
+
const authState = await buildAuthState({
|
|
54
58
|
workspaceIdFromCredential: tokens.workspaceId,
|
|
55
59
|
claims,
|
|
56
60
|
env,
|
|
57
61
|
client,
|
|
58
62
|
signal
|
|
59
63
|
});
|
|
64
|
+
if (authState.authenticated && authState.workspace) await tokenStorage.rememberWorkspace?.(tokens.workspaceId, authState.workspace);
|
|
65
|
+
return authState;
|
|
60
66
|
}
|
|
61
67
|
async function readServiceTokenAuthState(token, env, signal) {
|
|
62
68
|
const client = await requireComputeAuth(env, signal);
|
package/dist/lib/auth/guard.js
CHANGED
|
@@ -22,7 +22,10 @@ async function requireComputeAuth(env = process.env, signal) {
|
|
|
22
22
|
token
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
|
-
const tokenStorage = new FileTokenStorage(env, signal
|
|
25
|
+
const tokenStorage = new FileTokenStorage(env, signal, {
|
|
26
|
+
activateOnSetTokens: false,
|
|
27
|
+
lockSetTokens: false
|
|
28
|
+
});
|
|
26
29
|
if (!await tokenStorage.getTokens()) return null;
|
|
27
30
|
return createManagementApiSdk({
|
|
28
31
|
clientId: CLIENT_ID,
|
package/dist/lib/auth/login.js
CHANGED
|
@@ -106,35 +106,42 @@ async function consumePastedCallback(options) {
|
|
|
106
106
|
});
|
|
107
107
|
try {
|
|
108
108
|
for (;;) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (error?.name === "AbortError") return;
|
|
114
|
-
throw error;
|
|
115
|
-
}
|
|
116
|
-
const trimmed = answer.trim().replace(/^["']|["']$/g, "");
|
|
117
|
-
let url;
|
|
118
|
-
try {
|
|
119
|
-
if (!trimmed) throw new Error("empty input");
|
|
120
|
-
url = new URL(trimmed);
|
|
121
|
-
} catch {
|
|
122
|
-
options.output.write("That didn't look like a URL. Paste the full localhost callback URL and try again.\n");
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
try {
|
|
126
|
-
await options.complete(url);
|
|
127
|
-
return;
|
|
128
|
-
} catch (error) {
|
|
129
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
130
|
-
options.output.write(`Sign-in didn't complete (${message}). Paste the callback URL to try again.\n`);
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
109
|
+
const url = await readPastedCallbackUrl(rl, options);
|
|
110
|
+
if (url === null) return;
|
|
111
|
+
if (url === void 0) continue;
|
|
112
|
+
if (await tryCompletePastedCallback(url, options)) return;
|
|
133
113
|
}
|
|
134
114
|
} finally {
|
|
135
115
|
rl.close();
|
|
136
116
|
}
|
|
137
117
|
}
|
|
118
|
+
async function readPastedCallbackUrl(rl, options) {
|
|
119
|
+
let answer;
|
|
120
|
+
try {
|
|
121
|
+
answer = await rl.question("Paste the callback URL here: ", { signal: options.signal });
|
|
122
|
+
} catch (error) {
|
|
123
|
+
if (error?.name === "AbortError") return null;
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
const trimmed = answer.trim().replace(/^["']|["']$/g, "");
|
|
127
|
+
try {
|
|
128
|
+
if (!trimmed) throw new Error("empty input");
|
|
129
|
+
return new URL(trimmed);
|
|
130
|
+
} catch {
|
|
131
|
+
options.output.write("That didn't look like a URL. Paste the full localhost callback URL and try again.\n");
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async function tryCompletePastedCallback(url, options) {
|
|
136
|
+
try {
|
|
137
|
+
await options.complete(url);
|
|
138
|
+
return true;
|
|
139
|
+
} catch (error) {
|
|
140
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
141
|
+
options.output.write(`Sign-in didn't complete (${message}). Paste the callback URL to try again.\n`);
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
138
145
|
var LoginState = class {
|
|
139
146
|
latestVerifier;
|
|
140
147
|
latestState;
|
|
@@ -144,7 +151,7 @@ var LoginState = class {
|
|
|
144
151
|
output;
|
|
145
152
|
constructor(options) {
|
|
146
153
|
this.options = options;
|
|
147
|
-
this.tokenStorage = options.tokenStorage ?? new FileTokenStorage(options.env, options.signal);
|
|
154
|
+
this.tokenStorage = options.tokenStorage ?? new FileTokenStorage(options.env, options.signal, { activateOnSetTokens: true });
|
|
148
155
|
this.sdk = createManagementApiSdk({
|
|
149
156
|
clientId: options.clientId ?? "cmm3lndn701oo0uefvxzo0ivw",
|
|
150
157
|
redirectUri: `http://${options.hostname}:${options.port}/auth/callback`,
|
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
|
}
|
|
@@ -29,6 +29,53 @@ var LocalResolutionPinReadAbortedError = class extends TaggedError("LocalResolut
|
|
|
29
29
|
});
|
|
30
30
|
}
|
|
31
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
|
|
38
|
+
});
|
|
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
|
+
};
|
|
32
79
|
async function readLocalResolutionPin(cwd, signal) {
|
|
33
80
|
return Result.gen(async function* () {
|
|
34
81
|
yield* ensureLocalResolutionPinReadNotAborted(signal);
|
|
@@ -72,41 +119,74 @@ function parseLocalResolutionPin(raw) {
|
|
|
72
119
|
});
|
|
73
120
|
}
|
|
74
121
|
async function writeLocalResolutionPin(cwd, pin, signal) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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);
|
|
83
136
|
});
|
|
84
|
-
signal?.throwIfAborted();
|
|
85
|
-
await rename(tmpPath, pinPath);
|
|
86
137
|
}
|
|
87
138
|
async function ensureLocalResolutionPinGitignore(cwd, signal) {
|
|
88
139
|
const gitignorePath = path.join(cwd, ".gitignore");
|
|
89
140
|
let existing = null;
|
|
90
|
-
signal
|
|
91
|
-
|
|
92
|
-
|
|
141
|
+
const notAborted = ensureLocalResolutionPinGitignoreUpdateNotAborted(signal);
|
|
142
|
+
if (notAborted.isErr()) return Result.err(notAborted.error);
|
|
143
|
+
const existingResult = await Result.tryPromise({
|
|
144
|
+
try: () => readFile(gitignorePath, {
|
|
93
145
|
encoding: "utf8",
|
|
94
146
|
signal
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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, {
|
|
101
186
|
encoding: "utf8",
|
|
102
187
|
signal
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
if (existing.split(/\r?\n/).map((line) => line.trim()).some((line) => line === ".prisma/" || line === ".prisma/local.json")) return;
|
|
107
|
-
await writeFile(gitignorePath, existing.endsWith("\n") ? `${existing}.prisma/\n` : `${existing}\n.prisma/\n`, {
|
|
108
|
-
encoding: "utf8",
|
|
109
|
-
signal
|
|
188
|
+
}),
|
|
189
|
+
catch: (cause) => signal?.aborted ? new LocalResolutionPinGitignoreUpdateAbortedError(cause) : new LocalResolutionPinGitignoreUpdateFailedError("write", cause)
|
|
110
190
|
});
|
|
111
191
|
}
|
|
112
192
|
function isLocalResolutionPin(value) {
|