@prisma/cli 8.0.0-rc.2-dev.49 → 8.0.0-rc.2-dev.51
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/cli.js +217 -49
- package/package.json +4 -4
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import process$1 from "node:process";
|
|
4
|
-
import { SERVICE_TOKEN_ENV_VAR, claimedExpiresAt, claimedIdentity, createCli, credentialWorkspaceId, credentialWorkspaceMismatchError, credentialsRequiredError, defineCommand, defineCommandFamily, defineSessionCommand, emptyServiceTokenError, flag, loadConfig, noSessionForWorkspaceError, positional, telemetryCommandGroup } from "@prisma/cli-engine";
|
|
4
|
+
import { SERVICE_TOKEN_ENV_VAR, claimedExpiresAt, claimedIdentity, createCli, credentialWorkspaceId, credentialWorkspaceMismatchError, credentialsRequiredError, defineCommand, defineCommandFamily, defineSessionCommand, emptyServiceTokenError, flag, loadConfig, noSessionForWorkspaceError, positional, readActiveAccessToken, telemetryCommandGroup } from "@prisma/cli-engine";
|
|
5
5
|
import { createComposerFamily } from "@prisma/composer/family";
|
|
6
6
|
import { ormCommandFamily } from "@prisma/orm-toolchain/cli";
|
|
7
7
|
import { CliStructuredError, notOk, ok } from "@prisma/cli-engine/protocol";
|
|
@@ -24,6 +24,8 @@ import { promisify } from "node:util";
|
|
|
24
24
|
import { ApiError, CancelledError, ComputeClient, normalizeArtifactSymlinks, resolveBuildStrategy, streamLogs } from "@prisma/compute-sdk";
|
|
25
25
|
import { parse } from "dotenv";
|
|
26
26
|
import { fileURLToPath } from "node:url";
|
|
27
|
+
import { Writable } from "node:stream";
|
|
28
|
+
import { pipeline } from "node:stream/promises";
|
|
27
29
|
//#region src/cli-name.ts
|
|
28
30
|
/**
|
|
29
31
|
* The CLI's user-facing identity, in one place. The npm package is
|
|
@@ -848,9 +850,9 @@ function sameWorkspaceId(left, right) {
|
|
|
848
850
|
}
|
|
849
851
|
//#endregion
|
|
850
852
|
//#region src/auth/token-storage.ts
|
|
851
|
-
const REFRESH_LOCK_RETRY_MS = 100;
|
|
852
|
-
const REFRESH_LOCK_STALE_MS = 3e4;
|
|
853
|
-
const REFRESH_LOCK_WAIT_TIMEOUT_MS = 25e3;
|
|
853
|
+
const REFRESH_LOCK_RETRY_MS$1 = 100;
|
|
854
|
+
const REFRESH_LOCK_STALE_MS$1 = 3e4;
|
|
855
|
+
const REFRESH_LOCK_WAIT_TIMEOUT_MS$1 = 25e3;
|
|
854
856
|
const EMPTY_AUTH_CONTEXT = {
|
|
855
857
|
activeWorkspaceId: null,
|
|
856
858
|
workspaces: {}
|
|
@@ -1097,8 +1099,8 @@ var FileTokenStorage = class {
|
|
|
1097
1099
|
async acquireRefreshLock() {
|
|
1098
1100
|
const lockId = randomUUID();
|
|
1099
1101
|
const startedAt = Date.now();
|
|
1100
|
-
const retryMs = this.options.lockRetryMs ?? REFRESH_LOCK_RETRY_MS;
|
|
1101
|
-
const waitTimeoutMs = this.options.lockWaitTimeoutMs ?? REFRESH_LOCK_WAIT_TIMEOUT_MS;
|
|
1102
|
+
const retryMs = this.options.lockRetryMs ?? REFRESH_LOCK_RETRY_MS$1;
|
|
1103
|
+
const waitTimeoutMs = this.options.lockWaitTimeoutMs ?? REFRESH_LOCK_WAIT_TIMEOUT_MS$1;
|
|
1102
1104
|
this.signal?.throwIfAborted();
|
|
1103
1105
|
await fs.mkdir(path.dirname(this.lockFilePath), { recursive: true });
|
|
1104
1106
|
while (true) {
|
|
@@ -1152,7 +1154,7 @@ var FileTokenStorage = class {
|
|
|
1152
1154
|
const stats = await fs.stat(this.lockFilePath).catch(() => null);
|
|
1153
1155
|
this.signal?.throwIfAborted();
|
|
1154
1156
|
if (!stats) return null;
|
|
1155
|
-
const staleMs = this.options.lockStaleMs ?? REFRESH_LOCK_STALE_MS;
|
|
1157
|
+
const staleMs = this.options.lockStaleMs ?? REFRESH_LOCK_STALE_MS$1;
|
|
1156
1158
|
return Date.now() - stats.mtimeMs > staleMs ? lockId : null;
|
|
1157
1159
|
}
|
|
1158
1160
|
async releaseRefreshLock(lockId) {
|
|
@@ -14315,6 +14317,19 @@ const FILE_MODE = 384;
|
|
|
14315
14317
|
const LOCK_STALE_MS = 5e3;
|
|
14316
14318
|
const LOCK_RETRY_MS = 10;
|
|
14317
14319
|
const LOCK_WAIT_TIMEOUT_MS = 1e4;
|
|
14320
|
+
const REFRESH_LOCK_STALE_MS = 3e4;
|
|
14321
|
+
const REFRESH_LOCK_RETRY_MS = 100;
|
|
14322
|
+
const REFRESH_LOCK_WAIT_TIMEOUT_MS = 3e4;
|
|
14323
|
+
const STATE_LOCK_TIMINGS = {
|
|
14324
|
+
staleMs: LOCK_STALE_MS,
|
|
14325
|
+
retryMs: LOCK_RETRY_MS,
|
|
14326
|
+
waitTimeoutMs: LOCK_WAIT_TIMEOUT_MS
|
|
14327
|
+
};
|
|
14328
|
+
const REFRESH_LOCK_TIMINGS = {
|
|
14329
|
+
staleMs: REFRESH_LOCK_STALE_MS,
|
|
14330
|
+
retryMs: REFRESH_LOCK_RETRY_MS,
|
|
14331
|
+
waitTimeoutMs: REFRESH_LOCK_WAIT_TIMEOUT_MS
|
|
14332
|
+
};
|
|
14318
14333
|
const EMPTY_STATE = {
|
|
14319
14334
|
version: 1,
|
|
14320
14335
|
sessions: [],
|
|
@@ -14417,9 +14432,9 @@ async function writeCredentialState(filePath, state) {
|
|
|
14417
14432
|
await fs.chmod(filePath, FILE_MODE).catch(() => {});
|
|
14418
14433
|
}
|
|
14419
14434
|
var StateLockTimeoutError = class extends CliStructuredError {
|
|
14420
|
-
constructor(lockPath) {
|
|
14435
|
+
constructor(lockPath, waitTimeoutMs) {
|
|
14421
14436
|
super("CLI.CREDENTIALS_LOCKED", "Another prisma process is still updating your credentials.", {
|
|
14422
|
-
why: `The credentials lock at ${lockPath} was held for longer than ${
|
|
14437
|
+
why: `The credentials lock at ${lockPath} was held for longer than ${waitTimeoutMs}ms.`,
|
|
14423
14438
|
nextActions: [{
|
|
14424
14439
|
kind: "user-choice",
|
|
14425
14440
|
label: "Wait for the other command to finish, then try again."
|
|
@@ -14435,8 +14450,20 @@ var StateLockTimeoutError = class extends CliStructuredError {
|
|
|
14435
14450
|
* small fixed staleness threshold.
|
|
14436
14451
|
*/
|
|
14437
14452
|
async function withStateLock(filePath, debug, run) {
|
|
14438
|
-
|
|
14439
|
-
|
|
14453
|
+
return withFileLock(`${filePath}.lock`, debug, STATE_LOCK_TIMINGS, run);
|
|
14454
|
+
}
|
|
14455
|
+
/**
|
|
14456
|
+
* The cross-process lock the delegated refresh holds for its whole
|
|
14457
|
+
* read → exchange → write sequence, so two processes never spend the
|
|
14458
|
+
* same refresh token. Distinct from the state lock: it IS held across
|
|
14459
|
+
* network I/O, so its staleness and wait budgets are larger, and it
|
|
14460
|
+
* uses its own lock path so short mutations are not queued behind it.
|
|
14461
|
+
*/
|
|
14462
|
+
async function withRefreshFileLock(filePath, debug, run) {
|
|
14463
|
+
return withFileLock(`${filePath}.refresh-lock`, debug, REFRESH_LOCK_TIMINGS, run);
|
|
14464
|
+
}
|
|
14465
|
+
async function withFileLock(lockPath, debug, timings, run) {
|
|
14466
|
+
const lockId = await acquireStateLock(lockPath, debug, timings);
|
|
14440
14467
|
debug(`lock acquired ${lockPath}`);
|
|
14441
14468
|
try {
|
|
14442
14469
|
return await run();
|
|
@@ -14445,15 +14472,15 @@ async function withStateLock(filePath, debug, run) {
|
|
|
14445
14472
|
debug(`lock released ${lockPath}`);
|
|
14446
14473
|
}
|
|
14447
14474
|
}
|
|
14448
|
-
async function acquireStateLock(lockPath, debug) {
|
|
14475
|
+
async function acquireStateLock(lockPath, debug, timings) {
|
|
14449
14476
|
const lockId = randomUUID();
|
|
14450
14477
|
const startedAt = Date.now();
|
|
14451
14478
|
await fs.mkdir(path.dirname(lockPath), { recursive: true });
|
|
14452
14479
|
while (true) {
|
|
14453
14480
|
if (await tryCreateStateLock(lockPath, lockId)) return lockId;
|
|
14454
|
-
const tookOver = await takeOverStaleStateLock(lockPath, debug);
|
|
14455
|
-
if (Date.now() - startedAt >=
|
|
14456
|
-
if (!tookOver) await new Promise((resolve) => setTimeout(resolve,
|
|
14481
|
+
const tookOver = await takeOverStaleStateLock(lockPath, debug, timings);
|
|
14482
|
+
if (Date.now() - startedAt >= timings.waitTimeoutMs) throw new StateLockTimeoutError(lockPath, timings.waitTimeoutMs);
|
|
14483
|
+
if (!tookOver) await new Promise((resolve) => setTimeout(resolve, timings.retryMs));
|
|
14457
14484
|
}
|
|
14458
14485
|
}
|
|
14459
14486
|
async function tryCreateStateLock(lockPath, lockId) {
|
|
@@ -14479,10 +14506,10 @@ async function tryCreateStateLock(lockPath, lockId) {
|
|
|
14479
14506
|
* then run their read-modify-write at once and one update is lost,
|
|
14480
14507
|
* which is the very thing the lock exists to prevent.
|
|
14481
14508
|
*/
|
|
14482
|
-
async function takeOverStaleStateLock(lockPath, debug) {
|
|
14509
|
+
async function takeOverStaleStateLock(lockPath, debug, timings) {
|
|
14483
14510
|
const stale = await fs.stat(lockPath).catch(() => null);
|
|
14484
14511
|
if (stale === null) return true;
|
|
14485
|
-
if (Date.now() - stale.mtimeMs <=
|
|
14512
|
+
if (Date.now() - stale.mtimeMs <= timings.staleMs) return false;
|
|
14486
14513
|
const takenPath = `${lockPath}.${randomUUID()}.stale`;
|
|
14487
14514
|
try {
|
|
14488
14515
|
await fs.rename(lockPath, takenPath);
|
|
@@ -14520,12 +14547,16 @@ function memoryBackedStorage(credential, withRefreshLock) {
|
|
|
14520
14547
|
let tokens = {
|
|
14521
14548
|
workspaceId: credentialWorkspaceId(credential.token) ?? NO_WORKSPACE_CLAIMED,
|
|
14522
14549
|
accessToken: credential.token,
|
|
14523
|
-
refreshToken: credential.refreshToken
|
|
14550
|
+
refreshToken: credential.refreshToken,
|
|
14551
|
+
expiresAt: claimedExpiresAt(credential.token) ?? credential.expiresAt
|
|
14524
14552
|
};
|
|
14525
14553
|
return {
|
|
14526
14554
|
getTokens: async () => tokens,
|
|
14527
|
-
setTokens: async (rotated) => {
|
|
14528
|
-
tokens =
|
|
14555
|
+
setTokens: async (rotated, expiresAt) => {
|
|
14556
|
+
tokens = {
|
|
14557
|
+
...rotated,
|
|
14558
|
+
expiresAt: claimedExpiresAt(rotated.accessToken) ?? expiresAt ?? tokens?.expiresAt
|
|
14559
|
+
};
|
|
14529
14560
|
},
|
|
14530
14561
|
clearTokens: async () => {
|
|
14531
14562
|
tokens = null;
|
|
@@ -14544,6 +14575,7 @@ var FileCredentialManager = class {
|
|
|
14544
14575
|
#filePath;
|
|
14545
14576
|
#debug;
|
|
14546
14577
|
#fetchWorkspaceName;
|
|
14578
|
+
#refreshCredential;
|
|
14547
14579
|
#pin = { kind: "unresolved" };
|
|
14548
14580
|
/** Built for one pinned credential. Every mutation that moves the
|
|
14549
14581
|
* pin discards it, so a command that mutates and then reaches for
|
|
@@ -14556,6 +14588,7 @@ var FileCredentialManager = class {
|
|
|
14556
14588
|
this.#filePath = resolveStateFilePath(options.env).filePath;
|
|
14557
14589
|
this.#debug = makeDebugLog(options.env, options.debugWrite);
|
|
14558
14590
|
this.#fetchWorkspaceName = options.fetchWorkspaceName;
|
|
14591
|
+
this.#refreshCredential = options.refreshCredential;
|
|
14559
14592
|
this.#debug(`state file ${this.#filePath}`);
|
|
14560
14593
|
}
|
|
14561
14594
|
get stateFilePath() {
|
|
@@ -14666,14 +14699,13 @@ var FileCredentialManager = class {
|
|
|
14666
14699
|
this.#activeStorage ??= this.#buildActiveStorage();
|
|
14667
14700
|
return this.#activeStorage;
|
|
14668
14701
|
}
|
|
14669
|
-
/** The
|
|
14702
|
+
/** The delegated path's read: the active credential's access token,
|
|
14670
14703
|
* fresh on every call, never the refresh token. Null when there is
|
|
14671
14704
|
* no active credential to read — storage exists only once
|
|
14672
14705
|
* activeCredential() has returned non-null. */
|
|
14673
|
-
async activeAccessToken() {
|
|
14706
|
+
async activeAccessToken(options) {
|
|
14674
14707
|
if (await this.activeCredential() === null) return null;
|
|
14675
|
-
|
|
14676
|
-
return tokens === null ? null : tokens.accessToken;
|
|
14708
|
+
return readActiveAccessToken(await this.activeCredentialStorage(), this.#refreshCredential, options);
|
|
14677
14709
|
}
|
|
14678
14710
|
/** §11.2: which storage is chosen once, when the pin resolves. Each
|
|
14679
14711
|
* has exactly one source of truth — the file, or process memory. */
|
|
@@ -14701,10 +14733,11 @@ var FileCredentialManager = class {
|
|
|
14701
14733
|
return {
|
|
14702
14734
|
workspaceId,
|
|
14703
14735
|
accessToken: record.token,
|
|
14704
|
-
...record.refreshToken === void 0 ? {} : { refreshToken: record.refreshToken }
|
|
14736
|
+
...record.refreshToken === void 0 ? {} : { refreshToken: record.refreshToken },
|
|
14737
|
+
...record.expiresAt === void 0 ? {} : { expiresAt: new Date(record.expiresAt) }
|
|
14705
14738
|
};
|
|
14706
14739
|
},
|
|
14707
|
-
setTokens: async (tokens) => {
|
|
14740
|
+
setTokens: async (tokens, expiresAt) => {
|
|
14708
14741
|
this.#debug(`rotation write for session ${workspaceId}`);
|
|
14709
14742
|
const claimed = credentialWorkspaceId(tokens.accessToken);
|
|
14710
14743
|
if (claimed !== void 0 && claimed !== workspaceId) throw credentialWorkspaceMismatchError(workspaceId);
|
|
@@ -14716,7 +14749,7 @@ var FileCredentialManager = class {
|
|
|
14716
14749
|
...record.name === void 0 ? {} : { name: record.name },
|
|
14717
14750
|
token: tokens.accessToken,
|
|
14718
14751
|
...tokens.refreshToken === void 0 ? {} : { refreshToken: tokens.refreshToken },
|
|
14719
|
-
...expiresAtSlice(tokens.accessToken, void 0)
|
|
14752
|
+
...expiresAtSlice(tokens.accessToken, expiresAt ?? (record.expiresAt === void 0 ? void 0 : new Date(record.expiresAt)))
|
|
14720
14753
|
};
|
|
14721
14754
|
return {
|
|
14722
14755
|
state: {
|
|
@@ -14745,7 +14778,7 @@ var FileCredentialManager = class {
|
|
|
14745
14778
|
};
|
|
14746
14779
|
});
|
|
14747
14780
|
},
|
|
14748
|
-
withRefreshLock: (fn) => this.#withRefreshLock(fn)
|
|
14781
|
+
withRefreshLock: (fn) => this.#withRefreshLock(() => withRefreshFileLock(this.#filePath, this.#debug, fn))
|
|
14749
14782
|
};
|
|
14750
14783
|
}
|
|
14751
14784
|
#withRefreshLock(fn) {
|
|
@@ -14881,6 +14914,45 @@ function environmentCredential(token) {
|
|
|
14881
14914
|
};
|
|
14882
14915
|
}
|
|
14883
14916
|
//#endregion
|
|
14917
|
+
//#region src/auth/refresh.ts
|
|
14918
|
+
const TRAILING_SLASH = /\/$/;
|
|
14919
|
+
const CREDENTIAL_REFRESH_TIMEOUT_MS = 1e4;
|
|
14920
|
+
/** The dumb HTTP adapter behind the engine's delegated-credential policy. */
|
|
14921
|
+
function makeCredentialRefresher(authBaseUrl) {
|
|
14922
|
+
const endpoint = `${authBaseUrl.replace(TRAILING_SLASH, "")}/token`;
|
|
14923
|
+
return async ({ refreshToken, signal }) => {
|
|
14924
|
+
signal.throwIfAborted();
|
|
14925
|
+
const refreshSignal = AbortSignal.any([signal, AbortSignal.timeout(CREDENTIAL_REFRESH_TIMEOUT_MS)]);
|
|
14926
|
+
const response = await fetch(endpoint, {
|
|
14927
|
+
method: "POST",
|
|
14928
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
14929
|
+
body: new URLSearchParams({
|
|
14930
|
+
grant_type: "refresh_token",
|
|
14931
|
+
refresh_token: refreshToken,
|
|
14932
|
+
client_id: CLIENT_ID
|
|
14933
|
+
}),
|
|
14934
|
+
signal: refreshSignal
|
|
14935
|
+
});
|
|
14936
|
+
const body = await readBody(response);
|
|
14937
|
+
if (response.status >= 400 && response.status < 500 && body?.error === "invalid_grant") return { kind: "invalid" };
|
|
14938
|
+
if (!response.ok || typeof body?.access_token !== "string" || typeof body.refresh_token !== "string" || typeof body.expires_in !== "number" || !Number.isFinite(body.expires_in) || body.expires_in < 0) throw new Error(`OAuth token refresh failed (status ${String(response.status)})`);
|
|
14939
|
+
return {
|
|
14940
|
+
kind: "success",
|
|
14941
|
+
accessToken: body.access_token,
|
|
14942
|
+
refreshToken: body.refresh_token,
|
|
14943
|
+
expiresAt: new Date(Date.now() + body.expires_in * 1e3)
|
|
14944
|
+
};
|
|
14945
|
+
};
|
|
14946
|
+
}
|
|
14947
|
+
async function readBody(response) {
|
|
14948
|
+
try {
|
|
14949
|
+
const body = await response.json();
|
|
14950
|
+
return typeof body === "object" && body !== null ? body : null;
|
|
14951
|
+
} catch {
|
|
14952
|
+
return null;
|
|
14953
|
+
}
|
|
14954
|
+
}
|
|
14955
|
+
//#endregion
|
|
14884
14956
|
//#region src/auth/workspace-name.ts
|
|
14885
14957
|
/** The manager's injected name lookup: a static-token client over the
|
|
14886
14958
|
* credential just minted. The manager constructs no API client and
|
|
@@ -14994,33 +15066,127 @@ const runPackageManager = async ({ file, args, cwd, signal, onOutput }) => {
|
|
|
14994
15066
|
};
|
|
14995
15067
|
//#endregion
|
|
14996
15068
|
//#region src/spawn.ts
|
|
15069
|
+
/** How long after the child exits the relay keeps reading its pipes. A
|
|
15070
|
+
* grandchild that inherited them can hold EOF back forever; settlement
|
|
15071
|
+
* must not wait on it, so the pipes are destroyed after this grace. */
|
|
15072
|
+
const POST_EXIT_DRAIN_GRACE_MS = 5e3;
|
|
14997
15073
|
/**
|
|
14998
|
-
* The engine's spawn seam, adapted to node:child_process.
|
|
14999
|
-
* stdio
|
|
15000
|
-
*
|
|
15001
|
-
*
|
|
15074
|
+
* The engine's spawn seam, adapted to node:child_process. Human mode
|
|
15075
|
+
* inherits stdio; structured mode pipes both child output streams to
|
|
15076
|
+
* diagnostics. Neither mode detaches or opens a new console, so the child
|
|
15077
|
+
* stays in this process's group (POSIX) or console (Windows).
|
|
15078
|
+
*
|
|
15079
|
+
* The child's own status settles the run: `ended` resolves from the
|
|
15080
|
+
* process `exit` event, waits for the diagnostic relay only up to the
|
|
15081
|
+
* drain grace, and never rejects for a relay failure — rejection is
|
|
15082
|
+
* reserved for a child that could not be launched at all.
|
|
15002
15083
|
*/
|
|
15003
|
-
|
|
15004
|
-
const
|
|
15005
|
-
|
|
15006
|
-
|
|
15007
|
-
|
|
15008
|
-
|
|
15009
|
-
|
|
15010
|
-
|
|
15084
|
+
function makeSpawnChild(diagnostics, options) {
|
|
15085
|
+
const drainGraceMs = options?.drainGraceMs ?? POST_EXIT_DRAIN_GRACE_MS;
|
|
15086
|
+
return (request) => {
|
|
15087
|
+
const structured = request.output === "diagnostic";
|
|
15088
|
+
const child = spawn(request.command, [...request.args], {
|
|
15089
|
+
cwd: request.cwd,
|
|
15090
|
+
env: request.env,
|
|
15091
|
+
stdio: structured ? [
|
|
15092
|
+
"inherit",
|
|
15093
|
+
"pipe",
|
|
15094
|
+
"pipe"
|
|
15095
|
+
] : "inherit"
|
|
15096
|
+
});
|
|
15097
|
+
const processEnded = new Promise((resolve, reject) => {
|
|
15011
15098
|
child.on("error", reject);
|
|
15012
|
-
child.on("
|
|
15099
|
+
child.on("exit", (exitCode, signal) => {
|
|
15013
15100
|
resolve({
|
|
15014
15101
|
exitCode,
|
|
15015
15102
|
signal
|
|
15016
15103
|
});
|
|
15017
15104
|
});
|
|
15018
|
-
})
|
|
15019
|
-
|
|
15020
|
-
|
|
15021
|
-
|
|
15105
|
+
});
|
|
15106
|
+
if (!structured) return {
|
|
15107
|
+
ended: processEnded,
|
|
15108
|
+
kill: (signal) => {
|
|
15109
|
+
child.kill(signal);
|
|
15110
|
+
}
|
|
15111
|
+
};
|
|
15112
|
+
const forwarding = forwardStructuredOutput(child.stdout, child.stderr, diagnostics);
|
|
15113
|
+
return {
|
|
15114
|
+
ended: processEnded.then(async (result) => {
|
|
15115
|
+
const drainDeadline = setTimeout(() => {
|
|
15116
|
+
child.stdout?.destroy();
|
|
15117
|
+
child.stderr?.destroy();
|
|
15118
|
+
}, drainGraceMs);
|
|
15119
|
+
await forwarding;
|
|
15120
|
+
clearTimeout(drainDeadline);
|
|
15121
|
+
return result;
|
|
15122
|
+
}),
|
|
15123
|
+
kill: (signal) => {
|
|
15124
|
+
child.kill(signal);
|
|
15125
|
+
}
|
|
15126
|
+
};
|
|
15022
15127
|
};
|
|
15023
|
-
}
|
|
15128
|
+
}
|
|
15129
|
+
/** Best-effort relay: a forwarding failure never rejects, so the child's
|
|
15130
|
+
* real status still settles the run when the diagnostic sink dies. */
|
|
15131
|
+
function forwardStructuredOutput(stdout, stderr, diagnostics) {
|
|
15132
|
+
const sources = [stdout, stderr].filter((source) => source !== null);
|
|
15133
|
+
return Promise.all(sources.map((source) => forwardOutput(source, diagnostics))).then(() => void 0, () => void 0);
|
|
15134
|
+
}
|
|
15135
|
+
/** Decode each child stream continuously and stop reading while the
|
|
15136
|
+
* diagnostic destination applies backpressure. A destination that
|
|
15137
|
+
* errors or closes instead of draining fails the relay rather than
|
|
15138
|
+
* stalling it. */
|
|
15139
|
+
function forwardOutput(source, diagnostics) {
|
|
15140
|
+
let pendingDone;
|
|
15141
|
+
let pendingDrain;
|
|
15142
|
+
let failure;
|
|
15143
|
+
const fail = (cause) => {
|
|
15144
|
+
failure ??= cause;
|
|
15145
|
+
const done = pendingDone;
|
|
15146
|
+
pendingDone = void 0;
|
|
15147
|
+
done?.(cause);
|
|
15148
|
+
};
|
|
15149
|
+
const onSinkError = (cause) => {
|
|
15150
|
+
fail(toError(cause));
|
|
15151
|
+
};
|
|
15152
|
+
const onSinkClose = () => {
|
|
15153
|
+
fail(/* @__PURE__ */ new Error("the diagnostic stream closed during child output"));
|
|
15154
|
+
};
|
|
15155
|
+
diagnostics.once?.("error", onSinkError);
|
|
15156
|
+
diagnostics.once?.("close", onSinkClose);
|
|
15157
|
+
const destination = new Writable({
|
|
15158
|
+
decodeStrings: false,
|
|
15159
|
+
write: (text, _encoding, done) => {
|
|
15160
|
+
if (failure !== void 0) {
|
|
15161
|
+
done(failure);
|
|
15162
|
+
return;
|
|
15163
|
+
}
|
|
15164
|
+
try {
|
|
15165
|
+
if (diagnostics.write(text) === false && diagnostics.once !== void 0) {
|
|
15166
|
+
pendingDone = done;
|
|
15167
|
+
const onDrain = () => {
|
|
15168
|
+
pendingDrain = void 0;
|
|
15169
|
+
if (pendingDone !== done) return;
|
|
15170
|
+
pendingDone = void 0;
|
|
15171
|
+
done();
|
|
15172
|
+
};
|
|
15173
|
+
pendingDrain = onDrain;
|
|
15174
|
+
diagnostics.once("drain", onDrain);
|
|
15175
|
+
} else done();
|
|
15176
|
+
} catch (cause) {
|
|
15177
|
+
done(toError(cause));
|
|
15178
|
+
}
|
|
15179
|
+
}
|
|
15180
|
+
});
|
|
15181
|
+
return pipeline(source.setEncoding("utf8"), destination).finally(() => {
|
|
15182
|
+
diagnostics.off?.("error", onSinkError);
|
|
15183
|
+
diagnostics.off?.("close", onSinkClose);
|
|
15184
|
+
if (pendingDrain !== void 0) diagnostics.off?.("drain", pendingDrain);
|
|
15185
|
+
});
|
|
15186
|
+
}
|
|
15187
|
+
function toError(cause) {
|
|
15188
|
+
return cause instanceof Error ? cause : new Error(String(cause));
|
|
15189
|
+
}
|
|
15024
15190
|
//#endregion
|
|
15025
15191
|
//#region src/runtime.ts
|
|
15026
15192
|
/** Dumb wiring: forwards process signals to the engine's subscribers.
|
|
@@ -15092,6 +15258,7 @@ async function assembleRuntime(proc) {
|
|
|
15092
15258
|
};
|
|
15093
15259
|
warnOnDeprecatedStateFileEnvVar(proc);
|
|
15094
15260
|
const apiBaseUrl = getApiBaseUrl(proc.env);
|
|
15261
|
+
const authBaseUrl = getAuthBaseUrl(proc.env);
|
|
15095
15262
|
return {
|
|
15096
15263
|
stdout: { write: (text) => {
|
|
15097
15264
|
proc.stdout.write(text);
|
|
@@ -15118,15 +15285,16 @@ async function assembleRuntime(proc) {
|
|
|
15118
15285
|
loadConfig: (configPath) => loadConfig(proc.cwd(), configPath),
|
|
15119
15286
|
credentialManager: new FileCredentialManager({
|
|
15120
15287
|
env: proc.env,
|
|
15121
|
-
fetchWorkspaceName: fetchWorkspaceName(apiBaseUrl)
|
|
15288
|
+
fetchWorkspaceName: fetchWorkspaceName(apiBaseUrl),
|
|
15289
|
+
refreshCredential: makeCredentialRefresher(authBaseUrl)
|
|
15122
15290
|
}),
|
|
15123
15291
|
managementApiClientConfig: {
|
|
15124
15292
|
clientId: CLIENT_ID,
|
|
15125
15293
|
redirectUri: DEFAULT_REDIRECT_URI,
|
|
15126
15294
|
apiBaseUrl,
|
|
15127
|
-
authBaseUrl
|
|
15295
|
+
authBaseUrl
|
|
15128
15296
|
},
|
|
15129
|
-
spawn:
|
|
15297
|
+
spawn: makeSpawnChild(proc.stderr),
|
|
15130
15298
|
/** The engine has already decided and composed; the bin only forks
|
|
15131
15299
|
* the detached sender and hands the payload over. Every failure is
|
|
15132
15300
|
* swallowed inside runTelemetry. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma/cli",
|
|
3
|
-
"version": "8.0.0-rc.2-dev.
|
|
3
|
+
"version": "8.0.0-rc.2-dev.51",
|
|
4
4
|
"description": "Command-line interface for the Prisma Developer Platform.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -51,9 +51,9 @@
|
|
|
51
51
|
"open": "^11.0.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@repo/cli-conformance": "8.0.0-rc.2-dev.
|
|
55
|
-
"@repo/cli-telemetry": "8.0.0-rc.2-dev.
|
|
56
|
-
"@repo/tsconfig": "8.0.0-rc.2-dev.
|
|
54
|
+
"@repo/cli-conformance": "8.0.0-rc.2-dev.51",
|
|
55
|
+
"@repo/cli-telemetry": "8.0.0-rc.2-dev.51",
|
|
56
|
+
"@repo/tsconfig": "8.0.0-rc.2-dev.51",
|
|
57
57
|
"@types/node": "^22.19.19",
|
|
58
58
|
"tsdown": "^0.21.10",
|
|
59
59
|
"tsx": "^4.22.4",
|