prisma-sharding 0.0.5 → 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.
@@ -25,109 +25,374 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
25
 
26
26
  // src/cli/update.ts
27
27
  var import_config = require("dotenv/config");
28
+
29
+ // src/utils/env.ts
30
+ var parseBooleanEnv = (name, defaultValue, env = process.env) => {
31
+ const value = env[name]?.trim();
32
+ if (value === void 0 || value === "") {
33
+ return defaultValue;
34
+ }
35
+ return ["1", "true", "yes", "on"].includes(value.toLowerCase());
36
+ };
37
+ var isVerboseEnv = (names, env = process.env) => names.some((name) => parseBooleanEnv(name, false, env));
38
+
39
+ // src/cli/utils/command.ts
28
40
  var import_child_process = require("child_process");
29
41
  var import_path = __toESM(require("path"));
30
- var getShardConfigs = () => {
31
- const shards = [];
32
- const shardCount = parseInt(process.env.SHARD_COUNT || "0", 10);
33
- for (let i = 1; i <= shardCount; i++) {
34
- const url = process.env[`SHARD_${i}_URL`];
35
- if (url) {
36
- shards.push({ id: `shard_${i}`, url });
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
+ }
37
111
  }
112
+ return childProcess.kill(signal);
113
+ } catch {
114
+ return false;
115
+ }
116
+ };
117
+
118
+ // src/cli/utils/command.ts
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;
38
125
  }
39
- if (shards.length === 0 && process.env.DATABASE_URL) {
40
- shards.push({ id: "shard_1", url: process.env.DATABASE_URL });
126
+ if (limit <= OUTPUT_TRUNCATED_MARKER.length) {
127
+ return combined.slice(-limit);
41
128
  }
42
- return shards;
129
+ return OUTPUT_TRUNCATED_MARKER + combined.slice(-(limit - OUTPUT_TRUNCATED_MARKER.length));
43
130
  };
44
- var runCommand = (command, args, env = process.env, cwd = process.cwd()) => {
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
+ };
137
+ var runCommand = (command, args, options = {}) => {
45
138
  return new Promise((resolve) => {
46
- const proc = (0, import_child_process.spawn)(command, args, {
139
+ let stdout = "";
140
+ let stderr = "";
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";
149
+ const child = (0, import_child_process.spawn)(command, args, {
47
150
  env,
48
- cwd: import_path.default.resolve(cwd),
49
- shell: true
50
- });
51
- let output = "";
52
- let errorOutput = "";
53
- proc.stdout.on("data", (data) => {
54
- process.stdout.write(data);
55
- output += data.toString();
151
+ cwd: import_path.default.resolve(options.cwd || process.cwd()),
152
+ detached: processGroup,
153
+ shell: false,
154
+ stdio: ["ignore", "pipe", "pipe"]
56
155
  });
57
- proc.stderr.on("data", (data) => {
58
- process.stderr.write(data);
59
- errorOutput += data.toString();
156
+ const settle = (result) => {
157
+ if (!settled) {
158
+ settled = true;
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);
178
+ }
179
+ };
180
+ child.stdout?.on("data", (data) => {
181
+ if (!settled) {
182
+ stdout = appendBoundedOutput(stdout, data.toString(), maxOutputLength);
183
+ }
60
184
  });
61
- proc.on("close", (code) => {
62
- if (code === 0) {
63
- resolve({ success: true, output });
64
- } else {
65
- resolve({ success: false, output, error: errorOutput || `Exit code: ${code}` });
185
+ child.stderr?.on("data", (data) => {
186
+ if (!settled) {
187
+ stderr = appendBoundedOutput(stderr, data.toString(), maxOutputLength);
66
188
  }
67
189
  });
68
- proc.on("error", (err) => {
69
- resolve({ success: false, output, error: err.message });
190
+ child.once("error", (error) => {
191
+ settle({
192
+ success: false,
193
+ stdout,
194
+ stderr,
195
+ exitCode: null,
196
+ error: timedOut ? `Command timed out after ${timeoutMs}ms` : error.message
197
+ });
198
+ });
199
+ child.once("close", (exitCode) => {
200
+ settle({
201
+ success: !timedOut && exitCode === 0,
202
+ stdout,
203
+ stderr,
204
+ exitCode,
205
+ error: timedOut ? `Command timed out after ${timeoutMs}ms` : exitCode === 0 ? void 0 : stderr.trim() || stdout.trim() || `Command exited with code ${exitCode}`
206
+ });
70
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?.();
71
224
  });
72
225
  };
73
- var updateAll = async () => {
74
- console.log("\n" + "=".repeat(60));
75
- console.log("\u{1F504} PRISMA SHARDING UPDATE");
76
- console.log("=".repeat(60) + "\n");
77
- console.log("\u{1F6E0}\uFE0F Step 1: Generating Prisma Client Types...");
78
- const genResult = await runCommand("npx", ["prisma", "generate"]);
79
- if (!genResult.success) {
80
- console.error("\n\u274C Failed to generate client. Aborting migration.");
81
- process.exit(1);
226
+ var runPrismaCommand = (args, options = {}) => runCommand(getNpxCommand(), ["prisma", ...args], options);
227
+
228
+ // src/cli/utils/output.ts
229
+ var import_readline = __toESM(require("readline"));
230
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
231
+ var printCliHeader = (icon, title) => {
232
+ console.log(`${icon} ${title}
233
+ `);
234
+ };
235
+ var printCliRow = (icon, label, message) => {
236
+ console.log(`${icon} ${label} ${message}`);
237
+ };
238
+ var printVerboseHint = () => {
239
+ console.log("\nRun with SHARD_CLI_VERBOSE=true for details.");
240
+ };
241
+ var createCliLoader = (label, message, enabled = true) => {
242
+ const animated = enabled && Boolean(process.stdout.isTTY);
243
+ let frameIndex = 0;
244
+ let timer;
245
+ let finished = false;
246
+ const clear = () => {
247
+ if (animated) {
248
+ import_readline.default.clearLine(process.stdout, 0);
249
+ import_readline.default.cursorTo(process.stdout, 0);
250
+ }
251
+ };
252
+ const render = () => {
253
+ clear();
254
+ process.stdout.write(`${SPINNER_FRAMES[frameIndex]} ${label} ${message}`);
255
+ frameIndex = (frameIndex + 1) % SPINNER_FRAMES.length;
256
+ };
257
+ if (animated) {
258
+ render();
259
+ timer = setInterval(render, 80);
82
260
  }
83
- console.log("\u2705 Client types generated successfully.\n");
84
- const shards = getShardConfigs();
85
- const extraArgs = process.argv.slice(2);
86
- console.log("\u{1F4E6} Step 2: Migrating Shards...");
87
- console.log(`\u{1F4CA} Total shards to migrate: ${shards.length}`);
88
- if (extraArgs.length > 0) {
89
- console.log(`\u{1F527} Using flags: ${extraArgs.join(" ")}`);
261
+ const finish = (icon, finalMessage) => {
262
+ if (finished) {
263
+ return;
264
+ }
265
+ finished = true;
266
+ if (timer) {
267
+ clearInterval(timer);
268
+ }
269
+ clear();
270
+ printCliRow(icon, label, finalMessage);
271
+ };
272
+ return {
273
+ succeed: (finalMessage) => finish("\u2705", finalMessage),
274
+ fail: (finalMessage) => finish("\u274C", finalMessage)
275
+ };
276
+ };
277
+
278
+ // src/cli/utils/shards.ts
279
+ var NO_SHARDS_CONFIGURED_MESSAGE = "No shards configured. Set SHARD_COUNT and SHARD_N_URL environment variables.";
280
+ var getShardConfigResult = (env = process.env) => {
281
+ const shards = [];
282
+ const missingShardIds = [];
283
+ const shardCount = parseInt(env.SHARD_COUNT || "0", 10);
284
+ for (let i = 1; i <= shardCount; i++) {
285
+ const url = env[`SHARD_${i}_URL`];
286
+ if (url) {
287
+ shards.push({ id: `shard_${i}`, index: i - 1, url });
288
+ } else {
289
+ missingShardIds.push(`shard_${i}`);
290
+ }
90
291
  }
91
- console.log("");
92
- if (shards.length === 0) {
93
- console.error(
94
- "\u274C No shards configured. Set SHARD_COUNT and SHARD_N_URL environment variables."
292
+ if (shards.length === 0 && env.DATABASE_URL) {
293
+ shards.push({ id: "shard_1", index: 0, url: env.DATABASE_URL });
294
+ }
295
+ return { shards, missingShardIds };
296
+ };
297
+ var maskShardUrl = maskDatabaseUrl;
298
+
299
+ // src/cli/utils/prisma.ts
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)}
306
+ `);
307
+ console.log(
308
+ `Command: ${sanitizeCommandOutput(`prisma ${commandArgs.join(" ")}`, commandEnv)}`
95
309
  );
96
- process.exit(1);
97
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
+ }) => {
98
333
  const results = [];
99
334
  for (const shard of shards) {
100
- console.log(`
101
- \u{1F449} Processing ${shard.id}...`);
102
- const env = { ...process.env, DATABASE_URL: shard.url };
103
- const args = ["prisma", "db", "push", "--accept-data-loss", ...extraArgs];
104
- const { success, output, error } = await runCommand("npx", args, env);
105
- if (!success) {
106
- console.error(` \u274C Failed`);
107
- results.push({ shardId: shard.id, success: false, output, error });
335
+ const loader = createCliLoader(shard.id, action, !verbose);
336
+ const commandArgs = [...args, ...extraArgs];
337
+ const result = await executeShardCommand(shard, commandArgs, verbose, action);
338
+ results.push({ shardId: shard.id, success: result.success });
339
+ if (result.success) {
340
+ loader.succeed("Synced");
108
341
  } else {
109
- console.log(` \u2705 Success`);
110
- results.push({ shardId: shard.id, success: true, output });
342
+ loader.fail("Failed");
111
343
  }
112
344
  }
113
- console.log("\n" + "=".repeat(60));
114
- console.log("\u{1F4CB} Update Summary");
115
- console.log("=".repeat(60));
116
- const successful = results.filter((r) => r.success).length;
117
- const failed = results.filter((r) => !r.success).length;
118
- results.forEach((result) => {
119
- const status = result.success ? "\u2705" : "\u274C";
120
- console.log(` ${status} ${result.shardId}`);
121
- });
122
- console.log(`
123
- Total: ${results.length} | Success: ${successful} | Failed: ${failed}`);
345
+ return results;
346
+ };
347
+ var syncShardSchemas = (shards, extraArgs, verbose) => runShardCommands({
348
+ shards,
349
+ args: ["db", "push"],
350
+ extraArgs,
351
+ verbose,
352
+ action: "Syncing"
353
+ });
354
+
355
+ // src/cli/update.ts
356
+ var updateAll = async () => {
357
+ const verbose = isVerboseEnv(["SHARD_UPDATE_VERBOSE", "SHARD_CLI_VERBOSE"]);
358
+ const { shards, missingShardIds } = getShardConfigResult();
359
+ const extraArgs = process.argv.slice(2);
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
+ }
365
+ if (shards.length === 0) {
366
+ printCliRow("\u274C", "config", NO_SHARDS_CONFIGURED_MESSAGE);
367
+ process.exit(1);
368
+ }
369
+ if (verbose) {
370
+ console.log("Generating Prisma Client...\n");
371
+ }
372
+ const generateLoader = createCliLoader("client", "Generating", !verbose);
373
+ const generateResult = await runPrismaCommand(["generate"], { verbose });
374
+ if (verbose && generateResult.error && !generateResult.stderr.trim() && !generateResult.stdout.trim()) {
375
+ console.error(generateResult.error);
376
+ }
377
+ if (!generateResult.success) {
378
+ generateLoader.fail("Generation failed");
379
+ if (!verbose) {
380
+ printVerboseHint();
381
+ }
382
+ process.exit(1);
383
+ }
384
+ generateLoader.succeed("Generated");
385
+ const results = await syncShardSchemas(shards, extraArgs, verbose);
386
+ const successful = results.filter((result) => result.success).length;
387
+ const failed = results.length - successful;
124
388
  if (failed > 0) {
125
- console.log("\n\u26A0\uFE0F Some migrations failed. Run with --force-reset if needed.");
389
+ if (!verbose) {
390
+ printVerboseHint();
391
+ }
126
392
  process.exit(1);
127
393
  }
128
- console.log("\n\u2728 All updates completed successfully!");
129
394
  };
130
395
  updateAll().catch((error) => {
131
- console.error("Update script failed:", error);
396
+ console.error(error instanceof Error ? error.message : String(error));
132
397
  process.exit(1);
133
398
  });
@@ -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
+ });