create-db 1.0.3-pr48-DC-4894-posthog-fix-17269175017.0 → 1.0.3

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 +50 -0
  2. package/index.js +75 -38
  3. package/package.json +1 -1
package/analytics.js ADDED
@@ -0,0 +1,50 @@
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 };
package/index.js CHANGED
@@ -6,32 +6,80 @@ dotenv.config();
6
6
  import { select, spinner, intro, outro, log, cancel } from "@clack/prompts";
7
7
  import chalk from "chalk";
8
8
  import terminalLink from "terminal-link";
9
+ import { analytics } from "./analytics.js";
9
10
 
10
- async function sendAnalyticsToWorker(eventName, properties = {}) {
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 detectUserLocation() {
11
17
  try {
12
- const response = await fetch(`${CREATE_DB_WORKER_URL}/analytics`, {
13
- method: "POST",
14
- headers: { "Content-Type": "application/json" },
15
- body: JSON.stringify({ eventName, properties }),
18
+ const response = await fetch("https://ipapi.co/json/", {
19
+ method: "GET",
20
+ headers: {
21
+ "User-Agent": "create-db-cli/1.0",
22
+ },
16
23
  });
17
24
 
18
25
  if (!response.ok) {
19
- throw new Error(
20
- `Analytics request failed: ${response.status} ${response.statusText}`
21
- );
26
+ throw new Error(`Failed to fetch location data: ${response.status}`);
22
27
  }
23
28
 
24
- const result = await response.json();
25
- if (result.status === "success") {
26
- } else {
27
- }
28
- } catch (error) {}
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
+ }
29
41
  }
30
42
 
31
- const CREATE_DB_WORKER_URL =
32
- process.env.CREATE_DB_WORKER_URL || "https://create-db-temp.prisma.io";
33
- const CLAIM_DB_WORKER_URL =
34
- process.env.CLAIM_DB_WORKER_URL || "https://create-db.prisma.io";
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
+ }
35
83
 
36
84
  async function listRegions() {
37
85
  try {
@@ -284,7 +332,7 @@ async function promptForRegion(defaultRegion) {
284
332
  }
285
333
 
286
334
  try {
287
- await sendAnalyticsToWorker("create_db:region_selected", {
335
+ await analytics.capture("create_db:region_selected", {
288
336
  command: CLI_NAME,
289
337
  region: region,
290
338
  "selection-method": "interactive",
@@ -304,19 +352,7 @@ async function createDatabase(name, region, returnJson = false) {
304
352
  const resp = await fetch(`${CREATE_DB_WORKER_URL}/create`, {
305
353
  method: "POST",
306
354
  headers: { "Content-Type": "application/json" },
307
- body: JSON.stringify({
308
- region,
309
- name,
310
- utm_source: CLI_NAME,
311
- analytics: {
312
- eventName: "create_db:database_created",
313
- properties: {
314
- command: CLI_NAME,
315
- region: region,
316
- utm_source: CLI_NAME,
317
- },
318
- },
319
- }),
355
+ body: JSON.stringify({ region, name, utm_source: CLI_NAME }),
320
356
  });
321
357
 
322
358
  if (resp.status === 429) {
@@ -336,7 +372,7 @@ async function createDatabase(name, region, returnJson = false) {
336
372
  }
337
373
 
338
374
  try {
339
- await sendAnalyticsToWorker("create_db:database_creation_failed", {
375
+ await analytics.capture("create_db:database_creation_failed", {
340
376
  command: CLI_NAME,
341
377
  region: region,
342
378
  "error-type": "rate_limit",
@@ -365,13 +401,13 @@ async function createDatabase(name, region, returnJson = false) {
365
401
  s.stop("Unexpected response from create service.");
366
402
  }
367
403
  try {
368
- await sendAnalyticsToWorker("create_db:database_creation_failed", {
404
+ await analytics.capture("create_db:database_creation_failed", {
369
405
  command: CLI_NAME,
370
406
  region,
371
407
  "error-type": "invalid_json",
372
408
  "status-code": resp.status,
373
409
  });
374
- } catch (error) {}
410
+ } catch {}
375
411
  process.exit(1);
376
412
  }
377
413
 
@@ -429,7 +465,7 @@ async function createDatabase(name, region, returnJson = false) {
429
465
  }
430
466
 
431
467
  try {
432
- await sendAnalyticsToWorker("create_db:database_creation_failed", {
468
+ await analytics.capture("create_db:database_creation_failed", {
433
469
  command: CLI_NAME,
434
470
  region: region,
435
471
  "error-type": "api_error",
@@ -492,7 +528,7 @@ async function main() {
492
528
  try {
493
529
  const rawArgs = process.argv.slice(2);
494
530
  try {
495
- await sendAnalyticsToWorker("create_db:cli_command_ran", {
531
+ await analytics.capture("create_db:cli_command_ran", {
496
532
  command: CLI_NAME,
497
533
  "full-command": `${CLI_NAME} ${rawArgs.join(" ")}`.trim(),
498
534
  "has-region-flag":
@@ -515,7 +551,8 @@ async function main() {
515
551
  }
516
552
 
517
553
  let name = new Date().toISOString();
518
- let region = "us-east-1";
554
+ let userLocation = await detectUserLocation();
555
+ let region = getRegionClosestToLocation(userLocation) || "us-east-1";
519
556
  let chooseRegionPrompt = false;
520
557
 
521
558
  if (flags.help) {
@@ -531,7 +568,7 @@ async function main() {
531
568
  region = flags.region;
532
569
 
533
570
  try {
534
- await sendAnalyticsToWorker("create_db:region_selected", {
571
+ await analytics.capture("create_db:region_selected", {
535
572
  command: CLI_NAME,
536
573
  region: region,
537
574
  "selection-method": "flag",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-db",
3
- "version": "1.0.3-pr48-DC-4894-posthog-fix-17269175017.0",
3
+ "version": "1.0.3",
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": "",