create-db 1.1.1-pr66-version-bump-19346362281.0 → 1.1.1-pr67-overhaul-19905444027.0
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.d.mts +1 -0
- package/dist/cli.mjs +8 -0
- package/dist/index.d.mts +84 -0
- package/dist/index.mjs +4 -0
- package/dist/src-CWxeHoyI.mjs +464 -0
- package/package.json +33 -15
- package/index.js +0 -836
package/dist/cli.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/cli.mjs
ADDED
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import * as trpc_cli0 from "trpc-cli";
|
|
3
|
+
import z$1 from "zod";
|
|
4
|
+
|
|
5
|
+
//#region src/types.d.ts
|
|
6
|
+
declare const RegionSchema: z$1.ZodEnum<{
|
|
7
|
+
"ap-southeast-1": "ap-southeast-1";
|
|
8
|
+
"ap-northeast-1": "ap-northeast-1";
|
|
9
|
+
"eu-central-1": "eu-central-1";
|
|
10
|
+
"eu-west-3": "eu-west-3";
|
|
11
|
+
"us-east-1": "us-east-1";
|
|
12
|
+
"us-west-1": "us-west-1";
|
|
13
|
+
}>;
|
|
14
|
+
type RegionId = z$1.infer<typeof RegionSchema>;
|
|
15
|
+
interface Region {
|
|
16
|
+
id: string;
|
|
17
|
+
name?: string;
|
|
18
|
+
status: string;
|
|
19
|
+
}
|
|
20
|
+
interface DatabaseResult {
|
|
21
|
+
success: true;
|
|
22
|
+
connectionString: string | null;
|
|
23
|
+
claimUrl: string;
|
|
24
|
+
deletionDate: string;
|
|
25
|
+
region: string;
|
|
26
|
+
name: string;
|
|
27
|
+
projectId: string;
|
|
28
|
+
userAgent?: string;
|
|
29
|
+
}
|
|
30
|
+
interface DatabaseError {
|
|
31
|
+
success: false;
|
|
32
|
+
error: string;
|
|
33
|
+
message: string;
|
|
34
|
+
raw?: string;
|
|
35
|
+
details?: unknown;
|
|
36
|
+
status?: number;
|
|
37
|
+
}
|
|
38
|
+
type CreateDatabaseResult = DatabaseResult | DatabaseError;
|
|
39
|
+
declare function isDatabaseError(result: CreateDatabaseResult): result is DatabaseError;
|
|
40
|
+
declare function isDatabaseSuccess(result: CreateDatabaseResult): result is DatabaseResult;
|
|
41
|
+
interface ProgrammaticCreateOptions {
|
|
42
|
+
region?: RegionId;
|
|
43
|
+
userAgent?: string;
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
//#region src/index.d.ts
|
|
47
|
+
declare function createDbCli(): trpc_cli0.TrpcCli;
|
|
48
|
+
/**
|
|
49
|
+
* Create a new Prisma Postgres database programmatically.
|
|
50
|
+
*
|
|
51
|
+
* @param options - Options for creating the database
|
|
52
|
+
* @param options.region - The AWS region for the database (optional)
|
|
53
|
+
* @param options.userAgent - Custom user agent string (optional)
|
|
54
|
+
* @returns A promise that resolves to either a {@link DatabaseResult} or {@link DatabaseError}
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```typescript
|
|
58
|
+
* import { create } from "create-db";
|
|
59
|
+
*
|
|
60
|
+
* const result = await create({ region: "us-east-1" });
|
|
61
|
+
*
|
|
62
|
+
* if (result.success) {
|
|
63
|
+
* console.log(`Connection string: ${result.connectionString}`);
|
|
64
|
+
* console.log(`Claim URL: ${result.claimUrl}`);
|
|
65
|
+
* } else {
|
|
66
|
+
* console.error(`Error: ${result.message}`);
|
|
67
|
+
* }
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
declare function create(options?: ProgrammaticCreateOptions): Promise<CreateDatabaseResult>;
|
|
71
|
+
/**
|
|
72
|
+
* List available Prisma Postgres regions programmatically.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* import { regions } from "create-db";
|
|
77
|
+
*
|
|
78
|
+
* const availableRegions = await regions();
|
|
79
|
+
* console.log(availableRegions);
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
declare function regions(): Promise<Region[]>;
|
|
83
|
+
//#endregion
|
|
84
|
+
export { type CreateDatabaseResult, type DatabaseError, type DatabaseResult, type ProgrammaticCreateOptions, type Region, type RegionId, RegionSchema, create, createDbCli, isDatabaseError, isDatabaseSuccess, regions };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { cancel, intro, isCancel, log, outro, select, spinner } from "@clack/prompts";
|
|
3
|
+
import { createRouterClient, os } from "@orpc/server";
|
|
4
|
+
import { randomUUID } from "crypto";
|
|
5
|
+
import dotenv from "dotenv";
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import path from "path";
|
|
8
|
+
import pc from "picocolors";
|
|
9
|
+
import terminalLink from "terminal-link";
|
|
10
|
+
import { createCli } from "trpc-cli";
|
|
11
|
+
import z$1, { z } from "zod";
|
|
12
|
+
|
|
13
|
+
//#region src/types.ts
|
|
14
|
+
const RegionSchema = z$1.enum([
|
|
15
|
+
"ap-southeast-1",
|
|
16
|
+
"ap-northeast-1",
|
|
17
|
+
"eu-central-1",
|
|
18
|
+
"eu-west-3",
|
|
19
|
+
"us-east-1",
|
|
20
|
+
"us-west-1"
|
|
21
|
+
]);
|
|
22
|
+
function isDatabaseError(result) {
|
|
23
|
+
return !result.success;
|
|
24
|
+
}
|
|
25
|
+
function isDatabaseSuccess(result) {
|
|
26
|
+
return result.success;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/index.ts
|
|
31
|
+
dotenv.config({ quiet: true });
|
|
32
|
+
const CREATE_DB_WORKER_URL = process.env.CREATE_DB_WORKER_URL || "https://create-db-temp.prisma.io";
|
|
33
|
+
const CLAIM_DB_WORKER_URL = process.env.CLAIM_DB_WORKER_URL || "https://create-db.prisma.io";
|
|
34
|
+
const REGION_COORDINATES = {
|
|
35
|
+
"ap-southeast-1": {
|
|
36
|
+
lat: 1.3521,
|
|
37
|
+
lng: 103.8198
|
|
38
|
+
},
|
|
39
|
+
"ap-northeast-1": {
|
|
40
|
+
lat: 35.6762,
|
|
41
|
+
lng: 139.6503
|
|
42
|
+
},
|
|
43
|
+
"eu-central-1": {
|
|
44
|
+
lat: 50.1109,
|
|
45
|
+
lng: 8.6821
|
|
46
|
+
},
|
|
47
|
+
"eu-west-3": {
|
|
48
|
+
lat: 48.8566,
|
|
49
|
+
lng: 2.3522
|
|
50
|
+
},
|
|
51
|
+
"us-east-1": {
|
|
52
|
+
lat: 38.9072,
|
|
53
|
+
lng: -77.0369
|
|
54
|
+
},
|
|
55
|
+
"us-west-1": {
|
|
56
|
+
lat: 37.7749,
|
|
57
|
+
lng: -122.4194
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const pendingAnalytics = [];
|
|
61
|
+
async function sendAnalytics(eventName, properties, cliRunId) {
|
|
62
|
+
const controller = new AbortController();
|
|
63
|
+
const timer = setTimeout(() => controller.abort(), 5e3);
|
|
64
|
+
const promise = (async () => {
|
|
65
|
+
try {
|
|
66
|
+
await fetch(`${CREATE_DB_WORKER_URL}/analytics`, {
|
|
67
|
+
method: "POST",
|
|
68
|
+
headers: { "Content-Type": "application/json" },
|
|
69
|
+
body: JSON.stringify({
|
|
70
|
+
eventName,
|
|
71
|
+
properties: {
|
|
72
|
+
distinct_id: cliRunId,
|
|
73
|
+
...properties
|
|
74
|
+
}
|
|
75
|
+
}),
|
|
76
|
+
signal: controller.signal
|
|
77
|
+
});
|
|
78
|
+
} catch {} finally {
|
|
79
|
+
clearTimeout(timer);
|
|
80
|
+
}
|
|
81
|
+
})();
|
|
82
|
+
pendingAnalytics.push(promise);
|
|
83
|
+
}
|
|
84
|
+
async function flushAnalytics(maxWaitMs = 500) {
|
|
85
|
+
if (pendingAnalytics.length === 0) return;
|
|
86
|
+
await Promise.race([Promise.all(pendingAnalytics), new Promise((resolve) => setTimeout(resolve, maxWaitMs))]);
|
|
87
|
+
}
|
|
88
|
+
function getCommandName() {
|
|
89
|
+
const executable = process.argv[1] || "create-db";
|
|
90
|
+
if (executable.includes("create-pg")) return "create-pg";
|
|
91
|
+
if (executable.includes("create-postgres")) return "create-postgres";
|
|
92
|
+
return "create-db";
|
|
93
|
+
}
|
|
94
|
+
async function detectUserLocation() {
|
|
95
|
+
try {
|
|
96
|
+
const response = await fetch("https://ipapi.co/json/", {
|
|
97
|
+
method: "GET",
|
|
98
|
+
headers: { "User-Agent": "create-db-cli/1.0" }
|
|
99
|
+
});
|
|
100
|
+
if (!response.ok) return null;
|
|
101
|
+
const data = await response.json();
|
|
102
|
+
return {
|
|
103
|
+
country: data.country_code,
|
|
104
|
+
continent: data.continent_code,
|
|
105
|
+
city: data.city,
|
|
106
|
+
region: data.region,
|
|
107
|
+
latitude: data.latitude,
|
|
108
|
+
longitude: data.longitude
|
|
109
|
+
};
|
|
110
|
+
} catch {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function getRegionClosestToLocation(userLocation) {
|
|
115
|
+
if (!userLocation) return null;
|
|
116
|
+
const userLat = parseFloat(String(userLocation.latitude));
|
|
117
|
+
const userLng = parseFloat(String(userLocation.longitude));
|
|
118
|
+
if (isNaN(userLat) || isNaN(userLng)) return null;
|
|
119
|
+
let closestRegion = null;
|
|
120
|
+
let minDistance = Infinity;
|
|
121
|
+
for (const [region, coordinates] of Object.entries(REGION_COORDINATES)) {
|
|
122
|
+
const latDiff = (userLat - coordinates.lat) * Math.PI / 180;
|
|
123
|
+
const lngDiff = (userLng - coordinates.lng) * Math.PI / 180;
|
|
124
|
+
const a = Math.sin(latDiff / 2) ** 2 + Math.cos(userLat * Math.PI / 180) * Math.cos(coordinates.lat * Math.PI / 180) * Math.sin(lngDiff / 2) ** 2;
|
|
125
|
+
const distance = 6371 * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
126
|
+
if (distance < minDistance) {
|
|
127
|
+
minDistance = distance;
|
|
128
|
+
closestRegion = region;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return closestRegion;
|
|
132
|
+
}
|
|
133
|
+
function readUserEnvFile() {
|
|
134
|
+
const envPath = path.join(process.cwd(), ".env");
|
|
135
|
+
if (!fs.existsSync(envPath)) return {};
|
|
136
|
+
const envContent = fs.readFileSync(envPath, "utf8");
|
|
137
|
+
const envVars = {};
|
|
138
|
+
for (const line of envContent.split("\n")) {
|
|
139
|
+
const trimmed = line.trim();
|
|
140
|
+
if (trimmed && !trimmed.startsWith("#")) {
|
|
141
|
+
const [key, ...valueParts] = trimmed.split("=");
|
|
142
|
+
if (key && valueParts.length > 0) {
|
|
143
|
+
const value = valueParts.join("=").replace(/^["']|["']$/g, "");
|
|
144
|
+
envVars[key.trim()] = value.trim();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return envVars;
|
|
149
|
+
}
|
|
150
|
+
async function checkOnline() {
|
|
151
|
+
try {
|
|
152
|
+
if (!(await fetch(`${CREATE_DB_WORKER_URL}/health`)).ok) throw new Error("API not available");
|
|
153
|
+
} catch {
|
|
154
|
+
console.error(pc.bold(pc.red("\n✖ Error: Cannot reach Prisma Postgres API server.\n")));
|
|
155
|
+
console.error(pc.dim(`Check your internet connection or visit ${pc.green("https://www.prisma-status.com/")}\n`));
|
|
156
|
+
await flushAnalytics();
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async function getRegions() {
|
|
161
|
+
const res = await fetch(`${CREATE_DB_WORKER_URL}/regions`);
|
|
162
|
+
if (!res.ok) throw new Error(`Failed to fetch regions. Status: ${res.status} ${res.statusText}`);
|
|
163
|
+
const data = await res.json();
|
|
164
|
+
return (Array.isArray(data) ? data : data.data ?? []).filter((region) => region.status === "available");
|
|
165
|
+
}
|
|
166
|
+
async function validateRegion(region) {
|
|
167
|
+
const regionIds = (await getRegions()).map((r) => r.id);
|
|
168
|
+
if (!regionIds.includes(region)) throw new Error(`Invalid region: ${region}. Available regions: ${regionIds.join(", ")}`);
|
|
169
|
+
return region;
|
|
170
|
+
}
|
|
171
|
+
async function createDatabaseCore(region, userAgent, cliRunId) {
|
|
172
|
+
const name = (/* @__PURE__ */ new Date()).toISOString();
|
|
173
|
+
const runId = cliRunId ?? randomUUID();
|
|
174
|
+
const resp = await fetch(`${CREATE_DB_WORKER_URL}/create`, {
|
|
175
|
+
method: "POST",
|
|
176
|
+
headers: { "Content-Type": "application/json" },
|
|
177
|
+
body: JSON.stringify({
|
|
178
|
+
region,
|
|
179
|
+
name,
|
|
180
|
+
utm_source: getCommandName(),
|
|
181
|
+
userAgent
|
|
182
|
+
})
|
|
183
|
+
});
|
|
184
|
+
if (resp.status === 429) {
|
|
185
|
+
sendAnalytics("create_db:database_creation_failed", {
|
|
186
|
+
region,
|
|
187
|
+
"error-type": "rate_limit",
|
|
188
|
+
"status-code": 429
|
|
189
|
+
}, runId);
|
|
190
|
+
return {
|
|
191
|
+
success: false,
|
|
192
|
+
error: "rate_limit_exceeded",
|
|
193
|
+
message: "We're experiencing a high volume of requests. Please try again later.",
|
|
194
|
+
status: 429
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
let result;
|
|
198
|
+
let raw = "";
|
|
199
|
+
try {
|
|
200
|
+
raw = await resp.text();
|
|
201
|
+
result = JSON.parse(raw);
|
|
202
|
+
} catch {
|
|
203
|
+
sendAnalytics("create_db:database_creation_failed", {
|
|
204
|
+
region,
|
|
205
|
+
"error-type": "invalid_json",
|
|
206
|
+
"status-code": resp.status
|
|
207
|
+
}, runId);
|
|
208
|
+
return {
|
|
209
|
+
success: false,
|
|
210
|
+
error: "invalid_json",
|
|
211
|
+
message: "Unexpected response from create service.",
|
|
212
|
+
raw,
|
|
213
|
+
status: resp.status
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
if (result.error) {
|
|
217
|
+
sendAnalytics("create_db:database_creation_failed", {
|
|
218
|
+
region,
|
|
219
|
+
"error-type": "api_error",
|
|
220
|
+
"error-message": result.error.message
|
|
221
|
+
}, runId);
|
|
222
|
+
return {
|
|
223
|
+
success: false,
|
|
224
|
+
error: "api_error",
|
|
225
|
+
message: result.error.message || "Unknown error",
|
|
226
|
+
details: result.error,
|
|
227
|
+
status: result.error.status ?? resp.status
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
const database = result.data?.database ?? result.databases?.[0];
|
|
231
|
+
const projectId = result.data?.id ?? result.id ?? "";
|
|
232
|
+
const apiKeys = database?.apiKeys;
|
|
233
|
+
const directConnDetails = result.data ? apiKeys?.[0]?.directConnection : result.databases?.[0]?.apiKeys?.[0]?.ppgDirectConnection;
|
|
234
|
+
const directUser = directConnDetails?.user ? encodeURIComponent(String(directConnDetails.user)) : "";
|
|
235
|
+
const directPass = directConnDetails?.pass ? encodeURIComponent(String(directConnDetails.pass)) : "";
|
|
236
|
+
const directHost = directConnDetails?.host;
|
|
237
|
+
const directPort = directConnDetails?.port ? `:${directConnDetails.port}` : "";
|
|
238
|
+
const directDbName = directConnDetails?.database || "postgres";
|
|
239
|
+
const connectionString = directConnDetails && directHost ? `postgresql://${directUser}:${directPass}@${directHost}${directPort}/${directDbName}?sslmode=require` : null;
|
|
240
|
+
const claimUrl = `${CLAIM_DB_WORKER_URL}/claim?projectID=${projectId}&utm_source=${userAgent || getCommandName()}&utm_medium=cli`;
|
|
241
|
+
const expiryDate = new Date(Date.now() + 1440 * 60 * 1e3);
|
|
242
|
+
sendAnalytics("create_db:database_created", {
|
|
243
|
+
region,
|
|
244
|
+
utm_source: getCommandName()
|
|
245
|
+
}, runId);
|
|
246
|
+
return {
|
|
247
|
+
success: true,
|
|
248
|
+
connectionString,
|
|
249
|
+
claimUrl,
|
|
250
|
+
deletionDate: expiryDate.toISOString(),
|
|
251
|
+
region: database?.region?.id || region,
|
|
252
|
+
name: database?.name ?? name,
|
|
253
|
+
projectId,
|
|
254
|
+
userAgent
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
const router = os.router({
|
|
258
|
+
create: os.meta({
|
|
259
|
+
description: "Create a new Prisma Postgres database",
|
|
260
|
+
default: true
|
|
261
|
+
}).input(z.object({
|
|
262
|
+
region: RegionSchema.optional().describe("AWS region for the database").meta({ alias: "r" }),
|
|
263
|
+
interactive: z.boolean().optional().default(false).describe("Run in interactive mode to select a region").meta({ alias: "i" }),
|
|
264
|
+
json: z.boolean().optional().default(false).describe("Output machine-readable JSON").meta({ alias: "j" }),
|
|
265
|
+
env: z.string().optional().describe("Write DATABASE_URL and CLAIM_URL to the specified .env file").meta({ alias: "e" })
|
|
266
|
+
})).handler(async ({ input }) => {
|
|
267
|
+
const cliRunId = randomUUID();
|
|
268
|
+
const CLI_NAME = getCommandName();
|
|
269
|
+
let userAgent;
|
|
270
|
+
const userEnvVars = readUserEnvFile();
|
|
271
|
+
if (userEnvVars.PRISMA_ACTOR_NAME && userEnvVars.PRISMA_ACTOR_PROJECT) userAgent = `${userEnvVars.PRISMA_ACTOR_NAME}/${userEnvVars.PRISMA_ACTOR_PROJECT}`;
|
|
272
|
+
sendAnalytics("create_db:cli_command_ran", {
|
|
273
|
+
command: CLI_NAME,
|
|
274
|
+
"has-region-flag": !!input.region,
|
|
275
|
+
"has-interactive-flag": input.interactive,
|
|
276
|
+
"has-json-flag": input.json,
|
|
277
|
+
"has-env-flag": !!input.env,
|
|
278
|
+
"has-user-agent-from-env": !!userAgent,
|
|
279
|
+
"node-version": process.version,
|
|
280
|
+
platform: process.platform,
|
|
281
|
+
arch: process.arch
|
|
282
|
+
}, cliRunId);
|
|
283
|
+
let region = input.region ?? "us-east-1";
|
|
284
|
+
if (!input.region) region = getRegionClosestToLocation(await detectUserLocation()) ?? region;
|
|
285
|
+
const envPath = input.env;
|
|
286
|
+
const envEnabled = typeof envPath === "string" && envPath.trim().length > 0;
|
|
287
|
+
if (input.json || envEnabled) {
|
|
288
|
+
if (input.interactive) {
|
|
289
|
+
await checkOnline();
|
|
290
|
+
const regions$1 = await getRegions();
|
|
291
|
+
const selectedRegion = await select({
|
|
292
|
+
message: "Choose a region:",
|
|
293
|
+
options: regions$1.map((r) => ({
|
|
294
|
+
value: r.id,
|
|
295
|
+
label: r.name || r.id
|
|
296
|
+
})),
|
|
297
|
+
initialValue: regions$1.find((r) => r.id === region)?.id || regions$1[0]?.id
|
|
298
|
+
});
|
|
299
|
+
if (isCancel(selectedRegion)) {
|
|
300
|
+
cancel(pc.red("Operation cancelled."));
|
|
301
|
+
await flushAnalytics();
|
|
302
|
+
process.exit(0);
|
|
303
|
+
}
|
|
304
|
+
region = selectedRegion;
|
|
305
|
+
sendAnalytics("create_db:region_selected", {
|
|
306
|
+
region,
|
|
307
|
+
"selection-method": "interactive"
|
|
308
|
+
}, cliRunId);
|
|
309
|
+
} else if (input.region) {
|
|
310
|
+
await validateRegion(region);
|
|
311
|
+
sendAnalytics("create_db:region_selected", {
|
|
312
|
+
region,
|
|
313
|
+
"selection-method": "flag"
|
|
314
|
+
}, cliRunId);
|
|
315
|
+
}
|
|
316
|
+
await checkOnline();
|
|
317
|
+
const result$1 = await createDatabaseCore(region, userAgent, cliRunId);
|
|
318
|
+
await flushAnalytics();
|
|
319
|
+
if (input.json) {
|
|
320
|
+
console.log(JSON.stringify(result$1, null, 2));
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (!result$1.success) {
|
|
324
|
+
console.error(result$1.message);
|
|
325
|
+
process.exit(1);
|
|
326
|
+
}
|
|
327
|
+
try {
|
|
328
|
+
const targetEnvPath = envPath;
|
|
329
|
+
const lines = [
|
|
330
|
+
`DATABASE_URL="${result$1.connectionString ?? ""}"`,
|
|
331
|
+
`CLAIM_URL="${result$1.claimUrl}"`,
|
|
332
|
+
""
|
|
333
|
+
];
|
|
334
|
+
let prefix = "";
|
|
335
|
+
if (fs.existsSync(targetEnvPath)) {
|
|
336
|
+
const existing = fs.readFileSync(targetEnvPath, "utf8");
|
|
337
|
+
if (existing.length > 0 && !existing.endsWith("\n")) prefix = "\n";
|
|
338
|
+
}
|
|
339
|
+
fs.appendFileSync(targetEnvPath, prefix + lines.join("\n"), { encoding: "utf8" });
|
|
340
|
+
console.log(pc.green(`Wrote DATABASE_URL and CLAIM_URL to ${targetEnvPath}`));
|
|
341
|
+
} catch (err) {
|
|
342
|
+
console.error(pc.red(`Failed to write environment variables to ${envPath}: ${err instanceof Error ? err.message : String(err)}`));
|
|
343
|
+
process.exit(1);
|
|
344
|
+
}
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
await checkOnline();
|
|
348
|
+
intro(pc.bold(pc.cyan("🚀 Creating a Prisma Postgres database")));
|
|
349
|
+
if (input.interactive) {
|
|
350
|
+
const regions$1 = await getRegions();
|
|
351
|
+
const selectedRegion = await select({
|
|
352
|
+
message: "Choose a region:",
|
|
353
|
+
options: regions$1.map((r) => ({
|
|
354
|
+
value: r.id,
|
|
355
|
+
label: r.name || r.id
|
|
356
|
+
})),
|
|
357
|
+
initialValue: regions$1.find((r) => r.id === region)?.id || regions$1[0]?.id
|
|
358
|
+
});
|
|
359
|
+
if (isCancel(selectedRegion)) {
|
|
360
|
+
cancel(pc.red("Operation cancelled."));
|
|
361
|
+
await flushAnalytics();
|
|
362
|
+
process.exit(0);
|
|
363
|
+
}
|
|
364
|
+
region = selectedRegion;
|
|
365
|
+
sendAnalytics("create_db:region_selected", {
|
|
366
|
+
region,
|
|
367
|
+
"selection-method": "interactive"
|
|
368
|
+
}, cliRunId);
|
|
369
|
+
} else if (input.region) {
|
|
370
|
+
await validateRegion(region);
|
|
371
|
+
sendAnalytics("create_db:region_selected", {
|
|
372
|
+
region,
|
|
373
|
+
"selection-method": "flag"
|
|
374
|
+
}, cliRunId);
|
|
375
|
+
}
|
|
376
|
+
const s = spinner();
|
|
377
|
+
s.start(`Creating database in ${pc.cyan(region)}...`);
|
|
378
|
+
const result = await createDatabaseCore(region, userAgent, cliRunId);
|
|
379
|
+
if (!result.success) {
|
|
380
|
+
s.stop(pc.red(`Error: ${result.message}`));
|
|
381
|
+
await flushAnalytics();
|
|
382
|
+
process.exit(1);
|
|
383
|
+
}
|
|
384
|
+
s.stop(pc.green("Database created successfully!"));
|
|
385
|
+
const expiryFormatted = new Date(result.deletionDate).toLocaleString();
|
|
386
|
+
const clickableUrl = terminalLink(result.claimUrl, result.claimUrl, { fallback: false });
|
|
387
|
+
log.message("");
|
|
388
|
+
log.info(pc.bold("Database Connection"));
|
|
389
|
+
log.message("");
|
|
390
|
+
if (result.connectionString) {
|
|
391
|
+
log.message(pc.cyan(" Connection String:"));
|
|
392
|
+
log.message(" " + pc.yellow(result.connectionString));
|
|
393
|
+
log.message("");
|
|
394
|
+
} else {
|
|
395
|
+
log.warning(pc.yellow(" Connection details are not available."));
|
|
396
|
+
log.message("");
|
|
397
|
+
}
|
|
398
|
+
log.success(pc.bold("Claim Your Database"));
|
|
399
|
+
log.message(pc.cyan(" Keep your database for free:"));
|
|
400
|
+
log.message(" " + pc.yellow(clickableUrl));
|
|
401
|
+
log.message(pc.italic(pc.dim(` Database will be deleted on ${expiryFormatted} if not claimed.`)));
|
|
402
|
+
outro(pc.dim("Done!"));
|
|
403
|
+
await flushAnalytics();
|
|
404
|
+
}),
|
|
405
|
+
regions: os.meta({ description: "List available Prisma Postgres regions" }).handler(async () => {
|
|
406
|
+
const regions$1 = await getRegions();
|
|
407
|
+
log.message("");
|
|
408
|
+
log.info(pc.bold(pc.cyan("Available Prisma Postgres regions:")));
|
|
409
|
+
log.message("");
|
|
410
|
+
for (const r of regions$1) log.message(` ${pc.green(r.id)} - ${r.name || r.id}`);
|
|
411
|
+
log.message("");
|
|
412
|
+
})
|
|
413
|
+
});
|
|
414
|
+
function createDbCli() {
|
|
415
|
+
return createCli({
|
|
416
|
+
router,
|
|
417
|
+
name: getCommandName(),
|
|
418
|
+
version: "1.1.0",
|
|
419
|
+
description: "Instantly create a temporary Prisma Postgres database"
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
createRouterClient(router, { context: {} });
|
|
423
|
+
/**
|
|
424
|
+
* Create a new Prisma Postgres database programmatically.
|
|
425
|
+
*
|
|
426
|
+
* @param options - Options for creating the database
|
|
427
|
+
* @param options.region - The AWS region for the database (optional)
|
|
428
|
+
* @param options.userAgent - Custom user agent string (optional)
|
|
429
|
+
* @returns A promise that resolves to either a {@link DatabaseResult} or {@link DatabaseError}
|
|
430
|
+
*
|
|
431
|
+
* @example
|
|
432
|
+
* ```typescript
|
|
433
|
+
* import { create } from "create-db";
|
|
434
|
+
*
|
|
435
|
+
* const result = await create({ region: "us-east-1" });
|
|
436
|
+
*
|
|
437
|
+
* if (result.success) {
|
|
438
|
+
* console.log(`Connection string: ${result.connectionString}`);
|
|
439
|
+
* console.log(`Claim URL: ${result.claimUrl}`);
|
|
440
|
+
* } else {
|
|
441
|
+
* console.error(`Error: ${result.message}`);
|
|
442
|
+
* }
|
|
443
|
+
* ```
|
|
444
|
+
*/
|
|
445
|
+
async function create(options) {
|
|
446
|
+
return createDatabaseCore(options?.region || "us-east-1", options?.userAgent);
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* List available Prisma Postgres regions programmatically.
|
|
450
|
+
*
|
|
451
|
+
* @example
|
|
452
|
+
* ```typescript
|
|
453
|
+
* import { regions } from "create-db";
|
|
454
|
+
*
|
|
455
|
+
* const availableRegions = await regions();
|
|
456
|
+
* console.log(availableRegions);
|
|
457
|
+
* ```
|
|
458
|
+
*/
|
|
459
|
+
async function regions() {
|
|
460
|
+
return getRegions();
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
//#endregion
|
|
464
|
+
export { isDatabaseError as a, RegionSchema as i, createDbCli as n, isDatabaseSuccess as o, regions as r, create as t };
|
package/package.json
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-db",
|
|
3
|
-
"version": "1.1.1-
|
|
3
|
+
"version": "1.1.1-pr67-overhaul-19905444027.0",
|
|
4
4
|
"description": "Instantly create a temporary Prisma Postgres database with one command, then claim and persist it in your Prisma Data Platform project when ready.",
|
|
5
|
-
"
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.mts",
|
|
9
|
+
"import": "./dist/index.mjs"
|
|
10
|
+
},
|
|
11
|
+
"./cli": {
|
|
12
|
+
"import": "./dist/cli.mjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"main": "./dist/index.mjs",
|
|
16
|
+
"module": "./dist/index.mjs",
|
|
17
|
+
"types": "./dist/index.d.mts",
|
|
6
18
|
"author": "prisma",
|
|
7
19
|
"repository": {
|
|
8
20
|
"type": "git",
|
|
@@ -20,32 +32,38 @@
|
|
|
20
32
|
"temporary"
|
|
21
33
|
],
|
|
22
34
|
"license": "ISC",
|
|
23
|
-
"type": "module",
|
|
24
35
|
"bin": {
|
|
25
|
-
"create-db": "./
|
|
26
|
-
"create-postgres": "./
|
|
27
|
-
"create-pg": "./
|
|
36
|
+
"create-db": "./dist/cli.mjs",
|
|
37
|
+
"create-postgres": "./dist/cli.mjs",
|
|
38
|
+
"create-pg": "./dist/cli.mjs"
|
|
28
39
|
},
|
|
29
40
|
"dependencies": {
|
|
30
41
|
"@clack/prompts": "^0.11.0",
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"terminal-link": "^
|
|
42
|
+
"@orpc/server": "^1.12.2",
|
|
43
|
+
"dotenv": "^17.2.3",
|
|
44
|
+
"picocolors": "^1.1.1",
|
|
45
|
+
"terminal-link": "^5.0.0",
|
|
46
|
+
"trpc-cli": "^0.12.1",
|
|
47
|
+
"zod": "^4.1.13",
|
|
48
|
+
"execa": "^9.6.1"
|
|
35
49
|
},
|
|
36
50
|
"publishConfig": {
|
|
37
51
|
"access": "public"
|
|
38
52
|
},
|
|
39
53
|
"files": [
|
|
40
|
-
"
|
|
41
|
-
"README.md"
|
|
42
|
-
"analytics.js"
|
|
54
|
+
"dist",
|
|
55
|
+
"README.md"
|
|
43
56
|
],
|
|
44
57
|
"devDependencies": {
|
|
45
|
-
"
|
|
46
|
-
"
|
|
58
|
+
"@types/node": "^24.10.1",
|
|
59
|
+
"tsdown": "0.17.0-beta.5",
|
|
60
|
+
"typescript": "^5.9.3",
|
|
61
|
+
"vitest": "^4.0.15"
|
|
47
62
|
},
|
|
48
63
|
"scripts": {
|
|
64
|
+
"build": "tsdown",
|
|
65
|
+
"dev": "tsdown --watch",
|
|
66
|
+
"typecheck": "tsc --noEmit",
|
|
49
67
|
"test": "vitest run --reporter=verbose",
|
|
50
68
|
"test:watch": "vitest watch",
|
|
51
69
|
"test:coverage": "vitest run --coverage",
|