create-db 1.1.2-pr44-DC-4828-json-flag-17109503084.0 → 1.1.2

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/index.js DELETED
@@ -1,560 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import dotenv from "dotenv";
4
- dotenv.config();
5
-
6
- import { select, spinner, intro, outro, log, cancel } from "@clack/prompts";
7
- import chalk from "chalk";
8
- import terminalLink from "terminal-link";
9
- import { analytics } from "./analytics.js";
10
-
11
- const CREATE_DB_WORKER_URL =
12
- process.env.CREATE_DB_WORKER_URL || "https://create-db-temp.prisma.io";
13
- const CLAIM_DB_WORKER_URL =
14
- process.env.CLAIM_DB_WORKER_URL || "https://create-db.prisma.io";
15
-
16
- async function listRegions() {
17
- try {
18
- const regions = await getRegions();
19
- console.log(chalk.cyan.bold("\n🌐 Available Prisma Postgres regions:\n"));
20
- regions.forEach((r) =>
21
- console.log(`- ${chalk.green(r.id)}, ${r.name || r.id}`)
22
- );
23
- console.log("");
24
- } catch (e) {
25
- handleError("Failed to fetch regions.", e);
26
- }
27
- }
28
-
29
- async function isOffline() {
30
- const healthUrl = `${CREATE_DB_WORKER_URL}/health`;
31
-
32
- try {
33
- const res = await fetch(healthUrl, { method: "GET" });
34
- if (!res.ok) {
35
- throw new Error(`Prisma Postgres API returned a status of ${res.status}`);
36
- }
37
- return false; // Online
38
- } catch {
39
- console.error(
40
- chalk.red.bold("\nāœ– Error: Cannot reach Prisma Postgres API server.\n")
41
- );
42
- console.error(
43
- chalk.gray(
44
- `Check your internet connection or visit ${chalk.green("https://www.prisma-status.com/\n")}`
45
- )
46
- );
47
- process.exit(1);
48
- }
49
- }
50
-
51
- function getCommandName() {
52
- const executable = process.argv[1] || "create-db";
53
- if (executable.includes("create-pg")) return "create-pg";
54
- if (executable.includes("create-postgres")) return "create-postgres";
55
- return "create-db";
56
- }
57
-
58
- const CLI_NAME = getCommandName();
59
-
60
- async function showHelp() {
61
- let regionExamples = "us-east-1, eu-west-1";
62
- try {
63
- const regions = await getRegions();
64
- if (regions && regions.length > 0) {
65
- regionExamples = regions.map((r) => r.id).join(", ");
66
- }
67
- } catch {}
68
-
69
- console.log(`
70
- ${chalk.cyan.bold("Prisma Postgres Create DB")}
71
-
72
- Usage:
73
- ${chalk.green(`npx ${CLI_NAME} [options]`)}
74
-
75
- Options:
76
- ${chalk.yellow(`--region <region>, -r <region>`)} Specify the region (e.g., ${regionExamples})
77
- ${chalk.yellow("--interactive, -i")} Run in interactive mode to select a region and create the database
78
- ${chalk.yellow("--json, -j")} Output machine-readable JSON and exit
79
- ${chalk.yellow("--list-regions")} List available regions and exit
80
- ${chalk.yellow("--help, -h")} Show this help message
81
-
82
- Examples:
83
- ${chalk.gray(`npx ${CLI_NAME} --region us-east-1`)}
84
- ${chalk.gray(`npx ${CLI_NAME} -r us-east-1`)}
85
- ${chalk.gray(`npx ${CLI_NAME} --interactive`)}
86
- ${chalk.gray(`npx ${CLI_NAME} -i`)}
87
- ${chalk.gray(`npx ${CLI_NAME} --json --region us-east-1`)}
88
- `);
89
- process.exit(0);
90
- }
91
-
92
- async function parseArgs() {
93
- const args = process.argv.slice(2);
94
- const flags = {};
95
-
96
- const allowedFlags = [
97
- "region",
98
- "help",
99
- "list-regions",
100
- "interactive",
101
- "json",
102
- ];
103
- const shorthandMap = {
104
- r: "region",
105
- i: "interactive",
106
- h: "help",
107
- j: "json",
108
- };
109
-
110
- const exitWithError = (message) => {
111
- console.error(chalk.red.bold("\nāœ– " + message));
112
- console.error(chalk.gray("\nUse --help or -h to see available options.\n"));
113
- process.exit(1);
114
- };
115
-
116
- for (let i = 0; i < args.length; i++) {
117
- const arg = args[i];
118
-
119
- if (arg.startsWith("--")) {
120
- const flag = arg.slice(2);
121
- if (flag === "help") await showHelp();
122
- if (!allowedFlags.includes(flag))
123
- exitWithError(`Invalid flag: --${flag}`);
124
- if (flag === "region") {
125
- const region = args[i + 1];
126
- if (!region || region.startsWith("-"))
127
- exitWithError("Missing value for --region flag.");
128
- flags.region = region;
129
- i++;
130
- } else {
131
- flags[flag] = true;
132
- }
133
- continue;
134
- }
135
-
136
- if (arg.startsWith("-")) {
137
- const short = arg.slice(1);
138
-
139
- if (shorthandMap[short]) {
140
- const mappedFlag = shorthandMap[short];
141
- if (mappedFlag === "help") showHelp();
142
- if (mappedFlag === "region") {
143
- const region = args[i + 1];
144
- if (!region || region.startsWith("-"))
145
- exitWithError("Missing value for -r flag.");
146
- flags.region = region;
147
- i++;
148
- } else {
149
- flags[mappedFlag] = true;
150
- }
151
- continue;
152
- }
153
-
154
- for (const letter of short.split("")) {
155
- const mappedFlag = shorthandMap[letter];
156
- if (!mappedFlag) exitWithError(`Invalid flag: -${letter}`);
157
- if (mappedFlag === "help") {
158
- await showHelp();
159
- return;
160
- }
161
- if (mappedFlag === "region") {
162
- const region = args[i + 1];
163
- if (!region || region.startsWith("-"))
164
- exitWithError("Missing value for -r flag.");
165
- flags.region = region;
166
- i++;
167
- } else {
168
- flags[mappedFlag] = true;
169
- }
170
- }
171
- continue;
172
- }
173
-
174
- exitWithError(`Invalid argument: ${arg}`);
175
- }
176
-
177
- return { flags };
178
- }
179
-
180
- export async function getRegions(returnJson = false) {
181
- const url = `${CREATE_DB_WORKER_URL}/regions`;
182
- const res = await fetch(url);
183
-
184
- if (!res.ok) {
185
- if (returnJson) {
186
- throw new Error(
187
- `Failed to fetch regions. Status: ${res.status} ${res.statusText}`
188
- );
189
- }
190
- handleError(
191
- `Failed to fetch regions. Status: ${res.status} ${res.statusText}`
192
- );
193
- }
194
-
195
- try {
196
- const data = await res.json();
197
- const regions = Array.isArray(data) ? data : data.data;
198
- return regions.filter((region) => region.status === "available");
199
- } catch (e) {
200
- if (returnJson) {
201
- throw new Error("Failed to parse JSON from /regions endpoint.");
202
- }
203
- handleError("Failed to parse JSON from /regions endpoint.", e);
204
- }
205
- }
206
-
207
- export async function validateRegion(region, returnJson = false) {
208
- const regions = await getRegions(returnJson);
209
- const regionIds = regions.map((r) => r.id);
210
-
211
- if (!regionIds.includes(region)) {
212
- if (returnJson) {
213
- throw new Error(
214
- `Invalid region: ${region}. Available regions: ${regionIds.join(", ")}`
215
- );
216
- }
217
- handleError(
218
- `Invalid region: ${chalk.yellow(region)}.\nAvailable regions: ${chalk.green(
219
- regionIds.join(", ")
220
- )}`
221
- );
222
- }
223
-
224
- return region;
225
- }
226
-
227
- function handleError(message, extra = "") {
228
- console.error(
229
- "\n" +
230
- chalk.red.bold("āœ– An error occurred!") +
231
- "\n\n" +
232
- chalk.white("Message: ") +
233
- chalk.yellow(message) +
234
- (extra
235
- ? "\n" + chalk.white("Available regions: ") + chalk.green(extra)
236
- : "") +
237
- "\n"
238
- );
239
- process.exit(1);
240
- }
241
-
242
- async function promptForRegion(defaultRegion) {
243
- let regions;
244
- try {
245
- regions = await getRegions();
246
- } catch (e) {
247
- handleError("Failed to fetch regions.", e);
248
- }
249
-
250
- if (!regions || regions.length === 0) {
251
- handleError("No regions available to select.");
252
- }
253
-
254
- const region = await select({
255
- message: "Choose a region:",
256
- options: regions.map((r) => ({ value: r.id, label: r.id })),
257
- initialValue:
258
- regions.find((r) => r.id === defaultRegion)?.id || regions[0]?.id,
259
- });
260
-
261
- if (region === null) {
262
- cancel(chalk.red("Operation cancelled."));
263
- process.exit(0);
264
- }
265
-
266
- try {
267
- await analytics.capture("create_db:region_selected", {
268
- command: CLI_NAME,
269
- region: region,
270
- "selection-method": "interactive",
271
- });
272
- } catch (error) {}
273
-
274
- return region;
275
- }
276
-
277
- async function createDatabase(name, region, returnJson = false) {
278
- let s;
279
- if (!returnJson) {
280
- s = spinner();
281
- s.start("Creating your database...");
282
- }
283
-
284
- const resp = await fetch(`${CREATE_DB_WORKER_URL}/create`, {
285
- method: "POST",
286
- headers: { "Content-Type": "application/json" },
287
- body: JSON.stringify({ region, name, utm_source: CLI_NAME }),
288
- });
289
-
290
- if (resp.status === 429) {
291
- if (returnJson) {
292
- return {
293
- error: "rate_limit_exceeded",
294
- message:
295
- "We're experiencing a high volume of requests. Please try again later.",
296
- status: 429,
297
- };
298
- }
299
-
300
- if (s) {
301
- s.stop(
302
- "We're experiencing a high volume of requests. Please try again later."
303
- );
304
- }
305
-
306
- try {
307
- await analytics.capture("create_db:database_creation_failed", {
308
- command: CLI_NAME,
309
- region: region,
310
- "error-type": "rate_limit",
311
- "status-code": 429,
312
- });
313
- } catch (error) {}
314
-
315
- process.exit(1);
316
- }
317
-
318
- let result;
319
- let raw;
320
- try {
321
- raw = await resp.text();
322
- result = JSON.parse(raw);
323
- } catch (e) {
324
- if (returnJson) {
325
- return {
326
- error: "invalid_json",
327
- message: "Unexpected response from create service.",
328
- raw,
329
- status: resp.status,
330
- };
331
- }
332
- if (s) {
333
- s.stop("Unexpected response from create service.");
334
- }
335
- try {
336
- await analytics.capture("create_db:database_creation_failed", {
337
- command: CLI_NAME,
338
- region,
339
- "error-type": "invalid_json",
340
- "status-code": resp.status,
341
- });
342
- } catch {}
343
- process.exit(1);
344
- }
345
-
346
- const database = result.data ? result.data.database : result.databases?.[0];
347
- const projectId = result.data ? result.data.id : result.id;
348
- const prismaConn = database?.connectionString;
349
-
350
- const directConnDetails = result.data
351
- ? database?.apiKeys?.[0]?.directConnection
352
- : result.databases?.[0]?.apiKeys?.[0]?.ppgDirectConnection;
353
- const directUser = directConnDetails?.user
354
- ? encodeURIComponent(directConnDetails.user)
355
- : "";
356
- const directPass = directConnDetails?.pass
357
- ? encodeURIComponent(directConnDetails.pass)
358
- : "";
359
- const directHost = directConnDetails?.host;
360
- const directPort = directConnDetails?.port
361
- ? `:${directConnDetails.port}`
362
- : "";
363
- const directDbName = directConnDetails?.database || "postgres";
364
- const directConn =
365
- directConnDetails && directHost
366
- ? `postgresql://${directUser}:${directPass}@${directHost}${directPort}/${directDbName}`
367
- : null;
368
-
369
- const claimUrl = `${CLAIM_DB_WORKER_URL}?projectID=${projectId}&utm_source=${CLI_NAME}&utm_medium=cli`;
370
- const expiryDate = new Date(Date.now() + 24 * 60 * 60 * 1000);
371
-
372
- if (returnJson && !result.error) {
373
- return {
374
- connectionString: prismaConn,
375
- directConnectionString: directConn,
376
- claimUrl: claimUrl,
377
- deletionDate: expiryDate.toISOString(),
378
- region: database?.region?.id || region,
379
- name: database?.name,
380
- projectId: projectId,
381
- };
382
- }
383
-
384
- if (result.error) {
385
- if (returnJson) {
386
- return {
387
- error: "api_error",
388
- message: result.error.message || "Unknown error",
389
- details: result.error,
390
- };
391
- }
392
-
393
- if (s) {
394
- s.stop(
395
- `Error creating database: ${result.error.message || "Unknown error"}`
396
- );
397
- }
398
-
399
- try {
400
- await analytics.capture("create_db:database_creation_failed", {
401
- command: CLI_NAME,
402
- region: region,
403
- "error-type": "api_error",
404
- "error-message": result.error.message,
405
- });
406
- } catch (error) {}
407
- process.exit(1);
408
- }
409
-
410
- if (s) {
411
- s.stop("Database created successfully!");
412
- }
413
-
414
- const expiryFormatted = expiryDate.toLocaleString();
415
-
416
- log.message("");
417
-
418
- log.info(chalk.bold("Connect to your database →"));
419
-
420
- if (prismaConn) {
421
- log.message(
422
- chalk.magenta(" Use this connection string optimized for Prisma ORM:")
423
- );
424
- log.message(" " + chalk.yellow(prismaConn));
425
- log.message("");
426
- }
427
-
428
- if (directConn) {
429
- log.message(
430
- chalk.cyan(" Use this connection string for everything else:")
431
- );
432
- log.message(" " + chalk.yellow(directConn));
433
- log.message("");
434
- } else {
435
- log.warning(
436
- chalk.yellow(
437
- "Direct connection details are not available in the API response."
438
- )
439
- );
440
- }
441
-
442
- const clickableUrl = terminalLink(claimUrl, claimUrl, { fallback: false });
443
- log.success(`${chalk.bold("Claim your database →")}`);
444
- log.message(
445
- chalk.cyan(" Want to keep your database? Claim for free via this link:")
446
- );
447
- log.message(" " + chalk.yellow(clickableUrl));
448
- log.message(
449
- chalk.italic(
450
- chalk.gray(
451
- " Your database will be deleted on " +
452
- expiryFormatted +
453
- " if not claimed."
454
- )
455
- )
456
- );
457
- }
458
-
459
- async function main() {
460
- try {
461
- const rawArgs = process.argv.slice(2);
462
- try {
463
- await analytics.capture("create_db:cli_command_ran", {
464
- command: CLI_NAME,
465
- "full-command": `${CLI_NAME} ${rawArgs.join(" ")}`.trim(),
466
- "has-region-flag":
467
- rawArgs.includes("--region") || rawArgs.includes("-r"),
468
- "has-interactive-flag":
469
- rawArgs.includes("--interactive") || rawArgs.includes("-i"),
470
- "has-help-flag": rawArgs.includes("--help") || rawArgs.includes("-h"),
471
- "has-list-regions-flag": rawArgs.includes("--list-regions"),
472
- "has-json-flag": rawArgs.includes("--json") || rawArgs.includes("-j"),
473
- "node-version": process.version,
474
- platform: process.platform,
475
- arch: process.arch,
476
- });
477
- } catch (error) {}
478
-
479
- const { flags } = await parseArgs();
480
-
481
- if (!flags.help && !flags.json) {
482
- await isOffline();
483
- }
484
-
485
- let name = new Date().toISOString();
486
- let region = "us-east-1";
487
- let chooseRegionPrompt = false;
488
-
489
- if (flags.help) {
490
- return;
491
- }
492
-
493
- if (flags["list-regions"]) {
494
- await listRegions();
495
- process.exit(0);
496
- }
497
-
498
- if (flags.region) {
499
- region = flags.region;
500
-
501
- try {
502
- await analytics.capture("create_db:region_selected", {
503
- command: CLI_NAME,
504
- region: region,
505
- "selection-method": "flag",
506
- });
507
- } catch (error) {}
508
- }
509
-
510
- if (flags.interactive) {
511
- chooseRegionPrompt = true;
512
- }
513
-
514
- if (flags.json) {
515
- try {
516
- if (chooseRegionPrompt) {
517
- region = await promptForRegion(region);
518
- } else {
519
- await validateRegion(region, true);
520
- }
521
- const result = await createDatabase(name, region, true);
522
- console.log(JSON.stringify(result, null, 2));
523
- process.exit(0);
524
- } catch (e) {
525
- console.log(
526
- JSON.stringify(
527
- { error: "cli_error", message: e?.message || String(e) },
528
- null,
529
- 2
530
- )
531
- );
532
- process.exit(1);
533
- }
534
- }
535
-
536
- intro(chalk.cyan.bold("šŸš€ Creating a Prisma Postgres database"));
537
- log.message(
538
- chalk.white(`Provisioning a temporary database in ${region}...`)
539
- );
540
- log.message(
541
- chalk.gray(
542
- `It will be automatically deleted in 24 hours, but you can claim it.`
543
- )
544
- );
545
- if (chooseRegionPrompt) {
546
- region = await promptForRegion(region);
547
- }
548
-
549
- region = await validateRegion(region);
550
-
551
- await createDatabase(name, region);
552
-
553
- outro("");
554
- } catch (error) {
555
- console.error("Error:", error.message);
556
- process.exit(1);
557
- }
558
- }
559
-
560
- main();