prisma-sharding 0.0.6 → 0.0.7
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 +135 -58
- package/dist/cli/migrate.js +198 -47
- package/dist/cli/studio.js +152 -84
- package/dist/cli/test.js +145 -41
- package/dist/cli/update.js +195 -46
- package/dist/cli/utils/command.js +232 -0
- package/dist/cli/utils/postgres.js +55 -0
- package/dist/index.js +395 -89
- package/dist/index.mjs +395 -89
- package/package.json +3 -3
package/dist/cli/update.js
CHANGED
|
@@ -39,36 +39,152 @@ var isVerboseEnv = (names, env = process.env) => names.some((name) => parseBoole
|
|
|
39
39
|
// src/cli/utils/command.ts
|
|
40
40
|
var import_child_process = require("child_process");
|
|
41
41
|
var import_path = __toESM(require("path"));
|
|
42
|
+
|
|
43
|
+
// src/constants/internal.ts
|
|
44
|
+
var INTERNAL_DEFAULTS = {
|
|
45
|
+
HEALTH_CHECK_TIMEOUT_MS: 5e3,
|
|
46
|
+
CROSS_SHARD_CONCURRENCY: 4,
|
|
47
|
+
CROSS_SHARD_TIMEOUT_MS: 1e4,
|
|
48
|
+
CLI_COMMAND_TIMEOUT_MS: 12e4,
|
|
49
|
+
CLI_FORCE_KILL_GRACE_MS: 5e3,
|
|
50
|
+
CLI_MAX_OUTPUT_LENGTH: 1e6,
|
|
51
|
+
CLI_TEST_TCP_TIMEOUT_MS: 5e3,
|
|
52
|
+
CLI_TEST_COMMAND_TIMEOUT_MS: 15e3,
|
|
53
|
+
STUDIO_BASE_PORT: 51212,
|
|
54
|
+
STUDIO_REUSE_EXISTING: true,
|
|
55
|
+
STUDIO_STRICT_PORT_CHECK: false,
|
|
56
|
+
STUDIO_START_TIMEOUT_MS: 15e3,
|
|
57
|
+
STUDIO_STABILITY_MS: 500,
|
|
58
|
+
STUDIO_SHUTDOWN_TIMEOUT_MS: 5e3
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// src/utils/sanitize.ts
|
|
62
|
+
var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
63
|
+
var maskDatabaseUrl = (url) => {
|
|
64
|
+
try {
|
|
65
|
+
const parsed = new URL(url);
|
|
66
|
+
if (parsed.password) {
|
|
67
|
+
parsed.password = "***";
|
|
68
|
+
}
|
|
69
|
+
return parsed.toString();
|
|
70
|
+
} catch {
|
|
71
|
+
return url.replace(/:[^:@]+@/, ":***@");
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
var sanitizeDatabaseText = (output, databaseUrls) => {
|
|
75
|
+
let sanitized = output;
|
|
76
|
+
for (const url of databaseUrls) {
|
|
77
|
+
sanitized = sanitized.replace(new RegExp(escapeRegExp(url), "g"), maskDatabaseUrl(url));
|
|
78
|
+
try {
|
|
79
|
+
const password = new URL(url).password;
|
|
80
|
+
if (password.length >= 4) {
|
|
81
|
+
sanitized = sanitized.replace(
|
|
82
|
+
new RegExp(escapeRegExp(decodeURIComponent(password)), "g"),
|
|
83
|
+
"***"
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
} catch {
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return sanitized.replace(
|
|
90
|
+
/(postgres(?:ql)?:\/\/[^:/\s]+:)[^@\s]+(@)/gi,
|
|
91
|
+
"$1***$2"
|
|
92
|
+
);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// src/cli/utils/process.ts
|
|
96
|
+
var isChildProcessRunning = (childProcess) => {
|
|
97
|
+
return childProcess.exitCode === null && childProcess.signalCode === null;
|
|
98
|
+
};
|
|
99
|
+
var terminateChildProcess = (childProcess, options = {}) => {
|
|
100
|
+
if (!isChildProcessRunning(childProcess)) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
const signal = options.signal || "SIGTERM";
|
|
104
|
+
try {
|
|
105
|
+
if (options.processGroup && childProcess.pid && process.platform !== "win32") {
|
|
106
|
+
try {
|
|
107
|
+
process.kill(-childProcess.pid, signal);
|
|
108
|
+
return true;
|
|
109
|
+
} catch {
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return childProcess.kill(signal);
|
|
113
|
+
} catch {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// src/cli/utils/command.ts
|
|
42
119
|
var getNpxCommand = () => process.platform === "win32" ? "npx.cmd" : "npx";
|
|
120
|
+
var OUTPUT_TRUNCATED_MARKER = "\n[output truncated]\n";
|
|
121
|
+
var appendBoundedOutput = (current, chunk, limit) => {
|
|
122
|
+
const combined = current + chunk;
|
|
123
|
+
if (combined.length <= limit) {
|
|
124
|
+
return combined;
|
|
125
|
+
}
|
|
126
|
+
if (limit <= OUTPUT_TRUNCATED_MARKER.length) {
|
|
127
|
+
return combined.slice(-limit);
|
|
128
|
+
}
|
|
129
|
+
return OUTPUT_TRUNCATED_MARKER + combined.slice(-(limit - OUTPUT_TRUNCATED_MARKER.length));
|
|
130
|
+
};
|
|
131
|
+
var sanitizeCommandOutput = (output, env = process.env) => {
|
|
132
|
+
const databaseUrls = Object.entries(env).filter(
|
|
133
|
+
([name, value]) => Boolean(value) && (name === "DATABASE_URL" || /^SHARD_\d+_URL$/.test(name))
|
|
134
|
+
).map(([, value]) => value);
|
|
135
|
+
return sanitizeDatabaseText(output, databaseUrls).replace(/(\bpassword\s*[=:]\s*)[^\s,;]+/gi, "$1***");
|
|
136
|
+
};
|
|
43
137
|
var runCommand = (command, args, options = {}) => {
|
|
44
138
|
return new Promise((resolve) => {
|
|
45
139
|
let stdout = "";
|
|
46
140
|
let stderr = "";
|
|
47
141
|
let settled = false;
|
|
142
|
+
let timedOut = false;
|
|
143
|
+
let forceKillTimeout;
|
|
144
|
+
const env = options.env || process.env;
|
|
145
|
+
const timeoutMs = options.timeoutMs ?? INTERNAL_DEFAULTS.CLI_COMMAND_TIMEOUT_MS;
|
|
146
|
+
const forceKillGraceMs = options.forceKillGraceMs ?? INTERNAL_DEFAULTS.CLI_FORCE_KILL_GRACE_MS;
|
|
147
|
+
const maxOutputLength = options.maxOutputLength ?? INTERNAL_DEFAULTS.CLI_MAX_OUTPUT_LENGTH;
|
|
148
|
+
const processGroup = process.platform !== "win32";
|
|
48
149
|
const child = (0, import_child_process.spawn)(command, args, {
|
|
49
|
-
env
|
|
150
|
+
env,
|
|
50
151
|
cwd: import_path.default.resolve(options.cwd || process.cwd()),
|
|
152
|
+
detached: processGroup,
|
|
51
153
|
shell: false,
|
|
52
154
|
stdio: ["ignore", "pipe", "pipe"]
|
|
53
155
|
});
|
|
54
156
|
const settle = (result) => {
|
|
55
157
|
if (!settled) {
|
|
56
158
|
settled = true;
|
|
57
|
-
|
|
159
|
+
clearTimeout(timeout);
|
|
160
|
+
if (forceKillTimeout) {
|
|
161
|
+
clearTimeout(forceKillTimeout);
|
|
162
|
+
}
|
|
163
|
+
const sanitizedResult = {
|
|
164
|
+
...result,
|
|
165
|
+
stdout: sanitizeCommandOutput(result.stdout, env),
|
|
166
|
+
stderr: sanitizeCommandOutput(result.stderr, env),
|
|
167
|
+
error: result.error ? sanitizeCommandOutput(result.error, env) : void 0
|
|
168
|
+
};
|
|
169
|
+
if (options.verbose) {
|
|
170
|
+
if (sanitizedResult.stdout) {
|
|
171
|
+
process.stdout.write(sanitizedResult.stdout);
|
|
172
|
+
}
|
|
173
|
+
if (sanitizedResult.stderr) {
|
|
174
|
+
process.stderr.write(sanitizedResult.stderr);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
resolve(sanitizedResult);
|
|
58
178
|
}
|
|
59
179
|
};
|
|
60
180
|
child.stdout?.on("data", (data) => {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (options.verbose) {
|
|
64
|
-
process.stdout.write(output);
|
|
181
|
+
if (!settled) {
|
|
182
|
+
stdout = appendBoundedOutput(stdout, data.toString(), maxOutputLength);
|
|
65
183
|
}
|
|
66
184
|
});
|
|
67
185
|
child.stderr?.on("data", (data) => {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (options.verbose) {
|
|
71
|
-
process.stderr.write(output);
|
|
186
|
+
if (!settled) {
|
|
187
|
+
stderr = appendBoundedOutput(stderr, data.toString(), maxOutputLength);
|
|
72
188
|
}
|
|
73
189
|
});
|
|
74
190
|
child.once("error", (error) => {
|
|
@@ -76,18 +192,35 @@ var runCommand = (command, args, options = {}) => {
|
|
|
76
192
|
success: false,
|
|
77
193
|
stdout,
|
|
78
194
|
stderr,
|
|
79
|
-
|
|
195
|
+
exitCode: null,
|
|
196
|
+
error: timedOut ? `Command timed out after ${timeoutMs}ms` : error.message
|
|
80
197
|
});
|
|
81
198
|
});
|
|
82
199
|
child.once("close", (exitCode) => {
|
|
83
200
|
settle({
|
|
84
|
-
success: exitCode === 0,
|
|
201
|
+
success: !timedOut && exitCode === 0,
|
|
85
202
|
stdout,
|
|
86
203
|
stderr,
|
|
87
204
|
exitCode,
|
|
88
|
-
error: exitCode === 0 ? void 0 : stderr.trim() || stdout.trim() || `Command exited with code ${exitCode}`
|
|
205
|
+
error: timedOut ? `Command timed out after ${timeoutMs}ms` : exitCode === 0 ? void 0 : stderr.trim() || stdout.trim() || `Command exited with code ${exitCode}`
|
|
89
206
|
});
|
|
90
207
|
});
|
|
208
|
+
const timeout = setTimeout(() => {
|
|
209
|
+
timedOut = true;
|
|
210
|
+
terminateChildProcess(child, { signal: "SIGTERM", processGroup });
|
|
211
|
+
forceKillTimeout = setTimeout(() => {
|
|
212
|
+
terminateChildProcess(child, { signal: "SIGKILL", processGroup });
|
|
213
|
+
settle({
|
|
214
|
+
success: false,
|
|
215
|
+
stdout,
|
|
216
|
+
stderr,
|
|
217
|
+
exitCode: null,
|
|
218
|
+
error: `Command timed out after ${timeoutMs}ms`
|
|
219
|
+
});
|
|
220
|
+
}, forceKillGraceMs);
|
|
221
|
+
forceKillTimeout.unref?.();
|
|
222
|
+
}, timeoutMs);
|
|
223
|
+
timeout.unref?.();
|
|
91
224
|
});
|
|
92
225
|
};
|
|
93
226
|
var runPrismaCommand = (args, options = {}) => runCommand(getNpxCommand(), ["prisma", ...args], options);
|
|
@@ -161,42 +294,47 @@ var getShardConfigResult = (env = process.env) => {
|
|
|
161
294
|
}
|
|
162
295
|
return { shards, missingShardIds };
|
|
163
296
|
};
|
|
164
|
-
var
|
|
165
|
-
return getShardConfigResult(env).shards;
|
|
166
|
-
};
|
|
167
|
-
var maskShardUrl = (url) => {
|
|
168
|
-
try {
|
|
169
|
-
const parsed = new URL(url);
|
|
170
|
-
if (parsed.password) {
|
|
171
|
-
parsed.password = "***";
|
|
172
|
-
}
|
|
173
|
-
return parsed.toString();
|
|
174
|
-
} catch {
|
|
175
|
-
return url.replace(/:[^:@]+@/, ":***@");
|
|
176
|
-
}
|
|
177
|
-
};
|
|
297
|
+
var maskShardUrl = maskDatabaseUrl;
|
|
178
298
|
|
|
179
299
|
// src/cli/utils/prisma.ts
|
|
180
|
-
var
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
Syncing ${shard.id}...`);
|
|
187
|
-
console.log(`Database: ${maskShardUrl(shard.url)}
|
|
300
|
+
var executeShardCommand = async (shard, commandArgs, verbose, action) => {
|
|
301
|
+
const commandEnv = { ...process.env, DATABASE_URL: shard.url };
|
|
302
|
+
if (verbose) {
|
|
303
|
+
console.log(`
|
|
304
|
+
${action} ${shard.id}...`);
|
|
305
|
+
console.log(`Database: ${maskShardUrl(shard.url)}
|
|
188
306
|
`);
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
["db", "push", "--accept-data-loss", ...extraArgs],
|
|
192
|
-
{
|
|
193
|
-
env: { ...process.env, DATABASE_URL: shard.url },
|
|
194
|
-
verbose
|
|
195
|
-
}
|
|
307
|
+
console.log(
|
|
308
|
+
`Command: ${sanitizeCommandOutput(`prisma ${commandArgs.join(" ")}`, commandEnv)}`
|
|
196
309
|
);
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
310
|
+
}
|
|
311
|
+
const result = await runPrismaCommand(commandArgs, {
|
|
312
|
+
env: commandEnv,
|
|
313
|
+
verbose
|
|
314
|
+
});
|
|
315
|
+
if (verbose) {
|
|
316
|
+
console.log(`Exit code: ${result.exitCode ?? "unavailable"}`);
|
|
317
|
+
}
|
|
318
|
+
if (verbose && result.error && !result.stderr.trim() && !result.stdout.trim()) {
|
|
319
|
+
console.error(result.error);
|
|
320
|
+
}
|
|
321
|
+
if (verbose && !result.success) {
|
|
322
|
+
console.error(`Next: verify ${shard.id} connectivity and migration state, then retry.`);
|
|
323
|
+
}
|
|
324
|
+
return result;
|
|
325
|
+
};
|
|
326
|
+
var runShardCommands = async ({
|
|
327
|
+
shards,
|
|
328
|
+
args,
|
|
329
|
+
extraArgs,
|
|
330
|
+
verbose,
|
|
331
|
+
action
|
|
332
|
+
}) => {
|
|
333
|
+
const results = [];
|
|
334
|
+
for (const shard of shards) {
|
|
335
|
+
const loader = createCliLoader(shard.id, action, !verbose);
|
|
336
|
+
const commandArgs = [...args, ...extraArgs];
|
|
337
|
+
const result = await executeShardCommand(shard, commandArgs, verbose, action);
|
|
200
338
|
results.push({ shardId: shard.id, success: result.success });
|
|
201
339
|
if (result.success) {
|
|
202
340
|
loader.succeed("Synced");
|
|
@@ -206,13 +344,24 @@ Syncing ${shard.id}...`);
|
|
|
206
344
|
}
|
|
207
345
|
return results;
|
|
208
346
|
};
|
|
347
|
+
var syncShardSchemas = (shards, extraArgs, verbose) => runShardCommands({
|
|
348
|
+
shards,
|
|
349
|
+
args: ["db", "push"],
|
|
350
|
+
extraArgs,
|
|
351
|
+
verbose,
|
|
352
|
+
action: "Syncing"
|
|
353
|
+
});
|
|
209
354
|
|
|
210
355
|
// src/cli/update.ts
|
|
211
356
|
var updateAll = async () => {
|
|
212
357
|
const verbose = isVerboseEnv(["SHARD_UPDATE_VERBOSE", "SHARD_CLI_VERBOSE"]);
|
|
213
|
-
const shards =
|
|
358
|
+
const { shards, missingShardIds } = getShardConfigResult();
|
|
214
359
|
const extraArgs = process.argv.slice(2);
|
|
215
360
|
printCliHeader("\u{1F504}", "Prisma Sharding Update");
|
|
361
|
+
if (missingShardIds.length > 0) {
|
|
362
|
+
printCliRow("\u274C", "config", `Missing shard URLs: ${missingShardIds.join(", ")}`);
|
|
363
|
+
process.exit(1);
|
|
364
|
+
}
|
|
216
365
|
if (shards.length === 0) {
|
|
217
366
|
printCliRow("\u274C", "config", NO_SHARDS_CONFIGURED_MESSAGE);
|
|
218
367
|
process.exit(1);
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/cli/utils/command.ts
|
|
31
|
+
var command_exports = {};
|
|
32
|
+
__export(command_exports, {
|
|
33
|
+
getNpxCommand: () => getNpxCommand,
|
|
34
|
+
runCommand: () => runCommand,
|
|
35
|
+
runPrismaCommand: () => runPrismaCommand,
|
|
36
|
+
sanitizeCommandOutput: () => sanitizeCommandOutput
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(command_exports);
|
|
39
|
+
var import_child_process = require("child_process");
|
|
40
|
+
var import_path = __toESM(require("path"));
|
|
41
|
+
|
|
42
|
+
// src/constants/internal.ts
|
|
43
|
+
var INTERNAL_DEFAULTS = {
|
|
44
|
+
HEALTH_CHECK_TIMEOUT_MS: 5e3,
|
|
45
|
+
CROSS_SHARD_CONCURRENCY: 4,
|
|
46
|
+
CROSS_SHARD_TIMEOUT_MS: 1e4,
|
|
47
|
+
CLI_COMMAND_TIMEOUT_MS: 12e4,
|
|
48
|
+
CLI_FORCE_KILL_GRACE_MS: 5e3,
|
|
49
|
+
CLI_MAX_OUTPUT_LENGTH: 1e6,
|
|
50
|
+
CLI_TEST_TCP_TIMEOUT_MS: 5e3,
|
|
51
|
+
CLI_TEST_COMMAND_TIMEOUT_MS: 15e3,
|
|
52
|
+
STUDIO_BASE_PORT: 51212,
|
|
53
|
+
STUDIO_REUSE_EXISTING: true,
|
|
54
|
+
STUDIO_STRICT_PORT_CHECK: false,
|
|
55
|
+
STUDIO_START_TIMEOUT_MS: 15e3,
|
|
56
|
+
STUDIO_STABILITY_MS: 500,
|
|
57
|
+
STUDIO_SHUTDOWN_TIMEOUT_MS: 5e3
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// src/utils/sanitize.ts
|
|
61
|
+
var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
62
|
+
var maskDatabaseUrl = (url) => {
|
|
63
|
+
try {
|
|
64
|
+
const parsed = new URL(url);
|
|
65
|
+
if (parsed.password) {
|
|
66
|
+
parsed.password = "***";
|
|
67
|
+
}
|
|
68
|
+
return parsed.toString();
|
|
69
|
+
} catch {
|
|
70
|
+
return url.replace(/:[^:@]+@/, ":***@");
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
var sanitizeDatabaseText = (output, databaseUrls) => {
|
|
74
|
+
let sanitized = output;
|
|
75
|
+
for (const url of databaseUrls) {
|
|
76
|
+
sanitized = sanitized.replace(new RegExp(escapeRegExp(url), "g"), maskDatabaseUrl(url));
|
|
77
|
+
try {
|
|
78
|
+
const password = new URL(url).password;
|
|
79
|
+
if (password.length >= 4) {
|
|
80
|
+
sanitized = sanitized.replace(
|
|
81
|
+
new RegExp(escapeRegExp(decodeURIComponent(password)), "g"),
|
|
82
|
+
"***"
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
} catch {
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return sanitized.replace(
|
|
89
|
+
/(postgres(?:ql)?:\/\/[^:/\s]+:)[^@\s]+(@)/gi,
|
|
90
|
+
"$1***$2"
|
|
91
|
+
);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// src/cli/utils/process.ts
|
|
95
|
+
var isChildProcessRunning = (childProcess) => {
|
|
96
|
+
return childProcess.exitCode === null && childProcess.signalCode === null;
|
|
97
|
+
};
|
|
98
|
+
var terminateChildProcess = (childProcess, options = {}) => {
|
|
99
|
+
if (!isChildProcessRunning(childProcess)) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
const signal = options.signal || "SIGTERM";
|
|
103
|
+
try {
|
|
104
|
+
if (options.processGroup && childProcess.pid && process.platform !== "win32") {
|
|
105
|
+
try {
|
|
106
|
+
process.kill(-childProcess.pid, signal);
|
|
107
|
+
return true;
|
|
108
|
+
} catch {
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return childProcess.kill(signal);
|
|
112
|
+
} catch {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// src/cli/utils/command.ts
|
|
118
|
+
var getNpxCommand = () => process.platform === "win32" ? "npx.cmd" : "npx";
|
|
119
|
+
var OUTPUT_TRUNCATED_MARKER = "\n[output truncated]\n";
|
|
120
|
+
var appendBoundedOutput = (current, chunk, limit) => {
|
|
121
|
+
const combined = current + chunk;
|
|
122
|
+
if (combined.length <= limit) {
|
|
123
|
+
return combined;
|
|
124
|
+
}
|
|
125
|
+
if (limit <= OUTPUT_TRUNCATED_MARKER.length) {
|
|
126
|
+
return combined.slice(-limit);
|
|
127
|
+
}
|
|
128
|
+
return OUTPUT_TRUNCATED_MARKER + combined.slice(-(limit - OUTPUT_TRUNCATED_MARKER.length));
|
|
129
|
+
};
|
|
130
|
+
var sanitizeCommandOutput = (output, env = process.env) => {
|
|
131
|
+
const databaseUrls = Object.entries(env).filter(
|
|
132
|
+
([name, value]) => Boolean(value) && (name === "DATABASE_URL" || /^SHARD_\d+_URL$/.test(name))
|
|
133
|
+
).map(([, value]) => value);
|
|
134
|
+
return sanitizeDatabaseText(output, databaseUrls).replace(/(\bpassword\s*[=:]\s*)[^\s,;]+/gi, "$1***");
|
|
135
|
+
};
|
|
136
|
+
var runCommand = (command, args, options = {}) => {
|
|
137
|
+
return new Promise((resolve) => {
|
|
138
|
+
let stdout = "";
|
|
139
|
+
let stderr = "";
|
|
140
|
+
let settled = false;
|
|
141
|
+
let timedOut = false;
|
|
142
|
+
let forceKillTimeout;
|
|
143
|
+
const env = options.env || process.env;
|
|
144
|
+
const timeoutMs = options.timeoutMs ?? INTERNAL_DEFAULTS.CLI_COMMAND_TIMEOUT_MS;
|
|
145
|
+
const forceKillGraceMs = options.forceKillGraceMs ?? INTERNAL_DEFAULTS.CLI_FORCE_KILL_GRACE_MS;
|
|
146
|
+
const maxOutputLength = options.maxOutputLength ?? INTERNAL_DEFAULTS.CLI_MAX_OUTPUT_LENGTH;
|
|
147
|
+
const processGroup = process.platform !== "win32";
|
|
148
|
+
const child = (0, import_child_process.spawn)(command, args, {
|
|
149
|
+
env,
|
|
150
|
+
cwd: import_path.default.resolve(options.cwd || process.cwd()),
|
|
151
|
+
detached: processGroup,
|
|
152
|
+
shell: false,
|
|
153
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
154
|
+
});
|
|
155
|
+
const settle = (result) => {
|
|
156
|
+
if (!settled) {
|
|
157
|
+
settled = true;
|
|
158
|
+
clearTimeout(timeout);
|
|
159
|
+
if (forceKillTimeout) {
|
|
160
|
+
clearTimeout(forceKillTimeout);
|
|
161
|
+
}
|
|
162
|
+
const sanitizedResult = {
|
|
163
|
+
...result,
|
|
164
|
+
stdout: sanitizeCommandOutput(result.stdout, env),
|
|
165
|
+
stderr: sanitizeCommandOutput(result.stderr, env),
|
|
166
|
+
error: result.error ? sanitizeCommandOutput(result.error, env) : void 0
|
|
167
|
+
};
|
|
168
|
+
if (options.verbose) {
|
|
169
|
+
if (sanitizedResult.stdout) {
|
|
170
|
+
process.stdout.write(sanitizedResult.stdout);
|
|
171
|
+
}
|
|
172
|
+
if (sanitizedResult.stderr) {
|
|
173
|
+
process.stderr.write(sanitizedResult.stderr);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
resolve(sanitizedResult);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
child.stdout?.on("data", (data) => {
|
|
180
|
+
if (!settled) {
|
|
181
|
+
stdout = appendBoundedOutput(stdout, data.toString(), maxOutputLength);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
child.stderr?.on("data", (data) => {
|
|
185
|
+
if (!settled) {
|
|
186
|
+
stderr = appendBoundedOutput(stderr, data.toString(), maxOutputLength);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
child.once("error", (error) => {
|
|
190
|
+
settle({
|
|
191
|
+
success: false,
|
|
192
|
+
stdout,
|
|
193
|
+
stderr,
|
|
194
|
+
exitCode: null,
|
|
195
|
+
error: timedOut ? `Command timed out after ${timeoutMs}ms` : error.message
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
child.once("close", (exitCode) => {
|
|
199
|
+
settle({
|
|
200
|
+
success: !timedOut && exitCode === 0,
|
|
201
|
+
stdout,
|
|
202
|
+
stderr,
|
|
203
|
+
exitCode,
|
|
204
|
+
error: timedOut ? `Command timed out after ${timeoutMs}ms` : exitCode === 0 ? void 0 : stderr.trim() || stdout.trim() || `Command exited with code ${exitCode}`
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
const timeout = setTimeout(() => {
|
|
208
|
+
timedOut = true;
|
|
209
|
+
terminateChildProcess(child, { signal: "SIGTERM", processGroup });
|
|
210
|
+
forceKillTimeout = setTimeout(() => {
|
|
211
|
+
terminateChildProcess(child, { signal: "SIGKILL", processGroup });
|
|
212
|
+
settle({
|
|
213
|
+
success: false,
|
|
214
|
+
stdout,
|
|
215
|
+
stderr,
|
|
216
|
+
exitCode: null,
|
|
217
|
+
error: `Command timed out after ${timeoutMs}ms`
|
|
218
|
+
});
|
|
219
|
+
}, forceKillGraceMs);
|
|
220
|
+
forceKillTimeout.unref?.();
|
|
221
|
+
}, timeoutMs);
|
|
222
|
+
timeout.unref?.();
|
|
223
|
+
});
|
|
224
|
+
};
|
|
225
|
+
var runPrismaCommand = (args, options = {}) => runCommand(getNpxCommand(), ["prisma", ...args], options);
|
|
226
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
227
|
+
0 && (module.exports = {
|
|
228
|
+
getNpxCommand,
|
|
229
|
+
runCommand,
|
|
230
|
+
runPrismaCommand,
|
|
231
|
+
sanitizeCommandOutput
|
|
232
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/cli/utils/postgres.ts
|
|
21
|
+
var postgres_exports = {};
|
|
22
|
+
__export(postgres_exports, {
|
|
23
|
+
parsePostgresUrl: () => parsePostgresUrl,
|
|
24
|
+
postgresEndpoint: () => postgresEndpoint
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(postgres_exports);
|
|
27
|
+
var parsePostgresUrl = (url) => {
|
|
28
|
+
try {
|
|
29
|
+
const parsed = new URL(url);
|
|
30
|
+
if (parsed.protocol !== "postgresql:" && parsed.protocol !== "postgres:") {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const database = decodeURIComponent(parsed.pathname.replace(/^\/+/, ""));
|
|
34
|
+
const port = parsed.port ? Number(parsed.port) : 5432;
|
|
35
|
+
const queryHost = parsed.searchParams.get("host");
|
|
36
|
+
const host = parsed.hostname.replace(/^\[(.*)\]$/, "$1");
|
|
37
|
+
if (!database || !host || !Number.isInteger(port) || port <= 0 || port > 65535) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
host,
|
|
42
|
+
port,
|
|
43
|
+
database,
|
|
44
|
+
socketPath: queryHost?.startsWith("/") ? queryHost : void 0
|
|
45
|
+
};
|
|
46
|
+
} catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
var postgresEndpoint = (info) => info.socketPath || `${info.host}:${info.port}`;
|
|
51
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
52
|
+
0 && (module.exports = {
|
|
53
|
+
parsePostgresUrl,
|
|
54
|
+
postgresEndpoint
|
|
55
|
+
});
|