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