create-db 1.0.3-pr45-DC-4829-source-flag-17164819065.0 → 1.0.3-pr47-DC-4759-set-region-to-be-near-user-17250359916.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.
Files changed (3) hide show
  1. package/analytics.js +4 -11
  2. package/index.js +93 -112
  3. package/package.json +1 -1
package/analytics.js CHANGED
@@ -8,17 +8,10 @@ class EventCaptureError extends Error {
8
8
 
9
9
  class PosthogEventCapture {
10
10
  async capture(eventName, properties = {}) {
11
- const POSTHOG_API_HOST = process.env.POSTHOG_API_HOST;
12
- const POSTHOG_KEY = process.env.POSTHOG_API_KEY;
13
- if (!POSTHOG_API_HOST || !POSTHOG_KEY) {
14
- if (process.env.NODE_ENV === "development") {
15
- console.warn(
16
- "Analytics disabled: missing POSTHOG_API_HOST or POSTHOG_API_KEY."
17
- );
18
- }
19
- return;
20
- }
21
- const POSTHOG_CAPTURE_URL = `${POSTHOG_API_HOST.replace(/\/+$/, "")}/capture`;
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";
22
15
 
23
16
  const payload = {
24
17
  api_key: POSTHOG_KEY,
package/index.js CHANGED
@@ -1,8 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import dotenv from "dotenv";
4
- import fs from "fs";
5
- import path from "path";
6
4
  dotenv.config();
7
5
 
8
6
  import { select, spinner, intro, outro, log, cancel } from "@clack/prompts";
@@ -15,6 +13,74 @@ const CREATE_DB_WORKER_URL =
15
13
  const CLAIM_DB_WORKER_URL =
16
14
  process.env.CLAIM_DB_WORKER_URL || "https://create-db.prisma.io";
17
15
 
16
+ async function detectUserLocation() {
17
+ try {
18
+ const response = await fetch("https://ipapi.co/json/", {
19
+ method: "GET",
20
+ headers: {
21
+ "User-Agent": "create-db-cli/1.0",
22
+ },
23
+ });
24
+
25
+ if (!response.ok) {
26
+ throw new Error(`Failed to fetch location data: ${response.status}`);
27
+ }
28
+
29
+ const data = await response.json();
30
+ return {
31
+ country: data.country_code,
32
+ continent: data.continent_code,
33
+ city: data.city,
34
+ region: data.region,
35
+ latitude: data.latitude,
36
+ longitude: data.longitude,
37
+ };
38
+ } catch (error) {
39
+ return null;
40
+ }
41
+ }
42
+
43
+ // Region coordinates (latitude, longitude)
44
+ const REGION_COORDINATES = {
45
+ "ap-southeast-1": { lat: 1.3521, lng: 103.8198 }, // Singapore
46
+ "ap-northeast-1": { lat: 35.6762, lng: 139.6503 }, // Tokyo
47
+ "eu-central-1": { lat: 50.1109, lng: 8.6821 }, // Frankfurt
48
+ "eu-west-3": { lat: 48.8566, lng: 2.3522 }, // Paris
49
+ "us-east-1": { lat: 38.9072, lng: -77.0369 }, // N. Virginia
50
+ "us-west-1": { lat: 37.7749, lng: -122.4194 }, // N. California
51
+ };
52
+
53
+ function getRegionClosestToLocation(userLocation) {
54
+ if (!userLocation) return null;
55
+
56
+ const userLat = parseFloat(userLocation.latitude);
57
+ const userLng = parseFloat(userLocation.longitude);
58
+
59
+ let closestRegion = null;
60
+ let minDistance = Infinity;
61
+
62
+ for (const [region, coordinates] of Object.entries(REGION_COORDINATES)) {
63
+ // Simple distance calculation using Haversine formula
64
+ const latDiff = ((userLat - coordinates.lat) * Math.PI) / 180;
65
+ const lngDiff = ((userLng - coordinates.lng) * Math.PI) / 180;
66
+ const a =
67
+ Math.sin(latDiff / 2) * Math.sin(latDiff / 2) +
68
+ Math.cos((userLat * Math.PI) / 180) *
69
+ Math.cos((coordinates.lat * Math.PI) / 180) *
70
+ Math.sin(lngDiff / 2) *
71
+ Math.sin(lngDiff / 2);
72
+ const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
73
+ const distance = 6371 * c; // Earth radius in km
74
+
75
+ if (distance < minDistance) {
76
+ minDistance = distance;
77
+ closestRegion = region;
78
+ }
79
+ }
80
+
81
+ return closestRegion;
82
+ }
83
+
18
84
  async function listRegions() {
19
85
  try {
20
86
  const regions = await getRegions();
@@ -59,31 +125,6 @@ function getCommandName() {
59
125
 
60
126
  const CLI_NAME = getCommandName();
61
127
 
62
- function readUserEnvFile() {
63
- const userCwd = process.cwd();
64
- const envPath = path.join(userCwd, ".env");
65
-
66
- if (!fs.existsSync(envPath)) {
67
- return {};
68
- }
69
-
70
- const envContent = fs.readFileSync(envPath, "utf8");
71
- const envVars = {};
72
-
73
- envContent.split("\n").forEach((line) => {
74
- const trimmed = line.trim();
75
- if (trimmed && !trimmed.startsWith("#")) {
76
- const [key, ...valueParts] = trimmed.split("=");
77
- if (key && valueParts.length > 0) {
78
- const value = valueParts.join("=").replace(/^["']|["']$/g, "");
79
- envVars[key.trim()] = value.trim();
80
- }
81
- }
82
- });
83
-
84
- return envVars;
85
- }
86
-
87
128
  async function showHelp() {
88
129
  let regionExamples = "us-east-1, eu-west-1";
89
130
  try {
@@ -266,7 +307,7 @@ function handleError(message, extra = "") {
266
307
  process.exit(1);
267
308
  }
268
309
 
269
- async function promptForRegion(defaultRegion, userAgent) {
310
+ async function promptForRegion(defaultRegion) {
270
311
  let regions;
271
312
  try {
272
313
  regions = await getRegions();
@@ -291,23 +332,17 @@ async function promptForRegion(defaultRegion, userAgent) {
291
332
  }
292
333
 
293
334
  try {
294
- const analyticsProps = {
335
+ await analytics.capture("create_db:region_selected", {
295
336
  command: CLI_NAME,
296
337
  region: region,
297
338
  "selection-method": "interactive",
298
- };
299
-
300
- if (userAgent) {
301
- analyticsProps["user-agent"] = userAgent;
302
- }
303
-
304
- await analytics.capture("create_db:region_selected", analyticsProps);
339
+ });
305
340
  } catch (error) {}
306
341
 
307
342
  return region;
308
343
  }
309
344
 
310
- async function createDatabase(name, region, userAgent, returnJson = false) {
345
+ async function createDatabase(name, region, returnJson = false) {
311
346
  let s;
312
347
  if (!returnJson) {
313
348
  s = spinner();
@@ -317,7 +352,7 @@ async function createDatabase(name, region, userAgent, returnJson = false) {
317
352
  const resp = await fetch(`${CREATE_DB_WORKER_URL}/create`, {
318
353
  method: "POST",
319
354
  headers: { "Content-Type": "application/json" },
320
- body: JSON.stringify({ region, name, utm_source: userAgent }),
355
+ body: JSON.stringify({ region, name, utm_source: CLI_NAME }),
321
356
  });
322
357
 
323
358
  if (resp.status === 429) {
@@ -337,21 +372,12 @@ async function createDatabase(name, region, userAgent, returnJson = false) {
337
372
  }
338
373
 
339
374
  try {
340
- const analyticsProps = {
375
+ await analytics.capture("create_db:database_creation_failed", {
341
376
  command: CLI_NAME,
342
377
  region: region,
343
378
  "error-type": "rate_limit",
344
379
  "status-code": 429,
345
- };
346
-
347
- if (userAgent) {
348
- analyticsProps["user-agent"] = userAgent;
349
- }
350
-
351
- await analytics.capture(
352
- "create_db:database_creation_failed",
353
- analyticsProps
354
- );
380
+ });
355
381
  } catch (error) {}
356
382
 
357
383
  process.exit(1);
@@ -375,21 +401,12 @@ async function createDatabase(name, region, userAgent, returnJson = false) {
375
401
  s.stop("Unexpected response from create service.");
376
402
  }
377
403
  try {
378
- const analyticsProps = {
404
+ await analytics.capture("create_db:database_creation_failed", {
379
405
  command: CLI_NAME,
380
406
  region,
381
407
  "error-type": "invalid_json",
382
408
  "status-code": resp.status,
383
- };
384
-
385
- if (userAgent) {
386
- analyticsProps["user-agent"] = userAgent;
387
- }
388
-
389
- await analytics.capture(
390
- "create_db:database_creation_failed",
391
- analyticsProps
392
- );
409
+ });
393
410
  } catch {}
394
411
  process.exit(1);
395
412
  }
@@ -417,11 +434,11 @@ async function createDatabase(name, region, userAgent, returnJson = false) {
417
434
  ? `postgresql://${directUser}:${directPass}@${directHost}${directPort}/${directDbName}`
418
435
  : null;
419
436
 
420
- const claimUrl = `${CLAIM_DB_WORKER_URL}?projectID=${projectId}&utm_source=${userAgent}&utm_medium=cli`;
437
+ const claimUrl = `${CLAIM_DB_WORKER_URL}?projectID=${projectId}&utm_source=${CLI_NAME}&utm_medium=cli`;
421
438
  const expiryDate = new Date(Date.now() + 24 * 60 * 60 * 1000);
422
439
 
423
440
  if (returnJson && !result.error) {
424
- const jsonResponse = {
441
+ return {
425
442
  connectionString: prismaConn,
426
443
  directConnectionString: directConn,
427
444
  claimUrl: claimUrl,
@@ -430,12 +447,6 @@ async function createDatabase(name, region, userAgent, returnJson = false) {
430
447
  name: database?.name,
431
448
  projectId: projectId,
432
449
  };
433
-
434
- if (userAgent) {
435
- jsonResponse.source = userAgent;
436
- }
437
-
438
- return jsonResponse;
439
450
  }
440
451
 
441
452
  if (result.error) {
@@ -454,21 +465,12 @@ async function createDatabase(name, region, userAgent, returnJson = false) {
454
465
  }
455
466
 
456
467
  try {
457
- const analyticsProps = {
468
+ await analytics.capture("create_db:database_creation_failed", {
458
469
  command: CLI_NAME,
459
470
  region: region,
460
471
  "error-type": "api_error",
461
472
  "error-message": result.error.message,
462
- };
463
-
464
- if (userAgent) {
465
- analyticsProps["user-agent"] = userAgent;
466
- }
467
-
468
- await analytics.capture(
469
- "create_db:database_creation_failed",
470
- analyticsProps
471
- );
473
+ });
472
474
  } catch (error) {}
473
475
  process.exit(1);
474
476
  }
@@ -525,17 +527,8 @@ async function createDatabase(name, region, userAgent, returnJson = false) {
525
527
  async function main() {
526
528
  try {
527
529
  const rawArgs = process.argv.slice(2);
528
-
529
- const { flags } = await parseArgs();
530
-
531
- let userAgent;
532
- const userEnvVars = readUserEnvFile();
533
- if (userEnvVars.PRISMA_ACTOR_NAME && userEnvVars.PRISMA_ACTOR_PROJECT) {
534
- userAgent = `${userEnvVars.PRISMA_ACTOR_NAME}/${userEnvVars.PRISMA_ACTOR_PROJECT}`;
535
- }
536
-
537
530
  try {
538
- const analyticsProps = {
531
+ await analytics.capture("create_db:cli_command_ran", {
539
532
  command: CLI_NAME,
540
533
  "full-command": `${CLI_NAME} ${rawArgs.join(" ")}`.trim(),
541
534
  "has-region-flag":
@@ -545,27 +538,21 @@ async function main() {
545
538
  "has-help-flag": rawArgs.includes("--help") || rawArgs.includes("-h"),
546
539
  "has-list-regions-flag": rawArgs.includes("--list-regions"),
547
540
  "has-json-flag": rawArgs.includes("--json") || rawArgs.includes("-j"),
548
- "has-source-from-env": !!userAgent,
549
541
  "node-version": process.version,
550
542
  platform: process.platform,
551
543
  arch: process.arch,
552
- };
553
-
554
- if (userAgent) {
555
- analyticsProps["user-agent"] = userAgent;
556
- }
544
+ });
545
+ } catch (error) {}
557
546
 
558
- await analytics.capture("create_db:cli_command_ran", analyticsProps);
559
- } catch (error) {
560
- console.error("Error:", error.message);
561
- }
547
+ const { flags } = await parseArgs();
562
548
 
563
549
  if (!flags.help && !flags.json) {
564
550
  await isOffline();
565
551
  }
566
552
 
567
553
  let name = new Date().toISOString();
568
- let region = "us-east-1";
554
+ let userLocation = await detectUserLocation();
555
+ let region = getRegionClosestToLocation(userLocation) || "us-east-1";
569
556
  let chooseRegionPrompt = false;
570
557
 
571
558
  if (flags.help) {
@@ -581,17 +568,11 @@ async function main() {
581
568
  region = flags.region;
582
569
 
583
570
  try {
584
- const analyticsProps = {
571
+ await analytics.capture("create_db:region_selected", {
585
572
  command: CLI_NAME,
586
573
  region: region,
587
574
  "selection-method": "flag",
588
- };
589
-
590
- if (userAgent) {
591
- analyticsProps["user-agent"] = userAgent;
592
- }
593
-
594
- await analytics.capture("create_db:region_selected", analyticsProps);
575
+ });
595
576
  } catch (error) {}
596
577
  }
597
578
 
@@ -602,11 +583,11 @@ async function main() {
602
583
  if (flags.json) {
603
584
  try {
604
585
  if (chooseRegionPrompt) {
605
- region = await promptForRegion(region, userAgent);
586
+ region = await promptForRegion(region);
606
587
  } else {
607
588
  await validateRegion(region, true);
608
589
  }
609
- const result = await createDatabase(name, region, userAgent, true);
590
+ const result = await createDatabase(name, region, true);
610
591
  console.log(JSON.stringify(result, null, 2));
611
592
  process.exit(0);
612
593
  } catch (e) {
@@ -631,12 +612,12 @@ async function main() {
631
612
  )
632
613
  );
633
614
  if (chooseRegionPrompt) {
634
- region = await promptForRegion(region, userAgent);
615
+ region = await promptForRegion(region);
635
616
  }
636
617
 
637
618
  region = await validateRegion(region);
638
619
 
639
- await createDatabase(name, region, userAgent);
620
+ await createDatabase(name, region);
640
621
 
641
622
  outro("");
642
623
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-db",
3
- "version": "1.0.3-pr45-DC-4829-source-flag-17164819065.0",
3
+ "version": "1.0.3-pr47-DC-4759-set-region-to-be-near-user-17250359916.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
  "main": "index.js",
6
6
  "author": "",