From 6824f9f4abe4f5af63f3dfa3b335f2ef93316d34 Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Wed, 3 Dec 2025 00:46:34 +0530 Subject: [PATCH 01/22] feat: add programmatic api --- create-db/__tests__/create.test.js | 31 - create-db/__tests__/create.test.ts | 39 + create-db/__tests__/regions.test.js | 58 - create-db/__tests__/regions.test.ts | 35 + create-db/__tests__/utils.test.js | 32 - create-db/__tests__/utils.test.ts | 36 + create-db/index.js | 836 ------------ create-db/package.json | 51 +- create-db/src/cli.ts | 4 + create-db/src/index.ts | 594 +++++++++ create-db/src/types.ts | 129 ++ create-db/tsconfig.json | 16 + create-db/tsdown.config.ts | 13 + create-db/vitest.config.ts | 8 + pnpm-lock.yaml | 1875 +++++++++++++++++++++++---- 15 files changed, 2507 insertions(+), 1250 deletions(-) delete mode 100644 create-db/__tests__/create.test.js create mode 100644 create-db/__tests__/create.test.ts delete mode 100644 create-db/__tests__/regions.test.js create mode 100644 create-db/__tests__/regions.test.ts delete mode 100644 create-db/__tests__/utils.test.js create mode 100644 create-db/__tests__/utils.test.ts delete mode 100755 create-db/index.js create mode 100644 create-db/src/cli.ts create mode 100644 create-db/src/index.ts create mode 100644 create-db/src/types.ts create mode 100644 create-db/tsconfig.json create mode 100644 create-db/tsdown.config.ts create mode 100644 create-db/vitest.config.ts diff --git a/create-db/__tests__/create.test.js b/create-db/__tests__/create.test.js deleted file mode 100644 index e048352..0000000 --- a/create-db/__tests__/create.test.js +++ /dev/null @@ -1,31 +0,0 @@ -import { describe, it, expect } from "vitest"; -import { execa } from "execa"; -import { fileURLToPath } from "url"; -import path from "path"; - -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const CLI_PATH = path.resolve(__dirname, "../index.js"); - -const runCli = async (args = [], options = {}) => { - return execa("node", [CLI_PATH, ...args], { - ...options, - env: { ...process.env, ...options.env }, - reject: false, - timeout: 15000, // 15 second timeout - }); -}; - -describe("database creation", () => { - ["create-db", "create-postgres", "create-pg"].forEach((command) => { - it(`creates database with ${command}`, async () => { - const { stdout } = await runCli([], { - env: { - ...process.env, - npm_package_name: command, - }, - }); - - expect(stdout).toContain("Database created successfully!"); - }, 20000); - }); -}); diff --git a/create-db/__tests__/create.test.ts b/create-db/__tests__/create.test.ts new file mode 100644 index 0000000..29dee72 --- /dev/null +++ b/create-db/__tests__/create.test.ts @@ -0,0 +1,39 @@ +import { describe, it, expect } from "vitest"; +import { execa } from "execa"; +import { fileURLToPath } from "url"; +import path from "path"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const CLI_PATH = path.resolve(__dirname, "../dist/cli.mjs"); + +const runCli = async ( + args: string[] = [], + options: { env?: Record;[key: string]: unknown } = {} +) => { + return execa("node", [CLI_PATH, ...args], { + ...options, + env: { ...process.env, ...options.env }, + reject: false, + timeout: 15000, + }); +}; + +describe("CLI database creation", () => { + it("creates database with default command", async () => { + const { stdout } = await runCli([]); + expect(stdout).toContain("Database created successfully!"); + }, 20000); + + it("creates database with --json flag", async () => { + const { stdout } = await runCli(["--json"]); + const result = JSON.parse(stdout); + expect(result).toHaveProperty("success"); + expect(result).toHaveProperty("connectionString"); + expect(result).toHaveProperty("claimUrl"); + }, 20000); + + it("lists regions with regions command", async () => { + const { stdout } = await runCli(["regions"]); + expect(stdout).toContain("Available Prisma Postgres regions"); + }, 20000); +}); diff --git a/create-db/__tests__/regions.test.js b/create-db/__tests__/regions.test.js deleted file mode 100644 index 79f3864..0000000 --- a/create-db/__tests__/regions.test.js +++ /dev/null @@ -1,58 +0,0 @@ -import { describe, it, expect, vi } from "vitest"; -import { - validateRegion, - getRegions, - getRegionClosestToLocation, -} from "../index.js"; - -global.fetch = vi.fn(() => - Promise.resolve({ - ok: true, - json: () => - Promise.resolve([ - { id: "eu-central-1", name: "Frankfurt", status: "available" }, - { id: "us-east-1", name: "N. Virginia", status: "available" }, - { id: "ap-southeast-1", name: "Singapore", status: "available" }, - ]), - }) -); - -describe("regions", () => { - describe("validateRegion()", () => { - it("validates existing regions", async () => { - expect(await validateRegion("eu-central-1", true)).toBe("eu-central-1"); - expect(await validateRegion("us-east-1", true)).toBe("us-east-1"); - expect(await validateRegion("ap-southeast-1", true)).toBe( - "ap-southeast-1" - ); - }); - - it("rejects invalid regions", async () => { - await expect(validateRegion("invalid-region", true)).rejects.toThrow(); - await expect(validateRegion("", true)).rejects.toThrow(); - await expect(validateRegion(null, true)).rejects.toThrow(); - }); - }); - - describe("getRegions()", () => { - it("returns available regions", async () => { - const regions = await getRegions(); - expect(Array.isArray(regions)).toBe(true); - expect(regions.length).toBeGreaterThan(0); - expect(regions[0]).toHaveProperty("id"); - }); - }); - - describe("getRegionClosestToLocation()", () => { - it("finds closest region", () => { - const berlin = { latitude: 52.52, longitude: 13.405 }; - expect(getRegionClosestToLocation(berlin)).toBe("eu-central-1"); - }); - - it("handles invalid input", () => { - expect(getRegionClosestToLocation(null)).toBe(null); - expect(getRegionClosestToLocation({})).toBe(null); - expect(getRegionClosestToLocation({ lat: "invalid" })).toBe(null); - }); - }); -}); diff --git a/create-db/__tests__/regions.test.ts b/create-db/__tests__/regions.test.ts new file mode 100644 index 0000000..0f6118e --- /dev/null +++ b/create-db/__tests__/regions.test.ts @@ -0,0 +1,35 @@ +import { describe, it, expect } from "vitest"; +import { regions, RegionSchema } from "../src/index.js"; + +describe("programmatic regions API", () => { + it("returns available regions", async () => { + const result = await regions(); + expect(Array.isArray(result)).toBe(true); + expect(result.length).toBeGreaterThan(0); + expect(result[0]).toHaveProperty("id"); + expect(result[0]).toHaveProperty("status"); + }, 10000); + + it("all regions have expected properties", async () => { + const result = await regions(); + for (const region of result) { + expect(region).toHaveProperty("id"); + expect(typeof region.id).toBe("string"); + expect(region).toHaveProperty("status"); + } + }, 10000); +}); + +describe("RegionSchema", () => { + it("validates valid region IDs", () => { + expect(RegionSchema.safeParse("us-east-1").success).toBe(true); + expect(RegionSchema.safeParse("eu-central-1").success).toBe(true); + expect(RegionSchema.safeParse("ap-southeast-1").success).toBe(true); + }); + + it("rejects invalid region IDs", () => { + expect(RegionSchema.safeParse("invalid-region").success).toBe(false); + expect(RegionSchema.safeParse("").success).toBe(false); + expect(RegionSchema.safeParse(123).success).toBe(false); + }); +}); diff --git a/create-db/__tests__/utils.test.js b/create-db/__tests__/utils.test.js deleted file mode 100644 index 520d225..0000000 --- a/create-db/__tests__/utils.test.js +++ /dev/null @@ -1,32 +0,0 @@ -import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; -import { getCommandName } from "../index.js"; - -describe("utils", () => { - describe("getCommandName()", () => { - const originalArgv = process.argv; - - beforeEach(() => { - vi.resetModules(); - process.argv = [...originalArgv]; - }); - - afterEach(() => { - process.argv = originalArgv; - }); - - it("detects create-db command", () => { - process.argv[1] = "../index.js"; - expect(getCommandName()).toBe("create-db"); - }); - - it("detects create-pg command", () => { - process.argv[1] = "../../create-pg/index.js"; - expect(getCommandName()).toBe("create-pg"); - }); - - it("detects create-postgres command", () => { - process.argv[1] = "../../create-postgres/index.js"; - expect(getCommandName()).toBe("create-postgres"); - }); - }); -}); diff --git a/create-db/__tests__/utils.test.ts b/create-db/__tests__/utils.test.ts new file mode 100644 index 0000000..0b7f36b --- /dev/null +++ b/create-db/__tests__/utils.test.ts @@ -0,0 +1,36 @@ +import { describe, it, expect } from "vitest"; +import { + isDatabaseError, + isDatabaseSuccess, + type CreateDatabaseResult, +} from "../src/index.js"; + +describe("type guards", () => { + describe("isDatabaseError()", () => { + it("returns true for error results", () => { + const error: CreateDatabaseResult = { + success: false, + error: "api_error", + message: "Something went wrong", + }; + expect(isDatabaseError(error)).toBe(true); + expect(isDatabaseSuccess(error)).toBe(false); + }); + }); + + describe("isDatabaseSuccess()", () => { + it("returns true for success results", () => { + const success: CreateDatabaseResult = { + success: true, + connectionString: "postgresql://...", + claimUrl: "https://...", + deletionDate: "2025-01-01", + region: "us-east-1", + name: "test-db", + projectId: "proj_123", + }; + expect(isDatabaseSuccess(success)).toBe(true); + expect(isDatabaseError(success)).toBe(false); + }); + }); +}); diff --git a/create-db/index.js b/create-db/index.js deleted file mode 100755 index d036c6b..0000000 --- a/create-db/index.js +++ /dev/null @@ -1,836 +0,0 @@ -#!/usr/bin/env node - -import { select, spinner, intro, outro, log, cancel } from "@clack/prompts"; -import { randomUUID } from "crypto"; -import dotenv from "dotenv"; -import fs from "fs"; -import path from "path"; -import terminalLink from "terminal-link"; -import chalk from "chalk"; - -dotenv.config(); - -const CREATE_DB_WORKER_URL = - process.env.CREATE_DB_WORKER_URL || "https://create-db-temp.prisma.io"; -const CLAIM_DB_WORKER_URL = - process.env.CLAIM_DB_WORKER_URL || "https://create-db.prisma.io"; - -// Track pending analytics promises to ensure they complete before exit -const pendingAnalytics = []; - -async function sendAnalyticsToWorker(eventName, properties, cliRunId) { - const controller = new AbortController(); - const timer = setTimeout(() => controller.abort(), 5000); - - const analyticsPromise = (async () => { - try { - const payload = { - eventName, - properties: { distinct_id: cliRunId, ...(properties || {}) }, - }; - await fetch(`${CREATE_DB_WORKER_URL}/analytics`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload), - signal: controller.signal, - }); - } catch (error) { - // Silently fail - analytics shouldn't block CLI - } finally { - clearTimeout(timer); - } - })(); - - pendingAnalytics.push(analyticsPromise); - return analyticsPromise; -} - -// Wait for all pending analytics with a timeout -async function flushAnalytics(maxWaitMs = 500) { - if (pendingAnalytics.length === 0) return; - - const timeout = new Promise((resolve) => setTimeout(resolve, maxWaitMs)); - const allAnalytics = Promise.all(pendingAnalytics); - - await Promise.race([allAnalytics, timeout]); -} - -async function detectUserLocation() { - try { - const response = await fetch("https://ipapi.co/json/", { - method: "GET", - headers: { - "User-Agent": "create-db-cli/1.0", - }, - }); - - if (!response.ok) { - throw new Error(`Failed to fetch location data: ${response.status}`); - } - - const data = await response.json(); - return { - country: data.country_code, - continent: data.continent_code, - city: data.city, - region: data.region, - latitude: data.latitude, - longitude: data.longitude, - }; - } catch (error) { - return null; - } -} - -const REGION_COORDINATES = { - "ap-southeast-1": { lat: 1.3521, lng: 103.8198 }, // Singapore - "ap-northeast-1": { lat: 35.6762, lng: 139.6503 }, // Tokyo - "eu-central-1": { lat: 50.1109, lng: 8.6821 }, // Frankfurt - "eu-west-3": { lat: 48.8566, lng: 2.3522 }, // Paris - "us-east-1": { lat: 38.9072, lng: -77.0369 }, // N. Virginia - "us-west-1": { lat: 37.7749, lng: -122.4194 }, // N. California -}; - -export function getRegionClosestToLocation(userLocation) { - if (!userLocation) return null; - - const userLat = parseFloat(userLocation.latitude); - const userLng = parseFloat(userLocation.longitude); - - let closestRegion = null; - let minDistance = Infinity; - - for (const [region, coordinates] of Object.entries(REGION_COORDINATES)) { - // Simple distance calculation using Haversine formula - const latDiff = ((userLat - coordinates.lat) * Math.PI) / 180; - const lngDiff = ((userLng - coordinates.lng) * Math.PI) / 180; - const a = - Math.sin(latDiff / 2) * Math.sin(latDiff / 2) + - Math.cos((userLat * Math.PI) / 180) * - Math.cos((coordinates.lat * Math.PI) / 180) * - Math.sin(lngDiff / 2) * - Math.sin(lngDiff / 2); - const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); - const distance = 6371 * c; // Earth radius in km - - if (distance < minDistance) { - minDistance = distance; - closestRegion = region; - } - } - - return closestRegion; -} - -async function listRegions() { - try { - const regions = await getRegions(); - console.log(chalk.cyan.bold("\n🌐 Available Prisma Postgres regions:\n")); - regions.forEach((r) => - console.log(`- ${chalk.green(r.id)}, ${r.name || r.id}`) - ); - console.log(""); - } catch (e) { - handleError("Failed to fetch regions.", e); - } -} - -async function isOffline() { - const healthUrl = `${CREATE_DB_WORKER_URL}/health`; - - try { - const res = await fetch(healthUrl, { method: "GET" }); - if (!res.ok) { - throw new Error(`Prisma Postgres API returned a status of ${res.status}`); - } - return false; // Online - } catch { - console.error( - chalk.red.bold("\nāœ– Error: Cannot reach Prisma Postgres API server.\n") - ); - console.error( - chalk.gray( - `Check your internet connection or visit ${chalk.green("https://www.prisma-status.com/\n")}` - ) - ); - await flushAnalytics(); - process.exit(1); - } -} - -export function getCommandName() { - const executable = process.argv[1] || "create-db"; - if (executable.includes("create-pg")) return "create-pg"; - if (executable.includes("create-postgres")) return "create-postgres"; - return "create-db"; -} - -const CLI_NAME = getCommandName(); - -function readUserEnvFile() { - const userCwd = process.cwd(); - const envPath = path.join(userCwd, ".env"); - - if (!fs.existsSync(envPath)) { - return {}; - } - - const envContent = fs.readFileSync(envPath, "utf8"); - const envVars = {}; - - envContent.split("\n").forEach((line) => { - const trimmed = line.trim(); - if (trimmed && !trimmed.startsWith("#")) { - const [key, ...valueParts] = trimmed.split("="); - if (key && valueParts.length > 0) { - const value = valueParts.join("=").replace(/^["']|["']$/g, ""); - envVars[key.trim()] = value.trim(); - } - } - }); - - return envVars; -} - -async function showHelp() { - let regionExamples = "us-east-1, eu-west-1"; - try { - const regions = await getRegions(); - if (regions && regions.length > 0) { - regionExamples = regions.map((r) => r.id).join(", "); - } - } catch {} - - console.log(` -${chalk.cyan.bold("Prisma Postgres Create DB")} - -Usage: - ${chalk.green(`npx ${CLI_NAME} [options]`)} - -Options: - ${chalk.yellow(`--region , -r `)} Specify the region (e.g., ${regionExamples}) - ${chalk.yellow("--interactive, -i")} Run in interactive mode to select a region and create the database - ${chalk.yellow("--json, -j")} Output machine-readable JSON and exit - ${chalk.yellow("--list-regions")} List available regions and exit - ${chalk.yellow("--help, -h")} Show this help message - ${chalk.yellow("--env, -e")} Prints DATABASE_URL to the terminal. ${chalk.gray(`To write to .env, use --env >> .env`)} - -Examples: - ${chalk.gray(`npx ${CLI_NAME} --region us-east-1`)} - ${chalk.gray(`npx ${CLI_NAME} -r us-east-1`)} - ${chalk.gray(`npx ${CLI_NAME} --interactive`)} - ${chalk.gray(`npx ${CLI_NAME} -i`)} - ${chalk.gray(`npx ${CLI_NAME} --json --region us-east-1`)} - ${chalk.gray(`npx ${CLI_NAME} --env --region us-east-1`)} - ${chalk.gray(`npx ${CLI_NAME} --env >> .env`)} -`); - await flushAnalytics(); - process.exit(0); -} - -async function parseArgs() { - const args = process.argv.slice(2); - const flags = {}; - - const allowedFlags = [ - "region", - "help", - "list-regions", - "interactive", - "json", - "env", - ]; - const shorthandMap = { - r: "region", - i: "interactive", - h: "help", - j: "json", - e: "env", - }; - - const exitWithError = (message) => { - console.error(chalk.red.bold("\nāœ– " + message)); - console.error(chalk.gray("\nUse --help or -h to see available options.\n")); - process.exit(1); - }; - - for (let i = 0; i < args.length; i++) { - const arg = args[i]; - - if (arg.startsWith("--")) { - const flag = arg.slice(2); - if (flag === "help") await showHelp(); - if (!allowedFlags.includes(flag)) - exitWithError(`Invalid flag: --${flag}`); - if (flag === "region") { - const region = args[i + 1]; - if (!region || region.startsWith("-")) - exitWithError("Missing value for --region flag."); - flags.region = region; - i++; - } else { - flags[flag] = true; - } - continue; - } - - if (arg.startsWith("-")) { - const short = arg.slice(1); - - if (shorthandMap[short]) { - const mappedFlag = shorthandMap[short]; - if (mappedFlag === "help") showHelp(); - if (mappedFlag === "region") { - const region = args[i + 1]; - if (!region || region.startsWith("-")) - exitWithError("Missing value for -r flag."); - flags.region = region; - i++; - } else { - flags[mappedFlag] = true; - } - continue; - } - - for (const letter of short.split("")) { - const mappedFlag = shorthandMap[letter]; - if (!mappedFlag) exitWithError(`Invalid flag: -${letter}`); - if (mappedFlag === "help") { - await showHelp(); - return; - } - if (mappedFlag === "region") { - const region = args[i + 1]; - if (!region || region.startsWith("-")) - exitWithError("Missing value for -r flag."); - flags.region = region; - i++; - } else { - flags[mappedFlag] = true; - } - } - continue; - } - - exitWithError(`Invalid argument: ${arg}`); - } - - return { flags }; -} - -function validateFlagCombinations(flags) { - const conflictingFlags = [ - ["env", "json"], - ["list-regions", "env"], - ["list-regions", "json"], - ["list-regions", "interactive"], - ["list-regions", "region"], - ["interactive", "env"], - ["interactive", "json"], - ]; - - for (const [flag1, flag2] of conflictingFlags) { - if (flags[flag1] && flags[flag2]) { - console.error( - chalk.red.bold( - `\nāœ– Error: Cannot use --${flag1} and --${flag2} together.\n` - ) - ); - console.error(chalk.gray("Use --help or -h to see available options.\n")); - process.exit(1); - } - } -} - -export async function getRegions(returnJson = false) { - const url = `${CREATE_DB_WORKER_URL}/regions`; - const res = await fetch(url); - - if (!res.ok) { - if (returnJson) { - throw new Error( - `Failed to fetch regions. Status: ${res.status} ${res.statusText}` - ); - } - handleError( - `Failed to fetch regions. Status: ${res.status} ${res.statusText}` - ); - } - - try { - const data = await res.json(); - const regions = Array.isArray(data) ? data : data.data; - return regions.filter((region) => region.status === "available"); - } catch (e) { - if (returnJson) { - throw new Error("Failed to parse JSON from /regions endpoint."); - } - handleError("Failed to parse JSON from /regions endpoint.", e); - } -} - -export async function validateRegion(region, returnJson = false) { - const regions = await getRegions(returnJson); - const regionIds = regions.map((r) => r.id); - - if (!regionIds.includes(region)) { - if (returnJson) { - throw new Error( - `Invalid region: ${region}. Available regions: ${regionIds.join(", ")}` - ); - } - handleError( - `Invalid region: ${chalk.yellow(region)}.\nAvailable regions: ${chalk.green( - regionIds.join(", ") - )}` - ); - } - - return region; -} - -function handleError(message, extra = "") { - console.error( - "\n" + - chalk.red.bold("āœ– An error occurred!") + - "\n\n" + - chalk.white("Message: ") + - chalk.yellow(message) + - (extra - ? "\n" + chalk.white("Available regions: ") + chalk.green(extra) - : "") + - "\n" - ); - process.exit(1); -} - -async function promptForRegion(defaultRegion, userAgent, cliRunId) { - let regions; - try { - regions = await getRegions(); - } catch (e) { - handleError("Failed to fetch regions.", e); - } - - if (!regions || regions.length === 0) { - handleError("No regions available to select."); - } - - const region = await select({ - message: "Choose a region:", - options: regions.map((r) => ({ value: r.id, label: r.id })), - initialValue: - regions.find((r) => r.id === defaultRegion)?.id || regions[0]?.id, - }); - - if (region === null) { - cancel(chalk.red("Operation cancelled.")); - await flushAnalytics(); - process.exit(0); - } - - void sendAnalyticsToWorker( - "create_db:region_selected", - { - command: CLI_NAME, - region: region, - "selection-method": "interactive", - "user-agent": userAgent, - }, - cliRunId - ); - - return region; -} - -async function createDatabase( - name, - region, - userAgent, - cliRunId, - silent = false -) { - let s; - if (!silent) { - s = spinner(); - s.start("Creating your database..."); - } - - const resp = await fetch(`${CREATE_DB_WORKER_URL}/create`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - region, - name, - utm_source: CLI_NAME, - userAgent, - }), - }); - - if (resp.status === 429) { - if (silent) { - return { - error: "rate_limit_exceeded", - message: - "We're experiencing a high volume of requests. Please try again later.", - status: 429, - }; - } - - if (s) { - s.stop( - "We're experiencing a high volume of requests. Please try again later." - ); - } - - void sendAnalyticsToWorker( - "create_db:database_creation_failed", - { - command: CLI_NAME, - region: region, - "error-type": "rate_limit", - "status-code": 429, - "user-agent": userAgent, - }, - cliRunId - ); - - await flushAnalytics(); - process.exit(1); - } - - let result; - let raw; - try { - raw = await resp.text(); - result = JSON.parse(raw); - } catch (e) { - if (silent) { - return { - error: "invalid_json", - message: "Unexpected response from create service.", - raw, - status: resp.status, - }; - } - if (s) { - s.stop("Unexpected response from create service."); - } - - void sendAnalyticsToWorker( - "create_db:database_creation_failed", - { - command: CLI_NAME, - region, - "error-type": "invalid_json", - "status-code": resp.status, - "user-agent": userAgent, - }, - cliRunId - ); - - await flushAnalytics(); - process.exit(1); - } - - const database = result.data ? result.data.database : result.databases?.[0]; - const projectId = result.data ? result.data.id : result.id; - - const directConnDetails = result.data - ? database?.apiKeys?.[0]?.directConnection - : result.databases?.[0]?.apiKeys?.[0]?.ppgDirectConnection; - const directUser = directConnDetails?.user - ? encodeURIComponent(directConnDetails.user) - : ""; - const directPass = directConnDetails?.pass - ? encodeURIComponent(directConnDetails.pass) - : ""; - const directHost = directConnDetails?.host; - const directPort = directConnDetails?.port - ? `:${directConnDetails.port}` - : ""; - const directDbName = directConnDetails?.database || "postgres"; - const directConn = - directConnDetails && directHost - ? `postgresql://${directUser}:${directPass}@${directHost}${directPort}/${directDbName}?sslmode=require` - : null; - - const claimUrl = `${CLAIM_DB_WORKER_URL}/claim?projectID=${projectId}&utm_source=${userAgent || CLI_NAME}&utm_medium=cli`; - const expiryDate = new Date(Date.now() + 24 * 60 * 60 * 1000); - - if (silent && !result.error) { - const jsonResponse = { - connectionString: directConn, - claimUrl: claimUrl, - deletionDate: expiryDate.toISOString(), - region: database?.region?.id || region, - name: database?.name, - projectId: projectId, - }; - - if (userAgent) { - jsonResponse.userAgent = userAgent; - } - - return jsonResponse; - } - - if (result.error) { - if (silent) { - return { - error: "api_error", - message: result.error.message || "Unknown error", - details: result.error, - status: result.error.status ?? resp.status, - }; - } - - if (s) { - s.stop( - `Error creating database: ${result.error.message || "Unknown error"}` - ); - } - - void sendAnalyticsToWorker( - "create_db:database_creation_failed", - { - command: CLI_NAME, - region: region, - "error-type": "api_error", - "error-message": result.error.message, - "user-agent": userAgent, - }, - cliRunId - ); - - await flushAnalytics(); - process.exit(1); - } - - if (s) { - s.stop("Database created successfully!"); - } - - const expiryFormatted = expiryDate.toLocaleString(); - - log.message(""); - log.info(chalk.bold("Database Connection")); - log.message(""); - - // Direct connection (only output this one) - if (directConn) { - log.message(chalk.cyan(" Connection String:")); - log.message(" " + chalk.yellow(directConn)); - log.message(""); - } else { - log.warning(chalk.yellow(" Connection details are not available.")); - log.message(""); - } - - - // Claim database section - const clickableUrl = terminalLink(claimUrl, claimUrl, { fallback: false }); - log.success(chalk.bold("Claim Your Database")); - log.message(chalk.cyan(" Keep your database for free:")); - log.message(" " + chalk.yellow(clickableUrl)); - log.message( - chalk.italic( - chalk.gray( - ` Database will be deleted on ${expiryFormatted} if not claimed.` - ) - ) - ); - - void sendAnalyticsToWorker( - "create_db:database_created", - { - command: CLI_NAME, - region, - utm_source: CLI_NAME, - }, - cliRunId - ); -} - -export async function main() { - try { - // Generate unique ID for this CLI run - const cliRunId = randomUUID(); - - const rawArgs = process.argv.slice(2); - - const { flags } = await parseArgs(); - - validateFlagCombinations(flags); - - let userAgent; - const userEnvVars = readUserEnvFile(); - if (userEnvVars.PRISMA_ACTOR_NAME && userEnvVars.PRISMA_ACTOR_PROJECT) { - userAgent = `${userEnvVars.PRISMA_ACTOR_NAME}/${userEnvVars.PRISMA_ACTOR_PROJECT}`; - } - - void sendAnalyticsToWorker( - "create_db:cli_command_ran", - { - command: CLI_NAME, - "full-command": `${CLI_NAME} ${rawArgs.join(" ")}`.trim(), - "has-region-flag": - rawArgs.includes("--region") || rawArgs.includes("-r"), - "has-interactive-flag": - rawArgs.includes("--interactive") || rawArgs.includes("-i"), - "has-help-flag": rawArgs.includes("--help") || rawArgs.includes("-h"), - "has-list-regions-flag": rawArgs.includes("--list-regions"), - "has-json-flag": rawArgs.includes("--json") || rawArgs.includes("-j"), - "has-env-flag": rawArgs.includes("--env") || rawArgs.includes("-e"), - "has-user-agent-from-env": !!userAgent, - "node-version": process.version, - platform: process.platform, - arch: process.arch, - "user-agent": userAgent, - }, - cliRunId - ); - - if (!flags.help && !flags.json) { - await isOffline(); - } - - let name = new Date().toISOString(); - let region = flags.region || "us-east-1"; - if (!flags.region || !flags.interactive) { - const userLocation = await detectUserLocation(); - region = getRegionClosestToLocation(userLocation) || region; - } - let chooseRegionPrompt = false; - - if (flags.help) { - return; - } - - if (flags["list-regions"]) { - await listRegions(); - await flushAnalytics(); - process.exit(0); - } - - if (flags.region) { - region = flags.region; - - void sendAnalyticsToWorker( - "create_db:region_selected", - { - command: CLI_NAME, - region: region, - "selection-method": "flag", - "user-agent": userAgent, - }, - cliRunId - ); - } - - if (flags.interactive) { - chooseRegionPrompt = true; - } - - if (flags.json) { - try { - if (chooseRegionPrompt) { - region = await promptForRegion(region, userAgent, cliRunId); - } else { - await validateRegion(region, true); - } - const result = await createDatabase( - name, - region, - userAgent, - cliRunId, - true - ); - console.log(JSON.stringify(result, null, 2)); - await flushAnalytics(); - process.exit(0); - } catch (e) { - console.log( - JSON.stringify( - { error: "cli_error", message: e?.message || String(e) }, - null, - 2 - ) - ); - await flushAnalytics(); - process.exit(1); - } - } - - if (flags.env) { - try { - if (chooseRegionPrompt) { - region = await promptForRegion(region, userAgent, cliRunId); - } else { - await validateRegion(region, true); - } - const result = await createDatabase( - name, - region, - userAgent, - cliRunId, - true - ); - if (result.error) { - console.error(result.message || "Unknown error"); - await flushAnalytics(); - process.exit(1); - } - console.log(`DATABASE_URL="${result.connectionString}"`); - console.error("\n# Claim your database at: " + result.claimUrl); - await flushAnalytics(); - process.exit(0); - } catch (e) { - console.error(e?.message || String(e)); - await flushAnalytics(); - process.exit(1); - } - } - - intro(chalk.cyan.bold("šŸš€ Creating a Prisma Postgres database")); - log.message( - chalk.white(`Provisioning a temporary database in ${region}...`) - ); - log.message( - chalk.gray( - `It will be automatically deleted in 24 hours, but you can claim it.` - ) - ); - if (chooseRegionPrompt) { - region = await promptForRegion(region, userAgent, cliRunId); - } - - region = await validateRegion(region); - - await createDatabase(name, region, userAgent, cliRunId); - - outro(""); - await flushAnalytics(); - } catch (error) { - console.error("Error:", error.message); - await flushAnalytics(); - process.exit(1); - } -} - -// Run main() if this file is being executed directly -const isDirectExecution = - import.meta.url.endsWith("/index.js") || - process.argv[1] === import.meta.url.replace("file://", "") || - process.argv[1].includes("create-db") || - process.argv[1].includes("create-pg") || - process.argv[1].includes("create-postgres"); - -if (isDirectExecution && !process.env.__CREATE_DB_EXECUTING) { - process.env.__CREATE_DB_EXECUTING = "true"; - main().catch(console.error); -} - -// if (import.meta.url.endsWith('/index.js') || process.argv[1] === import.meta.url.replace('file://', '')) { -// main().catch(console.error); -// } diff --git a/create-db/package.json b/create-db/package.json index 5c4e877..2136de8 100644 --- a/create-db/package.json +++ b/create-db/package.json @@ -2,7 +2,19 @@ "name": "create-db", "version": "1.1.0", "description": "Instantly create a temporary Prisma Postgres database with one command, then claim and persist it in your Prisma Data Platform project when ready.", - "main": "index.js", + "type": "module", + "exports": { + ".": { + "types": "./dist/index.d.mts", + "import": "./dist/index.mjs" + }, + "./cli": { + "import": "./dist/cli.mjs" + } + }, + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.mts", "author": "prisma", "repository": { "type": "git", @@ -20,35 +32,42 @@ "temporary" ], "license": "ISC", - "type": "module", "bin": { - "create-db": "./index.js", - "create-postgres": "./index.js", - "create-pg": "./index.js" + "create-db": "./dist/cli.mjs", + "create-postgres": "./dist/cli.mjs", + "create-pg": "./dist/cli.mjs" }, "scripts": { + "build": "tsdown", + "dev": "tsdown --watch", + "typecheck": "tsc --noEmit", "test": "vitest run --reporter=verbose", "test:watch": "vitest watch", "test:coverage": "vitest run --coverage", - "test:clean": "vitest run" + "test:clean": "vitest run", + "prepublishOnly": "npm run build" }, "dependencies": { "@clack/prompts": "^0.11.0", - "chalk": "^4.1.2", - "clipboardy": "^4.0.0", - "dotenv": "^16.6.1", - "terminal-link": "^4.0.0" + "@orpc/server": "^1.12.2", + "dotenv": "^17.2.3", + "picocolors": "^1.1.1", + "terminal-link": "^5.0.0", + "trpc-cli": "^0.12.1", + "zod": "^4.1.13", + "execa": "^9.6.1" }, "publishConfig": { "access": "public" }, "files": [ - "index.js", - "README.md", - "analytics.js" + "dist", + "README.md" ], "devDependencies": { - "execa": "^9.6.0", - "vitest": "^3.2.4" + "@types/node": "^24.10.1", + "tsdown": "0.17.0-beta.5", + "typescript": "^5.9.3", + "vitest": "^4.0.15" } -} +} \ No newline at end of file diff --git a/create-db/src/cli.ts b/create-db/src/cli.ts new file mode 100644 index 0000000..45b13e9 --- /dev/null +++ b/create-db/src/cli.ts @@ -0,0 +1,4 @@ +import { createDbCli } from "./index.js"; + +createDbCli().run(); + diff --git a/create-db/src/index.ts b/create-db/src/index.ts new file mode 100644 index 0000000..b080d7d --- /dev/null +++ b/create-db/src/index.ts @@ -0,0 +1,594 @@ +import { intro, outro, cancel, select, spinner, log } from "@clack/prompts"; +import { createRouterClient, os } from "@orpc/server"; +import { randomUUID } from "crypto"; +import dotenv from "dotenv"; +import fs from "fs"; +import path from "path"; +import pc from "picocolors"; +import terminalLink from "terminal-link"; +import { createCli } from "trpc-cli"; +import { z } from "zod"; + +import { + type UserLocation, + type Region, + type RegionCoordinates, + type CreateDatabaseResult, + type DatabaseResult, + type ApiResponse, + type GeoLocationResponse, + type RegionsResponse, + type ProgrammaticCreateOptions, + type RegionId, + RegionSchema, +} from "./types.js"; + +export type { + Region, + RegionId, + CreateDatabaseResult, + DatabaseResult, + ProgrammaticCreateOptions, +} from "./types.js"; + +export { isDatabaseError, isDatabaseSuccess, RegionSchema } from "./types.js"; + +dotenv.config({ + quiet: true +}); + +const CREATE_DB_WORKER_URL = + process.env.CREATE_DB_WORKER_URL || "https://create-db-temp.prisma.io"; +const CLAIM_DB_WORKER_URL = + process.env.CLAIM_DB_WORKER_URL || "https://create-db.prisma.io"; + +const REGION_COORDINATES: Record = { + "ap-southeast-1": { lat: 1.3521, lng: 103.8198 }, + "ap-northeast-1": { lat: 35.6762, lng: 139.6503 }, + "eu-central-1": { lat: 50.1109, lng: 8.6821 }, + "eu-west-3": { lat: 48.8566, lng: 2.3522 }, + "us-east-1": { lat: 38.9072, lng: -77.0369 }, + "us-west-1": { lat: 37.7749, lng: -122.4194 }, +}; + +const pendingAnalytics: Promise[] = []; + +async function sendAnalytics( + eventName: string, + properties: Record, + cliRunId: string +): Promise { + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), 5000); + + const promise = (async () => { + try { + await fetch(`${CREATE_DB_WORKER_URL}/analytics`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + eventName, + properties: { distinct_id: cliRunId, ...properties }, + }), + signal: controller.signal, + }); + } catch { + // Analytics failures should not block CLI + } finally { + clearTimeout(timer); + } + })(); + + pendingAnalytics.push(promise); +} + +async function flushAnalytics(maxWaitMs = 500): Promise { + if (pendingAnalytics.length === 0) return; + await Promise.race([ + Promise.all(pendingAnalytics), + new Promise((resolve) => setTimeout(resolve, maxWaitMs)), + ]); +} + +function getCommandName(): string { + const executable = process.argv[1] || "create-db"; + if (executable.includes("create-pg")) return "create-pg"; + if (executable.includes("create-postgres")) return "create-postgres"; + return "create-db"; +} + +async function detectUserLocation(): Promise { + try { + const response = await fetch("https://ipapi.co/json/", { + method: "GET", + headers: { "User-Agent": "create-db-cli/1.0" }, + }); + + if (!response.ok) return null; + + const data = (await response.json()) as GeoLocationResponse; + return { + country: data.country_code, + continent: data.continent_code, + city: data.city, + region: data.region, + latitude: data.latitude, + longitude: data.longitude, + }; + } catch { + return null; + } +} + +function getRegionClosestToLocation( + userLocation: { latitude?: number | string; longitude?: number | string } | null +): RegionId | null { + if (!userLocation) return null; + + const userLat = parseFloat(String(userLocation.latitude)); + const userLng = parseFloat(String(userLocation.longitude)); + + if (isNaN(userLat) || isNaN(userLng)) return null; + + let closestRegion: RegionId | null = null; + let minDistance = Infinity; + + for (const [region, coordinates] of Object.entries(REGION_COORDINATES)) { + const latDiff = ((userLat - coordinates.lat) * Math.PI) / 180; + const lngDiff = ((userLng - coordinates.lng) * Math.PI) / 180; + 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; + const distance = 6371 * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + + if (distance < minDistance) { + minDistance = distance; + closestRegion = region as RegionId; + } + } + + return closestRegion; +} + +function readUserEnvFile(): Record { + const envPath = path.join(process.cwd(), ".env"); + if (!fs.existsSync(envPath)) return {}; + + const envContent = fs.readFileSync(envPath, "utf8"); + const envVars: Record = {}; + + for (const line of envContent.split("\n")) { + const trimmed = line.trim(); + if (trimmed && !trimmed.startsWith("#")) { + const [key, ...valueParts] = trimmed.split("="); + if (key && valueParts.length > 0) { + const value = valueParts.join("=").replace(/^["']|["']$/g, ""); + envVars[key.trim()] = value.trim(); + } + } + } + + return envVars; +} + +async function checkOnline(): Promise { + try { + const res = await fetch(`${CREATE_DB_WORKER_URL}/health`); + if (!res.ok) throw new Error("API not available"); + } catch { + console.error( + pc.bold(pc.red("\nāœ– Error: Cannot reach Prisma Postgres API server.\n")) + ); + console.error( + pc.dim( + `Check your internet connection or visit ${pc.green("https://www.prisma-status.com/")}\n` + ) + ); + await flushAnalytics(); + process.exit(1); + } +} + +async function getRegions(): Promise { + const res = await fetch(`${CREATE_DB_WORKER_URL}/regions`); + + if (!res.ok) { + throw new Error( + `Failed to fetch regions. Status: ${res.status} ${res.statusText}` + ); + } + + const data = (await res.json()) as RegionsResponse; + const regions: Region[] = Array.isArray(data) ? data : (data.data ?? []); + return regions.filter((region) => region.status === "available"); +} + +async function validateRegion(region: string): Promise { + const regions = await getRegions(); + const regionIds = regions.map((r) => r.id); + + if (!regionIds.includes(region)) { + throw new Error( + `Invalid region: ${region}. Available regions: ${regionIds.join(", ")}` + ); + } + + return region; +} + +async function createDatabaseCore( + region: string, + userAgent?: string, + cliRunId?: string +): Promise { + const name = new Date().toISOString(); + const runId = cliRunId ?? randomUUID(); + + const resp = await fetch(`${CREATE_DB_WORKER_URL}/create`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + region, + name, + utm_source: getCommandName(), + userAgent, + }), + }); + + if (resp.status === 429) { + void sendAnalytics( + "create_db:database_creation_failed", + { region, "error-type": "rate_limit", "status-code": 429 }, + runId + ); + return { + success: false, + error: "rate_limit_exceeded", + message: + "We're experiencing a high volume of requests. Please try again later.", + status: 429, + }; + } + + let result: ApiResponse; + let raw = ""; + try { + raw = await resp.text(); + result = JSON.parse(raw) as ApiResponse; + } catch { + void sendAnalytics( + "create_db:database_creation_failed", + { region, "error-type": "invalid_json", "status-code": resp.status }, + runId + ); + return { + success: false, + error: "invalid_json", + message: "Unexpected response from create service.", + raw, + status: resp.status, + }; + } + + if (result.error) { + void sendAnalytics( + "create_db:database_creation_failed", + { + region, + "error-type": "api_error", + "error-message": result.error.message, + }, + runId + ); + return { + success: false, + error: "api_error", + message: result.error.message || "Unknown error", + details: result.error, + status: result.error.status ?? resp.status, + }; + } + + const database = result.data?.database ?? result.databases?.[0]; + const projectId = result.data?.id ?? result.id ?? ""; + + const apiKeys = database?.apiKeys; + const directConnDetails = result.data + ? apiKeys?.[0]?.directConnection + : result.databases?.[0]?.apiKeys?.[0]?.ppgDirectConnection; + + const directUser = directConnDetails?.user + ? encodeURIComponent(String(directConnDetails.user)) + : ""; + const directPass = directConnDetails?.pass + ? encodeURIComponent(String(directConnDetails.pass)) + : ""; + const directHost = directConnDetails?.host; + const directPort = directConnDetails?.port + ? `:${directConnDetails.port}` + : ""; + const directDbName = directConnDetails?.database || "postgres"; + + const connectionString = + directConnDetails && directHost + ? `postgresql://${directUser}:${directPass}@${directHost}${directPort}/${directDbName}?sslmode=require` + : null; + + const claimUrl = `${CLAIM_DB_WORKER_URL}/claim?projectID=${projectId}&utm_source=${userAgent || getCommandName()}&utm_medium=cli`; + const expiryDate = new Date(Date.now() + 24 * 60 * 60 * 1000); + + void sendAnalytics( + "create_db:database_created", + { region, utm_source: getCommandName() }, + runId + ); + + return { + success: true, + connectionString, + claimUrl, + deletionDate: expiryDate.toISOString(), + region: database?.region?.id || region, + name: database?.name ?? name, + projectId, + userAgent, + }; +} + +const router = os.router({ + create: os + .meta({ + description: "Create a new Prisma Postgres database", + default: true, + }) + .input( + z.object({ + region: RegionSchema.optional() + .describe("AWS region for the database") + .meta({ alias: "r" }), + interactive: z + .boolean() + .optional() + .default(false) + .describe("Run in interactive mode to select a region") + .meta({ alias: "i" }), + json: z + .boolean() + .optional() + .default(false) + .describe("Output machine-readable JSON") + .meta({ alias: "j" }), + env: z + .boolean() + .optional() + .default(false) + .describe("Output DATABASE_URL format for .env files") + .meta({ alias: "e" }), + }) + ) + .handler(async ({ input }) => { + const cliRunId = randomUUID(); + const CLI_NAME = getCommandName(); + + let userAgent: string | undefined; + const userEnvVars = readUserEnvFile(); + if (userEnvVars.PRISMA_ACTOR_NAME && userEnvVars.PRISMA_ACTOR_PROJECT) { + userAgent = `${userEnvVars.PRISMA_ACTOR_NAME}/${userEnvVars.PRISMA_ACTOR_PROJECT}`; + } + + void sendAnalytics( + "create_db:cli_command_ran", + { + command: CLI_NAME, + "has-region-flag": !!input.region, + "has-interactive-flag": input.interactive, + "has-json-flag": input.json, + "has-env-flag": input.env, + "has-user-agent-from-env": !!userAgent, + "node-version": process.version, + platform: process.platform, + arch: process.arch, + }, + cliRunId + ); + + let region: RegionId = input.region ?? "us-east-1"; + + if (!input.region) { + const userLocation = await detectUserLocation(); + region = getRegionClosestToLocation(userLocation) ?? region; + } + + if (input.json || input.env) { + if (input.interactive) { + await checkOnline(); + const regions = await getRegions(); + + const selectedRegion = await select({ + message: "Choose a region:", + options: regions.map((r) => ({ value: r.id, label: r.name || r.id })), + initialValue: + regions.find((r) => r.id === region)?.id || regions[0]?.id, + }); + + if (selectedRegion === null) { + cancel(pc.red("Operation cancelled.")); + await flushAnalytics(); + process.exit(0); + } + + region = selectedRegion as RegionId; + void sendAnalytics( + "create_db:region_selected", + { region, "selection-method": "interactive" }, + cliRunId + ); + } else if (input.region) { + await validateRegion(region); + void sendAnalytics( + "create_db:region_selected", + { region, "selection-method": "flag" }, + cliRunId + ); + } + + await checkOnline(); + const result = await createDatabaseCore(region, userAgent, cliRunId); + await flushAnalytics(); + + if (input.json) { + console.log(JSON.stringify(result, null, 2)); + return; + } + + if (!result.success) { + console.error(result.message); + process.exit(1); + } + + console.log(`DATABASE_URL="${result.connectionString}"`); + console.error(`\n# Claim your database at: ${result.claimUrl}`); + } + + await checkOnline(); + + intro(pc.bold(pc.cyan("šŸš€ Creating a Prisma Postgres database"))); + + if (input.interactive) { + const regions = await getRegions(); + + const selectedRegion = await select({ + message: "Choose a region:", + options: regions.map((r) => ({ value: r.id, label: r.name || r.id })), + initialValue: + regions.find((r) => r.id === region)?.id || regions[0]?.id, + }); + + if (selectedRegion === null) { + cancel(pc.red("Operation cancelled.")); + await flushAnalytics(); + process.exit(0); + } + + region = selectedRegion as RegionId; + void sendAnalytics( + "create_db:region_selected", + { region, "selection-method": "interactive" }, + cliRunId + ); + } else if (input.region) { + await validateRegion(region); + void sendAnalytics( + "create_db:region_selected", + { region, "selection-method": "flag" }, + cliRunId + ); + } + + const s = spinner(); + s.start(`Creating database in ${pc.cyan(region)}...`); + + const result = await createDatabaseCore(region, userAgent, cliRunId); + + if (!result.success) { + s.stop(pc.red(`Error: ${result.message}`)); + await flushAnalytics(); + process.exit(1); + } + + s.stop(pc.green("Database created successfully!")); + + const expiryFormatted = new Date(result.deletionDate).toLocaleString(); + const clickableUrl = terminalLink(result.claimUrl, result.claimUrl, { + fallback: false, + }); + + log.message(""); + log.info(pc.bold("Database Connection")); + log.message(""); + + if (result.connectionString) { + log.message(pc.cyan(" Connection String:")); + log.message(" " + pc.yellow(result.connectionString)); + log.message(""); + } else { + log.warning(pc.yellow(" Connection details are not available.")); + log.message(""); + } + + log.success(pc.bold("Claim Your Database")); + log.message(pc.cyan(" Keep your database for free:")); + log.message(" " + pc.yellow(clickableUrl)); + log.message( + pc.italic(pc.dim(` Database will be deleted on ${expiryFormatted} if not claimed.`)) + ); + + outro(pc.dim("Done!")); + await flushAnalytics(); + }), + + regions: os + .meta({ description: "List available Prisma Postgres regions" }) + .handler(async (): Promise => { + const regions = await getRegions(); + + log.message(""); + log.info(pc.bold(pc.cyan("Available Prisma Postgres regions:"))); + log.message(""); + for (const r of regions) { + log.message(` ${pc.green(r.id)} - ${r.name || r.id}`); + } + log.message(""); + + return regions; + }), +}); + +export function createDbCli() { + return createCli({ + router, + name: getCommandName(), + version: "1.1.0", + description: "Instantly create a temporary Prisma Postgres database", + }); +} + +const caller = createRouterClient(router, { context: {} }); + +/** + * Create a new Prisma Postgres database programmatically. + * + * @example + * ```typescript + * import { create } from "create-db"; + * + * const result = await create({ region: "us-east-1" }); + * + * if (result.success) { + * console.log(`Connection string: ${result.connectionString}`); + * console.log(`Claim URL: ${result.claimUrl}`); + * } + * ``` + */ +export async function create( + options?: ProgrammaticCreateOptions +): Promise { + return createDatabaseCore(options?.region || "us-east-1", options?.userAgent); +} + +/** + * List available Prisma Postgres regions programmatically. + * + * @example + * ```typescript + * import { regions } from "create-db"; + * + * const availableRegions = await regions(); + * console.log(availableRegions); + * ``` + */ +export async function regions(): Promise { + return getRegions(); +} diff --git a/create-db/src/types.ts b/create-db/src/types.ts new file mode 100644 index 0000000..54f8b34 --- /dev/null +++ b/create-db/src/types.ts @@ -0,0 +1,129 @@ +import z from "zod"; + +export const RegionSchema = z.enum([ + "ap-southeast-1", + "ap-northeast-1", + "eu-central-1", + "eu-west-3", + "us-east-1", + "us-west-1", +]); + +export type RegionId = z.infer; + +export interface UserLocation { + country: string; + continent: string; + city: string; + region: string; + latitude: number; + longitude: number; +} + +export interface PartialUserLocation { + latitude?: number | string; + longitude?: number | string; +} + +export interface RegionCoordinates { + lat: number; + lng: number; +} + +export interface Region { + id: string; + name?: string; + status: string; +} + +export interface DatabaseResult { + success: true; + connectionString: string | null; + claimUrl: string; + deletionDate: string; + region: string; + name: string; + projectId: string; + userAgent?: string; +} + +export interface DatabaseError { + success: false; + error: string; + message: string; + raw?: string; + details?: unknown; + status?: number; +} + +export type CreateDatabaseResult = DatabaseResult | DatabaseError; + +export function isDatabaseError( + result: CreateDatabaseResult +): result is DatabaseError { + return !result.success; +} + +export function isDatabaseSuccess( + result: CreateDatabaseResult +): result is DatabaseResult { + return result.success; +} + +export interface ApiResponseData { + id?: string; + database?: DatabaseRecord; +} + +export interface DatabaseRecord { + name?: string; + region?: { + id?: string; + }; + apiKeys?: ApiKey[]; +} + +export interface ApiKey { + directConnection?: ConnectionDetails; + ppgDirectConnection?: ConnectionDetails; +} + +export interface ConnectionDetails { + user?: string; + pass?: string; + host?: string; + port?: number | string; + database?: string; +} + +export interface ApiResponse { + data?: ApiResponseData; + databases?: DatabaseRecord[]; + id?: string; + error?: ApiErrorInfo; +} + +export interface ApiErrorInfo { + message?: string; + status?: number; +} + +export interface GeoLocationResponse { + country_code: string; + continent_code: string; + city: string; + region: string; + latitude: number; + longitude: number; +} + +export interface RegionsApiResponse { + data?: Region[]; +} + +export type RegionsResponse = Region[] | RegionsApiResponse; + +export interface ProgrammaticCreateOptions { + region?: RegionId; + userAgent?: string; +} diff --git a/create-db/tsconfig.json b/create-db/tsconfig.json new file mode 100644 index 0000000..228bbeb --- /dev/null +++ b/create-db/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "esModuleInterop": true, + "verbatimModuleSyntax": true, + "strict": true, + "skipLibCheck": true, + "outDir": "dist", + "types": ["node"] + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "__tests__"] +} + diff --git a/create-db/tsdown.config.ts b/create-db/tsdown.config.ts new file mode 100644 index 0000000..286cc0b --- /dev/null +++ b/create-db/tsdown.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from "tsdown"; + +export default defineConfig({ + entry: ["src/index.ts", "src/cli.ts"], + format: ["esm"], + clean: true, + shims: true, + outDir: "dist", + dts: true, + outputOptions: { + banner: "#!/usr/bin/env node", + } +}); diff --git a/create-db/vitest.config.ts b/create-db/vitest.config.ts new file mode 100644 index 0000000..739d065 --- /dev/null +++ b/create-db/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + include: ["__tests__/**/*.test.ts"], + }, +}); + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3c4aa63..47cc60d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,7 +13,7 @@ importers: version: 2.29.5 '@commitlint/cli': specifier: ^19.8.1 - version: 19.8.1(@types/node@24.0.14)(typescript@5.8.3) + version: 19.8.1(@types/node@24.10.1)(typescript@5.8.3) '@commitlint/config-conventional': specifier: ^19.8.1 version: 19.8.1 @@ -89,7 +89,7 @@ importers: version: 19.1.7(@types/react@19.1.10) '@vitejs/plugin-react': specifier: ^5.0.2 - version: 5.0.2(vite@7.0.2(@types/node@20.19.11)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1)) + version: 5.0.2(vite@7.2.6(@types/node@20.19.11)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1)) '@vitest/ui': specifier: ^3.2.4 version: 3.2.4(vitest@3.2.4) @@ -104,10 +104,10 @@ importers: version: 5.8.3 vite-tsconfig-paths: specifier: ^5.1.4 - version: 5.1.4(typescript@5.8.3)(vite@7.0.2(@types/node@20.19.11)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1)) + version: 5.1.4(typescript@5.8.3)(vite@7.2.6(@types/node@20.19.11)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1)) vitest: specifier: ^3.2.4 - version: 3.2.4(@edge-runtime/vm@3.2.0)(@types/node@20.19.11)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) + version: 3.2.4(@edge-runtime/vm@3.2.0)(@types/node@20.19.11)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) wrangler: specifier: ^4.29.1 version: 4.30.0 @@ -117,37 +117,52 @@ importers: '@clack/prompts': specifier: ^0.11.0 version: 0.11.0 - chalk: - specifier: ^4.1.2 - version: 4.1.2 - clipboardy: - specifier: ^4.0.0 - version: 4.0.0 + '@orpc/server': + specifier: ^1.12.2 + version: 1.12.2(ws@8.18.3) dotenv: - specifier: ^16.6.1 - version: 16.6.1 + specifier: ^17.2.3 + version: 17.2.3 + execa: + specifier: ^9.6.1 + version: 9.6.1 + picocolors: + specifier: ^1.1.1 + version: 1.1.1 terminal-link: - specifier: ^4.0.0 - version: 4.0.0 + specifier: ^5.0.0 + version: 5.0.0 + trpc-cli: + specifier: ^0.12.1 + version: 0.12.1(@orpc/server@1.12.2(ws@8.18.3))(effect@3.16.12)(zod@4.1.13) + zod: + specifier: ^4.1.13 + version: 4.1.13 devDependencies: - execa: - specifier: ^9.6.0 - version: 9.6.0 + '@types/node': + specifier: ^24.10.1 + version: 24.10.1 + tsdown: + specifier: 0.17.0-beta.5 + version: 0.17.0-beta.5(typescript@5.9.3) + typescript: + specifier: ^5.9.3 + version: 5.9.3 vitest: - specifier: ^3.2.4 - version: 3.2.4(@edge-runtime/vm@3.2.0)(@types/node@24.0.14)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) + specifier: ^4.0.15 + version: 4.0.15(@edge-runtime/vm@3.2.0)(@types/node@24.10.1)(jiti@2.6.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) create-db-worker: devDependencies: '@cloudflare/vitest-pool-workers': specifier: ^0.8.19 - version: 0.8.49(@vitest/runner@3.2.4)(@vitest/snapshot@3.2.4)(vitest@3.2.4) + version: 0.8.49(@vitest/runner@4.0.15)(@vitest/snapshot@4.0.15)(vitest@3.2.4) typescript: specifier: ^5.5.2 version: 5.8.3 vitest: specifier: ~3.2.0 - version: 3.2.4(@edge-runtime/vm@3.2.0)(@types/node@24.0.14)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) + version: 3.2.4(@edge-runtime/vm@3.2.0)(@types/node@24.10.1)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) wrangler: specifier: ^4.22.0 version: 4.23.0 @@ -177,7 +192,7 @@ importers: version: 5.22.0 vercel: specifier: ^46.0.2 - version: 46.0.2(rollup@4.44.2) + version: 46.0.2(rollup@4.53.3) devDependencies: '@types/node': specifier: ^20.11.17 @@ -562,6 +577,10 @@ packages: resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} engines: {node: '>=6.9.0'} + '@babel/generator@7.28.5': + resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} + engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@7.27.2': resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} @@ -592,6 +611,10 @@ packages: resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.27.1': resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} @@ -605,6 +628,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-transform-react-jsx-self@7.27.1': resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} engines: {node: '>=6.9.0'} @@ -633,6 +661,10 @@ packages: resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} engines: {node: '>=6.9.0'} + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + engines: {node: '>=6.9.0'} + '@changesets/apply-release-plan@7.0.12': resolution: {integrity: sha512-EaET7As5CeuhTzvXTQCRZeBUcisoYPDDcXvgTE/2jmmypKp0RC7LxKj/yzqeh/1qFTZI7oDGFcL1PHRuQuketQ==} @@ -920,9 +952,24 @@ packages: resolution: {integrity: sha512-0dEVyRLM/lG4gp1R/Ik5bfPl/1wX00xFwd5KcNH602tzBa09oF7pbTKETEhR1GjZ75K6OJnYFu8II2dyMhONMw==} engines: {node: '>=16'} + '@emnapi/core@1.7.1': + resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} + '@emnapi/runtime@1.4.4': resolution: {integrity: sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg==} + '@emnapi/runtime@1.7.1': + resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} + + '@emnapi/wasi-threads@1.1.0': + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/aix-ppc64@0.25.4': resolution: {integrity: sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==} engines: {node: '>=18'} @@ -935,6 +982,12 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm64@0.25.4': resolution: {integrity: sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==} engines: {node: '>=18'} @@ -947,6 +1000,12 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-arm@0.25.4': resolution: {integrity: sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==} engines: {node: '>=18'} @@ -959,6 +1018,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/android-x64@0.25.4': resolution: {integrity: sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==} engines: {node: '>=18'} @@ -971,6 +1036,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-arm64@0.25.4': resolution: {integrity: sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==} engines: {node: '>=18'} @@ -983,6 +1054,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/darwin-x64@0.25.4': resolution: {integrity: sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==} engines: {node: '>=18'} @@ -995,6 +1072,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-arm64@0.25.4': resolution: {integrity: sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==} engines: {node: '>=18'} @@ -1007,6 +1090,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/freebsd-x64@0.25.4': resolution: {integrity: sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==} engines: {node: '>=18'} @@ -1019,6 +1108,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm64@0.25.4': resolution: {integrity: sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==} engines: {node: '>=18'} @@ -1031,6 +1126,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-arm@0.25.4': resolution: {integrity: sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==} engines: {node: '>=18'} @@ -1043,6 +1144,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-ia32@0.25.4': resolution: {integrity: sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==} engines: {node: '>=18'} @@ -1055,6 +1162,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-loong64@0.25.4': resolution: {integrity: sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==} engines: {node: '>=18'} @@ -1067,6 +1180,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-mips64el@0.25.4': resolution: {integrity: sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==} engines: {node: '>=18'} @@ -1079,6 +1198,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-ppc64@0.25.4': resolution: {integrity: sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==} engines: {node: '>=18'} @@ -1091,6 +1216,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-riscv64@0.25.4': resolution: {integrity: sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==} engines: {node: '>=18'} @@ -1103,6 +1234,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-s390x@0.25.4': resolution: {integrity: sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==} engines: {node: '>=18'} @@ -1115,6 +1252,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/linux-x64@0.25.4': resolution: {integrity: sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==} engines: {node: '>=18'} @@ -1127,6 +1270,12 @@ packages: cpu: [x64] os: [linux] + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-arm64@0.25.4': resolution: {integrity: sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==} engines: {node: '>=18'} @@ -1139,6 +1288,12 @@ packages: cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/netbsd-x64@0.25.4': resolution: {integrity: sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==} engines: {node: '>=18'} @@ -1151,6 +1306,12 @@ packages: cpu: [x64] os: [netbsd] + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-arm64@0.25.4': resolution: {integrity: sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==} engines: {node: '>=18'} @@ -1163,6 +1324,12 @@ packages: cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openbsd-x64@0.25.4': resolution: {integrity: sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==} engines: {node: '>=18'} @@ -1175,6 +1342,18 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/sunos-x64@0.25.4': resolution: {integrity: sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==} engines: {node: '>=18'} @@ -1187,6 +1366,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-arm64@0.25.4': resolution: {integrity: sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==} engines: {node: '>=18'} @@ -1199,6 +1384,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-ia32@0.25.4': resolution: {integrity: sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==} engines: {node: '>=18'} @@ -1211,6 +1402,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@esbuild/win32-x64@0.25.4': resolution: {integrity: sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==} engines: {node: '>=18'} @@ -1492,9 +1689,15 @@ packages: '@jridgewell/sourcemap-codec@1.5.4': resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/trace-mapping@0.3.30': resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} @@ -1519,6 +1722,9 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + '@napi-rs/wasm-runtime@1.1.0': + resolution: {integrity: sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==} + '@next/env@15.4.6': resolution: {integrity: sha512-yHDKVTcHrZy/8TWhj0B23ylKv5ypocuCwey9ZqPyv4rPdUdRzpGCkSi03t04KBPyU96kxVtUqx6O3nE1kpxASQ==} @@ -1616,6 +1822,64 @@ packages: peerDependencies: wrangler: ^4.24.4 + '@orpc/client@1.12.2': + resolution: {integrity: sha512-3MTFnWRYYjcyzhtcYpodvkaYQlqsxKd5xGv+7PPJSpjCgFg9wcp7mZmRKy7hK0sCwUlkyi7AKs1Q19aUVUFIGA==} + + '@orpc/contract@1.12.2': + resolution: {integrity: sha512-eleSbF7WgfkWz+7jl1b9t3C3DWn127694+yEdR3j6EiBjb9mzHMIeOMRTXsclIP4gWj13wD1NtXp1Qlv8m7oZw==} + + '@orpc/interop@1.12.2': + resolution: {integrity: sha512-whHawJ8XZBzxngqOZKRzkI6HaFZcFSdbaK0//LmqOdSKXBeuveHF+kprCcpr8C6rH2N5i9nKMdnt0RetjPvxCg==} + + '@orpc/server@1.12.2': + resolution: {integrity: sha512-lgT3VR+yXsCcgzbZ2d1fXtqaf1RbgUJHMDWQ4J22LBYH1P8pi0Nk+EYi9/w3YNFIr1WuUmVu4Pm6Dg6l92oiQA==} + peerDependencies: + crossws: '>=0.3.4' + ws: '>=8.18.1' + peerDependenciesMeta: + crossws: + optional: true + ws: + optional: true + + '@orpc/shared@1.12.2': + resolution: {integrity: sha512-aITtDnmkofoG/GY6897AOPLnFMkLpQpM7ljzaqsG8QMbL6oovO427G/9Tr9Y3DDSyrsxA7FQ8+rwV03ZDG7gfQ==} + peerDependencies: + '@opentelemetry/api': '>=1.9.0' + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + + '@orpc/standard-server-aws-lambda@1.12.2': + resolution: {integrity: sha512-2gpM0ipl3YvE6/bJKTE6Oga4EROo2zuRiOzwY21F/3q38xyQ344Lk1smHzUv20lYZEIBDWP0FRR0OPhBQYLwNQ==} + + '@orpc/standard-server-fastify@1.12.2': + resolution: {integrity: sha512-O1IsvytsK1j6cbuztreE5sJHL3OyvDMn1lSWKO16YoLBK47W8XRHCVBKDDV+1gk4f4Q74bSJOvNGQh1OUcNbkg==} + peerDependencies: + fastify: '>=5.6.1' + peerDependenciesMeta: + fastify: + optional: true + + '@orpc/standard-server-fetch@1.12.2': + resolution: {integrity: sha512-gjqZgD8uiW3dVaf+bo9Gj0GXTQ4B/vXkaHPmat+11AE+34UsOZJqZJzEuUi+P4qkmNOm6ZXAsb8roz6H3izPrg==} + + '@orpc/standard-server-node@1.12.2': + resolution: {integrity: sha512-st9yjw3i+xFJu8YHeKcCBchMkRKyobBNqstR8yUelrLE/+rC0qKX+AwrmP6xq6gP6BQqFk+RSfIManaIruKuuQ==} + + '@orpc/standard-server-peer@1.12.2': + resolution: {integrity: sha512-O51FDFAHgK5uG5EhIG5PYWrkpnwBSVIKih2yYtNzLVYsU3y8EkE07UHRWixJFjUDJ7YeGy16ud3M8oNqt2bE4g==} + + '@orpc/standard-server@1.12.2': + resolution: {integrity: sha512-8ZNhL3CRJmoJ7uFjOZB4U7NVGdcbGIOKLRTP0i/yhi51QL5Lw+56f/hNqztrCPVZolhJWif86HjyQhuixizrVg==} + + '@oxc-project/runtime@0.99.0': + resolution: {integrity: sha512-8iE5/4OK0SLHqWzRxSvI1gjFPmIH6718s8iwkuco95rBZsCZIHq+5wy4lYsASxnH+8FOhbGndiUrcwsVG5i2zw==} + engines: {node: ^20.19.0 || >=22.12.0} + + '@oxc-project/types@0.99.0': + resolution: {integrity: sha512-LLDEhXB7g1m5J+woRSgfKsFPS3LhR9xRhTeIoEBm5WrkwMxn6eZ0Ld0c0K5eHB57ChZX6I3uSmmLjZ8pcjlRcw==} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -1688,9 +1952,98 @@ packages: react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 + '@quansync/fs@0.1.5': + resolution: {integrity: sha512-lNS9hL2aS2NZgNW7BBj+6EBl4rOf8l+tQ0eRY6JWCI8jI2kc53gSoqbjojU0OnAWhzoXiOjFyGsHcDGePB3lhA==} + + '@rolldown/binding-android-arm64@1.0.0-beta.52': + resolution: {integrity: sha512-MBGIgysimZPqTDcLXI+i9VveijkP5C3EAncEogXhqfax6YXj1Tr2LY3DVuEOMIjWfMPMhtQSPup4fSTAmgjqIw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.0.0-beta.52': + resolution: {integrity: sha512-MmKeoLnKu1d9j6r19K8B+prJnIZ7u+zQ+zGQ3YHXGnr41rzE3eqQLovlkvoZnRoxDGPA4ps0pGiwXy6YE3lJyg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.0.0-beta.52': + resolution: {integrity: sha512-qpHedvQBmIjT8zdnjN3nWPR2qjQyJttbXniCEKKdHeAbZG9HyNPBUzQF7AZZGwmS9coQKL+hWg9FhWzh2dZ2IA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.0.0-beta.52': + resolution: {integrity: sha512-dDp7WbPapj/NVW0LSiH/CLwMhmLwwKb3R7mh2kWX+QW85X1DGVnIEyKh9PmNJjB/+suG1dJygdtdNPVXK1hylg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.52': + resolution: {integrity: sha512-9e4l6vy5qNSliDPqNfR6CkBOAx6PH7iDV4OJiEJzajajGrVy8gc/IKKJUsoE52G8ud8MX6r3PMl97NfwgOzB7g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.52': + resolution: {integrity: sha512-V48oDR84feRU2KRuzpALp594Uqlx27+zFsT6+BgTcXOtu7dWy350J1G28ydoCwKB+oxwsRPx2e7aeQnmd3YJbQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.52': + resolution: {integrity: sha512-ENLmSQCWqSA/+YN45V2FqTIemg7QspaiTjlm327eUAMeOLdqmSOVVyrQexJGNTQ5M8sDYCgVAig2Kk01Ggmqaw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.52': + resolution: {integrity: sha512-klahlb2EIFltSUubn/VLjuc3qxp1E7th8ukayPfdkcKvvYcQ5rJztgx8JsJSuAKVzKtNTqUGOhy4On71BuyV8g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-linux-x64-musl@1.0.0-beta.52': + resolution: {integrity: sha512-UuA+JqQIgqtkgGN2c/AQ5wi8M6mJHrahz/wciENPTeI6zEIbbLGoth5XN+sQe2pJDejEVofN9aOAp0kaazwnVg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-openharmony-arm64@1.0.0-beta.52': + resolution: {integrity: sha512-1BNQW8u4ro8bsN1+tgKENJiqmvc+WfuaUhXzMImOVSMw28pkBKdfZtX2qJPADV3terx+vNJtlsgSGeb3+W6Jiw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.0.0-beta.52': + resolution: {integrity: sha512-K/p7clhCqJOQpXGykrFaBX2Dp9AUVIDHGc+PtFGBwg7V+mvBTv/tsm3LC3aUmH02H2y3gz4y+nUTQ0MLpofEEg==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.52': + resolution: {integrity: sha512-a4EkXBtnYYsKipjS7QOhEBM4bU5IlR9N1hU+JcVEVeuTiaslIyhWVKsvf7K2YkQHyVAJ+7/A9BtrGqORFcTgng==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.52': + resolution: {integrity: sha512-5ZXcYyd4GxPA6QfbGrNcQjmjbuLGvfz6728pZMsQvGHI+06LT06M6TPtXvFvLgXtexc+OqvFe1yAIXJU1gob/w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.52': + resolution: {integrity: sha512-tzpnRQXJrSzb8Z9sm97UD3cY0toKOImx+xRKsDLX4zHaAlRXWh7jbaKBePJXEN7gNw7Nm03PBNwphdtA8KSUYQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@rolldown/pluginutils@1.0.0-beta.34': resolution: {integrity: sha512-LyAREkZHP5pMom7c24meKmJCdhf2hEyvam2q0unr3or9ydwDL+DJ8chTF6Av/RFPb3rH8UFBdMzO5MxTZW97oA==} + '@rolldown/pluginutils@1.0.0-beta.52': + resolution: {integrity: sha512-/L0htLJZbaZFL1g9OHOblTxbCYIGefErJjtYOwgl9ZqNx27P3L0SDfjhhHIss32gu5NWgnxuT2a2Hnnv6QGHKA==} + '@rollup/pluginutils@5.2.0': resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==} engines: {node: '>=14.0.0'} @@ -1705,51 +2058,106 @@ packages: cpu: [arm] os: [android] + '@rollup/rollup-android-arm-eabi@4.53.3': + resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==} + cpu: [arm] + os: [android] + '@rollup/rollup-android-arm64@4.44.2': resolution: {integrity: sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA==} cpu: [arm64] os: [android] + '@rollup/rollup-android-arm64@4.53.3': + resolution: {integrity: sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==} + cpu: [arm64] + os: [android] + '@rollup/rollup-darwin-arm64@4.44.2': resolution: {integrity: sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA==} cpu: [arm64] os: [darwin] + '@rollup/rollup-darwin-arm64@4.53.3': + resolution: {integrity: sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==} + cpu: [arm64] + os: [darwin] + '@rollup/rollup-darwin-x64@4.44.2': resolution: {integrity: sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw==} cpu: [x64] os: [darwin] + '@rollup/rollup-darwin-x64@4.53.3': + resolution: {integrity: sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==} + cpu: [x64] + os: [darwin] + '@rollup/rollup-freebsd-arm64@4.44.2': resolution: {integrity: sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg==} cpu: [arm64] os: [freebsd] + '@rollup/rollup-freebsd-arm64@4.53.3': + resolution: {integrity: sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==} + cpu: [arm64] + os: [freebsd] + '@rollup/rollup-freebsd-x64@4.44.2': resolution: {integrity: sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA==} cpu: [x64] os: [freebsd] + '@rollup/rollup-freebsd-x64@4.53.3': + resolution: {integrity: sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==} + cpu: [x64] + os: [freebsd] + '@rollup/rollup-linux-arm-gnueabihf@4.44.2': resolution: {integrity: sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-gnueabihf@4.53.3': + resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.44.2': resolution: {integrity: sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.53.3': + resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.44.2': resolution: {integrity: sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.53.3': + resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==} + cpu: [arm64] + os: [linux] + '@rollup/rollup-linux-arm64-musl@4.44.2': resolution: {integrity: sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-musl@4.53.3': + resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.53.3': + resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==} + cpu: [loong64] + os: [linux] + '@rollup/rollup-linux-loongarch64-gnu@4.44.2': resolution: {integrity: sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g==} cpu: [loong64] @@ -1760,46 +2168,101 @@ packages: cpu: [ppc64] os: [linux] + '@rollup/rollup-linux-ppc64-gnu@4.53.3': + resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==} + cpu: [ppc64] + os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.44.2': resolution: {integrity: sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg==} cpu: [riscv64] os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.53.3': + resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==} + cpu: [riscv64] + os: [linux] + '@rollup/rollup-linux-riscv64-musl@4.44.2': resolution: {integrity: sha512-e6vEbgaaqz2yEHqtkPXa28fFuBGmUJ0N2dOJK8YUfijejInt9gfCSA7YDdJ4nYlv67JfP3+PSWFX4IVw/xRIPg==} cpu: [riscv64] os: [linux] + '@rollup/rollup-linux-riscv64-musl@4.53.3': + resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==} + cpu: [riscv64] + os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.44.2': resolution: {integrity: sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw==} cpu: [s390x] os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.53.3': + resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==} + cpu: [s390x] + os: [linux] + '@rollup/rollup-linux-x64-gnu@4.44.2': resolution: {integrity: sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-gnu@4.53.3': + resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==} + cpu: [x64] + os: [linux] + '@rollup/rollup-linux-x64-musl@4.44.2': resolution: {integrity: sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-musl@4.53.3': + resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.53.3': + resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==} + cpu: [arm64] + os: [openharmony] + '@rollup/rollup-win32-arm64-msvc@4.44.2': resolution: {integrity: sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw==} cpu: [arm64] os: [win32] + '@rollup/rollup-win32-arm64-msvc@4.53.3': + resolution: {integrity: sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==} + cpu: [arm64] + os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.44.2': resolution: {integrity: sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q==} cpu: [ia32] os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.53.3': + resolution: {integrity: sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.53.3': + resolution: {integrity: sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==} + cpu: [x64] + os: [win32] + '@rollup/rollup-win32-x64-msvc@4.44.2': resolution: {integrity: sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA==} cpu: [x64] os: [win32] + '@rollup/rollup-win32-x64-msvc@4.53.3': + resolution: {integrity: sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==} + cpu: [x64] + os: [win32] + '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} @@ -2308,6 +2771,9 @@ packages: '@tsconfig/node18@1.0.3': resolution: {integrity: sha512-RbwvSJQsuN9TB04AQbGULYfOGE/RnSFk/FLQ5b0NmDf5Kx2q/lABZbHQPKCO1vZ6Fiwkplu+yb9pGdLy1iGseQ==} + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} @@ -2326,6 +2792,9 @@ packages: '@types/chai@5.2.2': resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + '@types/conventional-commits-parser@5.0.1': resolution: {integrity: sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ==} @@ -2347,14 +2816,14 @@ packages: '@types/node@16.18.11': resolution: {integrity: sha512-3oJbGBUWuS6ahSnEq1eN2XrCyf4YsWI8OyCvo7c64zQJNplk3mO84t53o8lfTk+2ji59g5ycfc6qQ3fdHliHuA==} - '@types/node@18.19.123': - resolution: {integrity: sha512-K7DIaHnh0mzVxreCR9qwgNxp3MH9dltPNIEddW9MYUlcKAzm+3grKNSTe2vCJHI1FaLpvpL5JGJrz1UZDKYvDg==} + '@types/node@18.19.130': + resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} '@types/node@20.19.11': resolution: {integrity: sha512-uug3FEEGv0r+jrecvUUpbY8lLisvIjg6AAic6a2bSP5OEOLeJsDSnvhCDov7ipFFMXS3orMpzlmi0ZcuGkBbow==} - '@types/node@24.0.14': - resolution: {integrity: sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw==} + '@types/node@24.10.1': + resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==} '@types/pg@8.15.5': resolution: {integrity: sha512-LF7lF6zWEKxuT3/OR8wAZGzkg4ENGXFNyiV/JeOt9z5B+0ZVwbql9McqX5c/WStFq1GaGso7H1AzP/qSzmlCKQ==} @@ -2440,6 +2909,9 @@ packages: '@vitest/expect@3.2.4': resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + '@vitest/expect@4.0.15': + resolution: {integrity: sha512-Gfyva9/GxPAWXIWjyGDli9O+waHDC0Q0jaLdFP1qPAUUfo1FEXPXUfUkp3eZA0sSq340vPycSyOlYUeM15Ft1w==} + '@vitest/mocker@3.2.4': resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} peerDependencies: @@ -2451,18 +2923,41 @@ packages: vite: optional: true + '@vitest/mocker@4.0.15': + resolution: {integrity: sha512-CZ28GLfOEIFkvCFngN8Sfx5h+Se0zN+h4B7yOsPVCcgtiO7t5jt9xQh2E1UkFep+eb9fjyMfuC5gBypwb07fvQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + '@vitest/pretty-format@3.2.4': resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + '@vitest/pretty-format@4.0.15': + resolution: {integrity: sha512-SWdqR8vEv83WtZcrfLNqlqeQXlQLh2iilO1Wk1gv4eiHKjEzvgHb2OVc3mIPyhZE6F+CtfYjNlDJwP5MN6Km7A==} + '@vitest/runner@3.2.4': resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + '@vitest/runner@4.0.15': + resolution: {integrity: sha512-+A+yMY8dGixUhHmNdPUxOh0la6uVzun86vAbuMT3hIDxMrAOmn5ILBHm8ajrqHE0t8R9T1dGnde1A5DTnmi3qw==} + '@vitest/snapshot@3.2.4': resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + '@vitest/snapshot@4.0.15': + resolution: {integrity: sha512-A7Ob8EdFZJIBjLjeO0DZF4lqR6U7Ydi5/5LIZ0xcI+23lYlsYJAfGn8PrIWTYdZQRNnSRlzhg0zyGu37mVdy5g==} + '@vitest/spy@3.2.4': resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + '@vitest/spy@4.0.15': + resolution: {integrity: sha512-+EIjOJmnY6mIfdXtE/bnozKEvTC4Uczg19yeZ2vtCz5Yyb0QQ31QWVQ8hswJ3Ysx/K2EqaNsVanjr//2+P3FHw==} + '@vitest/ui@3.2.4': resolution: {integrity: sha512-hGISOaP18plkzbWEcP/QvtRW1xDXF2+96HbEX6byqQhAUbiS5oH6/9JwW+QsQCIYON2bI6QZBF+2PvOmrRZ9wA==} peerDependencies: @@ -2471,6 +2966,9 @@ packages: '@vitest/utils@3.2.4': resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + '@vitest/utils@4.0.15': + resolution: {integrity: sha512-HXjPW2w5dxhTD0dLwtYHDnelK3j8sR8cWIaLxr22evTyY6q8pRCjZSmhRWVjBaOVXChQd6AwMzi9pucorXCPZA==} + JSONStream@1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true @@ -2519,8 +3017,8 @@ packages: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} - ansi-escapes@7.0.0: - resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==} + ansi-escapes@7.2.0: + resolution: {integrity: sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==} engines: {node: '>=18'} ansi-regex@5.0.1: @@ -2543,6 +3041,10 @@ packages: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} + ansis@4.2.0: + resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} + engines: {node: '>=14'} + any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} @@ -2579,6 +3081,10 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + ast-kit@2.2.0: + resolution: {integrity: sha512-m1Q/RaVOnTp9JxPX+F+Zn7IcLYMzM8kZofDImfsKZd8MbR+ikdOzTeztStWqfrqIxZnYWryyI9ePm3NGjnZgGw==} + engines: {node: '>=20.19.0'} + async-listen@1.2.0: resolution: {integrity: sha512-CcEtRh/oc9Jc4uWeUwdpG/+Mb2YUHKmdaTf0gUr7Wa+bfp4xx70HOb3RuSTJMvqKNB1TkdTfjLdrcz2X4rkkZA==} @@ -2622,6 +3128,9 @@ packages: birpc@0.2.14: resolution: {integrity: sha512-37FHE8rqsYM5JEKCnXFyHpBCzvgHEExwVVTq+nUmloInU7l8ezD1TpOhKpS8oe1DTYFqEK27rFZVKG43oTqXRA==} + birpc@2.8.0: + resolution: {integrity: sha512-Bz2a4qD/5GRhiHSwj30c/8kC8QGj12nNDwz3D4ErQ4Xhy35dsSDvF+RA/tWpjyU0pdGtSDiEk6B5fBGE1qNVhw==} + blake3-wasm@2.1.5: resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} @@ -2695,12 +3204,12 @@ packages: resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} engines: {node: '>=12'} - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} + chai@6.2.1: + resolution: {integrity: sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==} + engines: {node: '>=18'} - chalk@5.4.1: - resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} chardet@0.7.0: @@ -2742,10 +3251,6 @@ packages: client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} - clipboardy@4.0.0: - resolution: {integrity: sha512-5mOlNS0mhX0707P2I0aZ2V/cmHUEO/fL7VFLqszkhUsxt7RwnmrInf/eEQKlf5GzvYeHIjT+Ov1HRfNmymlG0w==} - engines: {node: '>=18'} - cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -2782,6 +3287,10 @@ packages: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} + commander@14.0.2: + resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==} + engines: {node: '>=20'} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -2842,8 +3351,8 @@ packages: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} - cookie@1.0.2: - resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} engines: {node: '>=18'} core-js@3.46.0: @@ -2991,6 +3500,19 @@ packages: resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} engines: {node: '>=12'} + dotenv@17.2.3: + resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==} + engines: {node: '>=12'} + + dts-resolver@2.1.3: + resolution: {integrity: sha512-bihc7jPC90VrosXNzK0LTE2cuLP6jr0Ro8jk+kMugHReJVLIpHz/xadeq3MhuwyO4TD4OA3L1Q8pBBFRc08Tsw==} + engines: {node: '>=20.19.0'} + peerDependencies: + oxc-resolver: '>=11.0.0' + peerDependenciesMeta: + oxc-resolver: + optional: true + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -3212,6 +3734,11 @@ packages: engines: {node: '>=12'} hasBin: true + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + esbuild@0.25.4: resolution: {integrity: sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==} engines: {node: '>=18'} @@ -3255,12 +3782,8 @@ packages: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} - execa@8.0.1: - resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} - engines: {node: '>=16.17'} - - execa@9.6.0: - resolution: {integrity: sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==} + execa@9.6.1: + resolution: {integrity: sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==} engines: {node: ^18.19.0 || >=20.5.0} exit-hook@2.2.1: @@ -3321,6 +3844,15 @@ packages: picomatch: optional: true + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + fflate@0.4.8: resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==} @@ -3434,10 +3966,6 @@ packages: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} - get-stream@8.0.1: - resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} - engines: {node: '>=16'} - get-stream@9.0.1: resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} engines: {node: '>=18'} @@ -3445,6 +3973,9 @@ packages: get-tsconfig@4.10.1: resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} + get-tsconfig@4.13.0: + resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} + giget@2.0.0: resolution: {integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==} hasBin: true @@ -3501,9 +4032,9 @@ packages: resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==} engines: {node: '>=10'} - has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} + has-flag@5.0.1: + resolution: {integrity: sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==} + engines: {node: '>=12'} has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} @@ -3521,6 +4052,9 @@ packages: resolution: {integrity: sha512-61hl6MF6ojTl/8QSRu5ran6GXt+6zsngIUN95KzF5v5UjiX/xnrLR358BNRawwIRO49JwUqJqQe3Rb2v559R8Q==} engines: {node: '>=16.9.0'} + hookable@5.5.3: + resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + html-encoding-sniffer@4.0.0: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} @@ -3553,10 +4087,6 @@ packages: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} - human-signals@5.0.0: - resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} - engines: {node: '>=16.17.0'} - human-signals@8.0.1: resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} engines: {node: '>=18.18.0'} @@ -3588,6 +4118,10 @@ packages: import-meta-resolve@4.1.0: resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} + import-without-cache@0.2.0: + resolution: {integrity: sha512-I662PbFnhZjlpMQKeOgmZiuCOfMXfdX0Q4vMjDQ9cxIiOKNUzWJobE1FA+5ulKTssXlY6GY8l7hJ6Sy+/I5AEA==} + engines: {node: '>=20.19.0'} + indent-string@4.0.0: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} @@ -3616,11 +4150,6 @@ packages: resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} engines: {node: '>=4'} - is-docker@3.0.0: - resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - hasBin: true - is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -3633,11 +4162,6 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - is-inside-container@1.0.0: - resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} - engines: {node: '>=14.16'} - hasBin: true - is-node-process@1.2.0: resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} @@ -3663,10 +4187,6 @@ packages: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} - is-stream@3.0.0: - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - is-stream@4.0.1: resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} engines: {node: '>=18'} @@ -3687,14 +4207,6 @@ packages: resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} engines: {node: '>=0.10.0'} - is-wsl@3.1.0: - resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} - engines: {node: '>=16'} - - is64bit@2.0.0: - resolution: {integrity: sha512-jv+8jaWCl0g2lSBkNSVXdzfBA0npK1HGC2KtWM9FumFRoGS94g3NbCCLVnCYHLjp4GrW2KZeeSTMo5ddtznmGw==} - engines: {node: '>=18'} - isarray@0.0.1: resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} @@ -3720,6 +4232,10 @@ packages: resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==} hasBin: true + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + jose@5.9.6: resolution: {integrity: sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==} @@ -3910,6 +4426,9 @@ packages: magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} @@ -3975,11 +4494,7 @@ packages: mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - - mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} + engines: {node: '>=6'} min-indent@1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} @@ -4155,10 +4670,6 @@ packages: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} - npm-run-path@5.3.0: - resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - npm-run-path@6.0.0: resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} engines: {node: '>=18'} @@ -4179,6 +4690,9 @@ packages: obliterator@1.6.1: resolution: {integrity: sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig==} + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + ohash@2.0.11: resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} @@ -4196,9 +4710,8 @@ packages: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} - onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} - engines: {node: '>=12'} + openapi-types@12.1.3: + resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} os-paths@4.4.0: resolution: {integrity: sha512-wrAwOeXp1RRMFfQY8Sy7VaGVmPocaLwSFOYCGKSyo8qmJ+/yaafCl5BCA1IQZWqFSRBrKDYFeR9d/VyQzfH/jg==} @@ -4377,6 +4890,10 @@ packages: resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} engines: {node: '>=12'} + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + pify@4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} @@ -4479,9 +4996,16 @@ packages: quansync@0.2.10: resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==} + quansync@0.2.11: + resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + radash@12.1.1: + resolution: {integrity: sha512-h36JMxKRqrAxVD8201FrCpyeNuUY9Y5zZwujr20fFO77tpUtGa6EZzfKw/3WaiBX95fq7+MpsuMLNdSnORAwSA==} + engines: {node: '>=14.18.0'} + range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -4559,11 +5083,40 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rolldown-plugin-dts@0.18.1: + resolution: {integrity: sha512-uIgNMix6OI+6bSkw0nw6O+G/ydPRCWKwvvcEyL6gWkVkSFVGWWO23DX4ZYVOqC7w5u2c8uPY9Q74U0QCKvegFA==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@ts-macro/tsc': ^0.3.6 + '@typescript/native-preview': '>=7.0.0-dev.20250601.1' + rolldown: ^1.0.0-beta.51 + typescript: ^5.0.0 + vue-tsc: ~3.1.0 + peerDependenciesMeta: + '@ts-macro/tsc': + optional: true + '@typescript/native-preview': + optional: true + typescript: + optional: true + vue-tsc: + optional: true + + rolldown@1.0.0-beta.52: + resolution: {integrity: sha512-Hbnpljue+JhMJrlOjQ1ixp9me7sUec7OjFvS+A1Qm8k8Xyxmw3ZhxFu7LlSXW1s9AX3POE9W9o2oqCEeR5uDmg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + rollup@4.44.2: resolution: {integrity: sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rollup@4.53.3: + resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + router@2.2.0: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} engines: {node: '>= 18'} @@ -4601,6 +5154,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + send@1.2.0: resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} engines: {node: '>= 18'} @@ -4716,6 +5274,9 @@ packages: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + std-env@3.9.0: resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} @@ -4757,10 +5318,6 @@ packages: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} - strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} - engines: {node: '>=12'} - strip-final-newline@4.0.0: resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} engines: {node: '>=18'} @@ -4791,24 +5348,20 @@ packages: babel-plugin-macros: optional: true - supports-color@10.2.0: - resolution: {integrity: sha512-5eG9FQjEjDbAlI5+kdpdyPIBMRH4GfTVDGREVupaZHmVoppknhM29b/S9BkQz7cathp85BVgRi/As3Siln7e0Q==} + supports-color@10.2.2: + resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} engines: {node: '>=18'} - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - - supports-hyperlinks@3.2.0: - resolution: {integrity: sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==} - engines: {node: '>=14.18'} + supports-hyperlinks@4.3.0: + resolution: {integrity: sha512-i6sWEzuwadSlcr2mOnb0ktlIl+K5FVxsPXmoPfknDd2gyw4ZBIAZ5coc0NQzYqDdEYXMHy8NaY9rWwa1Q1myiQ==} + engines: {node: '>=20'} symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - system-architecture@0.1.0: - resolution: {integrity: sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==} - engines: {node: '>=18'} + tagged-tag@1.0.0: + resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} + engines: {node: '>=20'} tailwindcss@4.1.12: resolution: {integrity: sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA==} @@ -4829,9 +5382,9 @@ packages: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} - terminal-link@4.0.0: - resolution: {integrity: sha512-lk+vH+MccxNqgVqSnkMVKx4VLJfnLjDBGzH16JVZjKE2DoxP57s6/vt6JmXV5I3jBcfGrxNrYtC+mPtU7WJztA==} - engines: {node: '>=18'} + terminal-link@5.0.0: + resolution: {integrity: sha512-qFAy10MTMwjzjU8U16YS4YoZD+NQLHzLssFMNqgravjbvIPNiqkGFR4yjhJfmY9R5OFU7+yHxc6y+uGHkKwLRA==} + engines: {node: '>=20'} terser@5.16.9: resolution: {integrity: sha512-HPa/FdTB9XGI2H1/keLFZHxl6WNvAI4YalHGtDQTlMnJcoqSab1UwL4l1hGEhs6/GmLHBZIg/YgB++jcbzoOEg==} @@ -4862,10 +5415,18 @@ packages: tinyexec@1.0.1: resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} + tinyexec@1.0.2: + resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} + engines: {node: '>=18'} + tinyglobby@0.2.14: resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} engines: {node: '>=12.0.0'} + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + tinypool@1.1.1: resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} engines: {node: ^18.0.0 || >=20.0.0} @@ -4874,6 +5435,10 @@ packages: resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} engines: {node: '>=14.0.0'} + tinyrainbow@3.0.3: + resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} + engines: {node: '>=14.0.0'} + tinyspy@4.0.3: resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==} engines: {node: '>=14.0.0'} @@ -4920,6 +5485,31 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true + trpc-cli@0.12.1: + resolution: {integrity: sha512-/D/mIQf3tUrS7ZKJZ1gmSPJn2psAABJfkC5Eevm55SZ4s6KwANOUNlwhAGXN9HT4VSJVfoF2jettevE9vHPQlg==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + '@orpc/server': ^1.0.0 + '@trpc/server': ^10.45.2 || ^11.0.1 + '@valibot/to-json-schema': ^1.1.0 + effect: ^3.14.2 || ^4.0.0 + valibot: ^1.1.0 + zod: ^3.24.0 || ^4.0.0 + peerDependenciesMeta: + '@orpc/server': + optional: true + '@trpc/server': + optional: true + '@valibot/to-json-schema': + optional: true + effect: + optional: true + valibot: + optional: true + zod: + optional: true + ts-morph@12.0.0: resolution: {integrity: sha512-VHC8XgU2fFW7yO1f/b3mxKDje1vmyzFXHWzOYmKEkCEwcLjDtbdLgBQviqj4ZwP4MJkQtRo6Ha2I29lq/B+VxA==} @@ -4953,6 +5543,31 @@ packages: typescript: optional: true + tsdown@0.17.0-beta.5: + resolution: {integrity: sha512-Rmu4kmNsyYA4aI+h2okJKYaxj04pIJHlTxyYRprKWYaR1QOqos+rDfz3N38Sb5kGDXoZJl67Kqokjdk+p5oZ2A==} + engines: {node: '>=20.19.0'} + hasBin: true + peerDependencies: + '@arethetypeswrong/core': ^0.18.1 + '@vitejs/devtools': ^0.0.0-alpha.18 + publint: ^0.3.0 + typescript: ^5.0.0 + unplugin-lightningcss: ^0.4.0 + unplugin-unused: ^0.5.0 + peerDependenciesMeta: + '@arethetypeswrong/core': + optional: true + '@vitejs/devtools': + optional: true + publint: + optional: true + typescript: + optional: true + unplugin-lightningcss: + optional: true + unplugin-unused: + optional: true + tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} @@ -4964,6 +5579,10 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + type-fest@5.3.0: + resolution: {integrity: sha512-d9CwU93nN0IA1QL+GSNDdwLAu1Ew5ZjTwupvedwg3WdfoH6pIDvYQ2hV0Uc2nKBLPq7NB5apCx57MLS5qlmO5g==} + engines: {node: '>=20'} + type-is@2.0.1: resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} engines: {node: '>= 0.6'} @@ -4978,20 +5597,28 @@ packages: engines: {node: '>=14.17'} hasBin: true + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + ufo@1.6.1: resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} uid-promise@1.0.0: resolution: {integrity: sha512-R8375j0qwXyIu/7R0tjdF06/sElHqbmdmWC9M2qQHpEVbvE4I5+38KJI7LUUmQMp7NVq4tKHiBMkT0NFM453Ig==} + unconfig-core@7.4.1: + resolution: {integrity: sha512-Bp/bPZjV2Vl/fofoA2OYLSnw1Z0MOhCX7zHnVCYrazpfZvseBbGhwcNQMxsg185Mqh7VZQqK3C8hFG/Dyng+yA==} + undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - undici-types@7.8.0: - resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} undici@5.28.4: resolution: {integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==} @@ -5031,6 +5658,16 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} + unrun@0.2.15: + resolution: {integrity: sha512-UZ653WcLSK33meAX3nHXgD1JJ+t4RGa8WIzv9Dr4Y5ahhILZ5UIvObkVauKmtwwZ8Lsin3hUfso2UlzIwOiCNA==} + engines: {node: '>=20.19.0'} + hasBin: true + peerDependencies: + synckit: ^0.11.11 + peerDependenciesMeta: + synckit: + optional: true + update-browserslist-db@1.1.3: resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} hasBin: true @@ -5116,6 +5753,46 @@ packages: yaml: optional: true + vite@7.2.6: + resolution: {integrity: sha512-tI2l/nFHC5rLh7+5+o7QjKjSR04ivXDF4jcgV0f/bTQ+OJiITy5S6gaynVsEM+7RqzufMnVbIon6Sr5x1SDYaQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + vitest@3.2.4: resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -5144,6 +5821,40 @@ packages: jsdom: optional: true + vitest@4.0.15: + resolution: {integrity: sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.0.15 + '@vitest/browser-preview': 4.0.15 + '@vitest/browser-webdriverio': 4.0.15 + '@vitest/ui': 4.0.15 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + w3c-xmlserializer@5.0.0: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} @@ -5353,8 +6064,11 @@ packages: zod@3.22.3: resolution: {integrity: sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==} - zod@3.25.75: - resolution: {integrity: sha512-OhpzAmVzabPOL6C3A3gpAifqr9MqihV/Msx3gor2b2kviCgcb+HM9SEOpMWwwNp9MRunWnhtAKUoo0AHhjyPPg==} + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + + zod@4.1.13: + resolution: {integrity: sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==} snapshots: @@ -6424,6 +7138,14 @@ snapshots: '@jridgewell/trace-mapping': 0.3.30 jsesc: 3.1.0 + '@babel/generator@7.28.5': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + '@babel/helper-compilation-targets@7.27.2': dependencies: '@babel/compat-data': 7.28.4 @@ -6456,6 +7178,8 @@ snapshots: '@babel/helper-validator-identifier@7.27.1': {} + '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-option@7.27.1': {} '@babel/helpers@7.28.4': @@ -6467,6 +7191,10 @@ snapshots: dependencies: '@babel/types': 7.28.4 + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 @@ -6502,6 +7230,11 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 + '@babel/types@7.28.5': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@changesets/apply-release-plan@7.0.12': dependencies: '@changesets/config': 3.1.1 @@ -6671,18 +7404,18 @@ snapshots: optionalDependencies: workerd: 1.20250813.0 - '@cloudflare/vitest-pool-workers@0.8.49(@vitest/runner@3.2.4)(@vitest/snapshot@3.2.4)(vitest@3.2.4)': + '@cloudflare/vitest-pool-workers@0.8.49(@vitest/runner@4.0.15)(@vitest/snapshot@4.0.15)(vitest@3.2.4)': dependencies: - '@vitest/runner': 3.2.4 - '@vitest/snapshot': 3.2.4 + '@vitest/runner': 4.0.15 + '@vitest/snapshot': 4.0.15 birpc: 0.2.14 cjs-module-lexer: 1.4.3 devalue: 4.3.3 miniflare: 4.20250617.5 semver: 7.7.2 - vitest: 3.2.4(@edge-runtime/vm@3.2.0)(@types/node@24.0.14)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) + vitest: 3.2.4(@edge-runtime/vm@3.2.0)(@types/node@24.10.1)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) wrangler: 4.23.0 - zod: 3.25.75 + zod: 3.25.76 transitivePeerDependencies: - '@cloudflare/workers-types' - bufferutil @@ -6718,11 +7451,11 @@ snapshots: '@cloudflare/workerd-windows-64@1.20250813.0': optional: true - '@commitlint/cli@19.8.1(@types/node@24.0.14)(typescript@5.8.3)': + '@commitlint/cli@19.8.1(@types/node@24.10.1)(typescript@5.8.3)': dependencies: '@commitlint/format': 19.8.1 '@commitlint/lint': 19.8.1 - '@commitlint/load': 19.8.1(@types/node@24.0.14)(typescript@5.8.3) + '@commitlint/load': 19.8.1(@types/node@24.10.1)(typescript@5.8.3) '@commitlint/read': 19.8.1 '@commitlint/types': 19.8.1 tinyexec: 1.0.1 @@ -6755,12 +7488,12 @@ snapshots: '@commitlint/format@19.8.1': dependencies: '@commitlint/types': 19.8.1 - chalk: 5.4.1 + chalk: 5.6.2 '@commitlint/is-ignored@19.8.1': dependencies: '@commitlint/types': 19.8.1 - semver: 7.7.2 + semver: 7.7.3 '@commitlint/lint@19.8.1': dependencies: @@ -6769,15 +7502,15 @@ snapshots: '@commitlint/rules': 19.8.1 '@commitlint/types': 19.8.1 - '@commitlint/load@19.8.1(@types/node@24.0.14)(typescript@5.8.3)': + '@commitlint/load@19.8.1(@types/node@24.10.1)(typescript@5.8.3)': dependencies: '@commitlint/config-validator': 19.8.1 '@commitlint/execute-rule': 19.8.1 '@commitlint/resolve-extends': 19.8.1 '@commitlint/types': 19.8.1 - chalk: 5.4.1 + chalk: 5.6.2 cosmiconfig: 9.0.0(typescript@5.8.3) - cosmiconfig-typescript-loader: 6.1.0(@types/node@24.0.14)(cosmiconfig@9.0.0(typescript@5.8.3))(typescript@5.8.3) + cosmiconfig-typescript-loader: 6.1.0(@types/node@24.10.1)(cosmiconfig@9.0.0(typescript@5.8.3))(typescript@5.8.3) lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 lodash.uniq: 4.5.0 @@ -6826,7 +7559,7 @@ snapshots: '@commitlint/types@19.8.1': dependencies: '@types/conventional-commits-parser': 5.0.1 - chalk: 5.4.1 + chalk: 5.6.2 '@cspotcode/source-map-support@0.8.1': dependencies: @@ -6862,10 +7595,10 @@ snapshots: dotenv: 16.6.1 eciesjs: 0.4.15 execa: 5.1.1 - fdir: 6.4.6(picomatch@4.0.2) + fdir: 6.5.0(picomatch@4.0.3) ignore: 5.3.2 object-treeify: 1.1.33 - picomatch: 4.0.2 + picomatch: 4.0.3 which: 4.0.0 '@ecies/ciphers@0.2.4(@noble/ciphers@1.3.0)': @@ -6884,155 +7617,249 @@ snapshots: dependencies: '@edge-runtime/primitives': 4.1.0 + '@emnapi/core@1.7.1': + dependencies: + '@emnapi/wasi-threads': 1.1.0 + tslib: 2.8.1 + optional: true + '@emnapi/runtime@1.4.4': dependencies: tslib: 2.8.1 optional: true + '@emnapi/runtime@1.7.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.1.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.25.12': + optional: true + '@esbuild/aix-ppc64@0.25.4': optional: true '@esbuild/aix-ppc64@0.25.5': optional: true + '@esbuild/android-arm64@0.25.12': + optional: true + '@esbuild/android-arm64@0.25.4': optional: true '@esbuild/android-arm64@0.25.5': optional: true + '@esbuild/android-arm@0.25.12': + optional: true + '@esbuild/android-arm@0.25.4': optional: true '@esbuild/android-arm@0.25.5': optional: true + '@esbuild/android-x64@0.25.12': + optional: true + '@esbuild/android-x64@0.25.4': optional: true '@esbuild/android-x64@0.25.5': optional: true + '@esbuild/darwin-arm64@0.25.12': + optional: true + '@esbuild/darwin-arm64@0.25.4': optional: true '@esbuild/darwin-arm64@0.25.5': optional: true + '@esbuild/darwin-x64@0.25.12': + optional: true + '@esbuild/darwin-x64@0.25.4': optional: true '@esbuild/darwin-x64@0.25.5': optional: true + '@esbuild/freebsd-arm64@0.25.12': + optional: true + '@esbuild/freebsd-arm64@0.25.4': optional: true '@esbuild/freebsd-arm64@0.25.5': optional: true + '@esbuild/freebsd-x64@0.25.12': + optional: true + '@esbuild/freebsd-x64@0.25.4': optional: true '@esbuild/freebsd-x64@0.25.5': optional: true + '@esbuild/linux-arm64@0.25.12': + optional: true + '@esbuild/linux-arm64@0.25.4': optional: true '@esbuild/linux-arm64@0.25.5': optional: true + '@esbuild/linux-arm@0.25.12': + optional: true + '@esbuild/linux-arm@0.25.4': optional: true '@esbuild/linux-arm@0.25.5': optional: true + '@esbuild/linux-ia32@0.25.12': + optional: true + '@esbuild/linux-ia32@0.25.4': optional: true '@esbuild/linux-ia32@0.25.5': optional: true + '@esbuild/linux-loong64@0.25.12': + optional: true + '@esbuild/linux-loong64@0.25.4': optional: true '@esbuild/linux-loong64@0.25.5': optional: true + '@esbuild/linux-mips64el@0.25.12': + optional: true + '@esbuild/linux-mips64el@0.25.4': optional: true '@esbuild/linux-mips64el@0.25.5': optional: true + '@esbuild/linux-ppc64@0.25.12': + optional: true + '@esbuild/linux-ppc64@0.25.4': optional: true '@esbuild/linux-ppc64@0.25.5': optional: true + '@esbuild/linux-riscv64@0.25.12': + optional: true + '@esbuild/linux-riscv64@0.25.4': optional: true '@esbuild/linux-riscv64@0.25.5': optional: true + '@esbuild/linux-s390x@0.25.12': + optional: true + '@esbuild/linux-s390x@0.25.4': optional: true '@esbuild/linux-s390x@0.25.5': optional: true + '@esbuild/linux-x64@0.25.12': + optional: true + '@esbuild/linux-x64@0.25.4': optional: true '@esbuild/linux-x64@0.25.5': optional: true + '@esbuild/netbsd-arm64@0.25.12': + optional: true + '@esbuild/netbsd-arm64@0.25.4': optional: true '@esbuild/netbsd-arm64@0.25.5': optional: true + '@esbuild/netbsd-x64@0.25.12': + optional: true + '@esbuild/netbsd-x64@0.25.4': optional: true '@esbuild/netbsd-x64@0.25.5': optional: true + '@esbuild/openbsd-arm64@0.25.12': + optional: true + '@esbuild/openbsd-arm64@0.25.4': optional: true '@esbuild/openbsd-arm64@0.25.5': optional: true + '@esbuild/openbsd-x64@0.25.12': + optional: true + '@esbuild/openbsd-x64@0.25.4': optional: true '@esbuild/openbsd-x64@0.25.5': optional: true + '@esbuild/openharmony-arm64@0.25.12': + optional: true + + '@esbuild/sunos-x64@0.25.12': + optional: true + '@esbuild/sunos-x64@0.25.4': optional: true '@esbuild/sunos-x64@0.25.5': optional: true + '@esbuild/win32-arm64@0.25.12': + optional: true + '@esbuild/win32-arm64@0.25.4': optional: true '@esbuild/win32-arm64@0.25.5': optional: true + '@esbuild/win32-ia32@0.25.12': + optional: true + '@esbuild/win32-ia32@0.25.4': optional: true '@esbuild/win32-ia32@0.25.5': optional: true + '@esbuild/win32-x64@0.25.12': + optional: true + '@esbuild/win32-x64@0.25.4': optional: true @@ -7227,7 +8054,7 @@ snapshots: '@jridgewell/gen-mapping@0.3.13': dependencies: - '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/trace-mapping': 0.3.30 '@jridgewell/remapping@2.3.5': @@ -7240,19 +8067,26 @@ snapshots: '@jridgewell/source-map@0.3.11': dependencies: '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/sourcemap-codec@1.5.4': {} + '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/trace-mapping@0.3.30': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/trace-mapping@0.3.9': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/sourcemap-codec': 1.5.5 '@manypkg/find-root@1.1.0': dependencies: @@ -7277,7 +8111,7 @@ snapshots: https-proxy-agent: 7.0.6 node-fetch: 2.7.0 nopt: 8.1.0 - semver: 7.7.2 + semver: 7.7.3 tar: 7.4.3 transitivePeerDependencies: - encoding @@ -7294,6 +8128,13 @@ snapshots: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) + '@napi-rs/wasm-runtime@1.1.0': + dependencies: + '@emnapi/core': 1.7.1 + '@emnapi/runtime': 1.7.1 + '@tybys/wasm-util': 0.10.1 + optional: true + '@next/env@15.4.6': {} '@next/swc-darwin-arm64@15.4.6': @@ -7367,8 +8208,8 @@ snapshots: '@node-minify/terser': 8.0.6 '@tsconfig/node18': 1.0.3 aws4fetch: 1.0.20 - chalk: 5.4.1 - cookie: 1.0.2 + chalk: 5.6.2 + cookie: 1.1.1 esbuild: 0.25.4 express: 5.0.1 path-to-regexp: 6.3.0 @@ -7389,9 +8230,102 @@ snapshots: wrangler: 4.30.0 yargs: 18.0.0 transitivePeerDependencies: - - aws-crt - - encoding - - supports-color + - aws-crt + - encoding + - supports-color + + '@orpc/client@1.12.2': + dependencies: + '@orpc/shared': 1.12.2 + '@orpc/standard-server': 1.12.2 + '@orpc/standard-server-fetch': 1.12.2 + '@orpc/standard-server-peer': 1.12.2 + transitivePeerDependencies: + - '@opentelemetry/api' + + '@orpc/contract@1.12.2': + dependencies: + '@orpc/client': 1.12.2 + '@orpc/shared': 1.12.2 + '@standard-schema/spec': 1.0.0 + openapi-types: 12.1.3 + transitivePeerDependencies: + - '@opentelemetry/api' + + '@orpc/interop@1.12.2': {} + + '@orpc/server@1.12.2(ws@8.18.3)': + dependencies: + '@orpc/client': 1.12.2 + '@orpc/contract': 1.12.2 + '@orpc/interop': 1.12.2 + '@orpc/shared': 1.12.2 + '@orpc/standard-server': 1.12.2 + '@orpc/standard-server-aws-lambda': 1.12.2 + '@orpc/standard-server-fastify': 1.12.2 + '@orpc/standard-server-fetch': 1.12.2 + '@orpc/standard-server-node': 1.12.2 + '@orpc/standard-server-peer': 1.12.2 + cookie: 1.1.1 + optionalDependencies: + ws: 8.18.3 + transitivePeerDependencies: + - '@opentelemetry/api' + - fastify + + '@orpc/shared@1.12.2': + dependencies: + radash: 12.1.1 + type-fest: 5.3.0 + + '@orpc/standard-server-aws-lambda@1.12.2': + dependencies: + '@orpc/shared': 1.12.2 + '@orpc/standard-server': 1.12.2 + '@orpc/standard-server-fetch': 1.12.2 + '@orpc/standard-server-node': 1.12.2 + transitivePeerDependencies: + - '@opentelemetry/api' + + '@orpc/standard-server-fastify@1.12.2': + dependencies: + '@orpc/shared': 1.12.2 + '@orpc/standard-server': 1.12.2 + '@orpc/standard-server-node': 1.12.2 + transitivePeerDependencies: + - '@opentelemetry/api' + + '@orpc/standard-server-fetch@1.12.2': + dependencies: + '@orpc/shared': 1.12.2 + '@orpc/standard-server': 1.12.2 + transitivePeerDependencies: + - '@opentelemetry/api' + + '@orpc/standard-server-node@1.12.2': + dependencies: + '@orpc/shared': 1.12.2 + '@orpc/standard-server': 1.12.2 + '@orpc/standard-server-fetch': 1.12.2 + transitivePeerDependencies: + - '@opentelemetry/api' + + '@orpc/standard-server-peer@1.12.2': + dependencies: + '@orpc/shared': 1.12.2 + '@orpc/standard-server': 1.12.2 + transitivePeerDependencies: + - '@opentelemetry/api' + + '@orpc/standard-server@1.12.2': + dependencies: + '@orpc/shared': 1.12.2 + transitivePeerDependencies: + - '@opentelemetry/api' + + '@oxc-project/runtime@0.99.0': {} + + '@oxc-project/types@0.99.0': {} '@pkgjs/parseargs@0.11.0': optional: true @@ -7406,7 +8340,7 @@ snapshots: dependencies: '@poppinss/colors': 4.1.5 '@sindresorhus/is': 7.0.2 - supports-color: 10.2.0 + supports-color: 10.2.2 '@poppinss/exception@1.2.2': {} @@ -7475,76 +8409,192 @@ snapshots: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) + '@quansync/fs@0.1.5': + dependencies: + quansync: 0.2.11 + + '@rolldown/binding-android-arm64@1.0.0-beta.52': + optional: true + + '@rolldown/binding-darwin-arm64@1.0.0-beta.52': + optional: true + + '@rolldown/binding-darwin-x64@1.0.0-beta.52': + optional: true + + '@rolldown/binding-freebsd-x64@1.0.0-beta.52': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.52': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.52': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.52': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.52': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.0-beta.52': + optional: true + + '@rolldown/binding-openharmony-arm64@1.0.0-beta.52': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.0-beta.52': + dependencies: + '@napi-rs/wasm-runtime': 1.1.0 + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.52': + optional: true + + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.52': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.52': + optional: true + '@rolldown/pluginutils@1.0.0-beta.34': {} - '@rollup/pluginutils@5.2.0(rollup@4.44.2)': + '@rolldown/pluginutils@1.0.0-beta.52': {} + + '@rollup/pluginutils@5.2.0(rollup@4.53.3)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 - picomatch: 4.0.2 + picomatch: 4.0.3 optionalDependencies: - rollup: 4.44.2 + rollup: 4.53.3 '@rollup/rollup-android-arm-eabi@4.44.2': optional: true + '@rollup/rollup-android-arm-eabi@4.53.3': + optional: true + '@rollup/rollup-android-arm64@4.44.2': optional: true + '@rollup/rollup-android-arm64@4.53.3': + optional: true + '@rollup/rollup-darwin-arm64@4.44.2': optional: true + '@rollup/rollup-darwin-arm64@4.53.3': + optional: true + '@rollup/rollup-darwin-x64@4.44.2': optional: true + '@rollup/rollup-darwin-x64@4.53.3': + optional: true + '@rollup/rollup-freebsd-arm64@4.44.2': optional: true + '@rollup/rollup-freebsd-arm64@4.53.3': + optional: true + '@rollup/rollup-freebsd-x64@4.44.2': optional: true + '@rollup/rollup-freebsd-x64@4.53.3': + optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.44.2': optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.53.3': + optional: true + '@rollup/rollup-linux-arm-musleabihf@4.44.2': optional: true + '@rollup/rollup-linux-arm-musleabihf@4.53.3': + optional: true + '@rollup/rollup-linux-arm64-gnu@4.44.2': optional: true + '@rollup/rollup-linux-arm64-gnu@4.53.3': + optional: true + '@rollup/rollup-linux-arm64-musl@4.44.2': optional: true + '@rollup/rollup-linux-arm64-musl@4.53.3': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.53.3': + optional: true + '@rollup/rollup-linux-loongarch64-gnu@4.44.2': optional: true '@rollup/rollup-linux-powerpc64le-gnu@4.44.2': optional: true + '@rollup/rollup-linux-ppc64-gnu@4.53.3': + optional: true + '@rollup/rollup-linux-riscv64-gnu@4.44.2': optional: true + '@rollup/rollup-linux-riscv64-gnu@4.53.3': + optional: true + '@rollup/rollup-linux-riscv64-musl@4.44.2': optional: true + '@rollup/rollup-linux-riscv64-musl@4.53.3': + optional: true + '@rollup/rollup-linux-s390x-gnu@4.44.2': optional: true + '@rollup/rollup-linux-s390x-gnu@4.53.3': + optional: true + '@rollup/rollup-linux-x64-gnu@4.44.2': optional: true + '@rollup/rollup-linux-x64-gnu@4.53.3': + optional: true + '@rollup/rollup-linux-x64-musl@4.44.2': optional: true + '@rollup/rollup-linux-x64-musl@4.53.3': + optional: true + + '@rollup/rollup-openharmony-arm64@4.53.3': + optional: true + '@rollup/rollup-win32-arm64-msvc@4.44.2': optional: true + '@rollup/rollup-win32-arm64-msvc@4.53.3': + optional: true + '@rollup/rollup-win32-ia32-msvc@4.44.2': optional: true + '@rollup/rollup-win32-ia32-msvc@4.53.3': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.53.3': + optional: true + '@rollup/rollup-win32-x64-msvc@4.44.2': optional: true + '@rollup/rollup-win32-x64-msvc@4.53.3': + optional: true + '@sec-ant/readable-stream@0.4.1': {} '@sinclair/typebox@0.25.24': {} @@ -8133,7 +9183,7 @@ snapshots: enhanced-resolve: 5.18.3 jiti: 2.5.1 lightningcss: 1.30.1 - magic-string: 0.30.17 + magic-string: 0.30.21 source-map-js: 1.2.1 tailwindcss: 4.1.12 @@ -8248,6 +9298,11 @@ snapshots: '@tsconfig/node18@1.0.3': {} + '@tybys/wasm-util@0.10.1': + dependencies: + tslib: 2.8.1 + optional: true + '@types/aria-query@5.0.4': {} '@types/babel__core@7.20.5': @@ -8275,9 +9330,14 @@ snapshots: dependencies: '@types/deep-eql': 4.0.2 + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + '@types/conventional-commits-parser@5.0.1': dependencies: - '@types/node': 20.19.11 + '@types/node': 24.10.1 '@types/deep-eql@4.0.2': {} @@ -8294,7 +9354,7 @@ snapshots: '@types/node@16.18.11': {} - '@types/node@18.19.123': + '@types/node@18.19.130': dependencies: undici-types: 5.26.5 @@ -8302,9 +9362,9 @@ snapshots: dependencies: undici-types: 6.21.0 - '@types/node@24.0.14': + '@types/node@24.10.1': dependencies: - undici-types: 7.8.0 + undici-types: 7.16.0 '@types/pg@8.15.5': dependencies: @@ -8334,9 +9394,9 @@ snapshots: '@vercel/error-utils@2.0.3': {} - '@vercel/express@0.0.6(rollup@4.44.2)': + '@vercel/express@0.0.6(rollup@4.53.3)': dependencies: - '@vercel/node': 5.3.13(rollup@4.44.2) + '@vercel/node': 5.3.13(rollup@4.53.3) '@vercel/static-config': 3.1.1 ts-morph: 12.0.0 transitivePeerDependencies: @@ -8384,9 +9444,9 @@ snapshots: '@vercel/go@3.2.3': {} - '@vercel/hono@0.0.14(rollup@4.44.2)': + '@vercel/hono@0.0.14(rollup@4.53.3)': dependencies: - '@vercel/node': 5.3.13(rollup@4.44.2) + '@vercel/node': 5.3.13(rollup@4.53.3) '@vercel/static-config': 3.1.1 ts-morph: 12.0.0 transitivePeerDependencies: @@ -8401,18 +9461,18 @@ snapshots: '@vercel/static-config': 3.1.1 ts-morph: 12.0.0 - '@vercel/next@4.11.3(rollup@4.44.2)': + '@vercel/next@4.11.3(rollup@4.53.3)': dependencies: - '@vercel/nft': 0.29.2(rollup@4.44.2) + '@vercel/nft': 0.29.2(rollup@4.53.3) transitivePeerDependencies: - encoding - rollup - supports-color - '@vercel/nft@0.29.2(rollup@4.44.2)': + '@vercel/nft@0.29.2(rollup@4.53.3)': dependencies: '@mapbox/node-pre-gyp': 2.0.0 - '@rollup/pluginutils': 5.2.0(rollup@4.44.2) + '@rollup/pluginutils': 5.2.0(rollup@4.53.3) acorn: 8.14.0 acorn-import-attributes: 1.9.5(acorn@8.14.0) async-sema: 3.1.1 @@ -8421,14 +9481,14 @@ snapshots: glob: 10.4.5 graceful-fs: 4.2.11 node-gyp-build: 4.8.4 - picomatch: 4.0.2 + picomatch: 4.0.3 resolve-from: 5.0.0 transitivePeerDependencies: - encoding - rollup - supports-color - '@vercel/node@5.3.13(rollup@4.44.2)': + '@vercel/node@5.3.13(rollup@4.53.3)': dependencies: '@edge-runtime/node-utils': 2.3.0 '@edge-runtime/primitives': 4.1.0 @@ -8436,7 +9496,7 @@ snapshots: '@types/node': 16.18.11 '@vercel/build-utils': 11.0.1 '@vercel/error-utils': 2.0.3 - '@vercel/nft': 0.29.2(rollup@4.44.2) + '@vercel/nft': 0.29.2(rollup@4.53.3) '@vercel/static-config': 3.1.1 async-listen: 3.0.0 cjs-module-lexer: 1.2.3 @@ -8460,9 +9520,9 @@ snapshots: '@vercel/python@5.0.0': {} - '@vercel/redwood@2.3.4(rollup@4.44.2)': + '@vercel/redwood@2.3.4(rollup@4.53.3)': dependencies: - '@vercel/nft': 0.29.2(rollup@4.44.2) + '@vercel/nft': 0.29.2(rollup@4.53.3) '@vercel/static-config': 3.1.1 semver: 6.3.1 ts-morph: 12.0.0 @@ -8471,10 +9531,10 @@ snapshots: - rollup - supports-color - '@vercel/remix-builder@5.4.10(rollup@4.44.2)': + '@vercel/remix-builder@5.4.10(rollup@4.53.3)': dependencies: '@vercel/error-utils': 2.0.3 - '@vercel/nft': 0.29.2(rollup@4.44.2) + '@vercel/nft': 0.29.2(rollup@4.53.3) '@vercel/static-config': 3.1.1 path-to-regexp: 6.1.0 path-to-regexp-updated: path-to-regexp@6.3.0 @@ -8499,7 +9559,7 @@ snapshots: json-schema-to-ts: 1.6.4 ts-morph: 12.0.0 - '@vitejs/plugin-react@5.0.2(vite@7.0.2(@types/node@20.19.11)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1))': + '@vitejs/plugin-react@5.0.2(vite@7.2.6(@types/node@20.19.11)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1))': dependencies: '@babel/core': 7.28.4 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.4) @@ -8507,7 +9567,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.34 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 7.0.2(@types/node@20.19.11)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) + vite: 7.2.6(@types/node@20.19.11)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) transitivePeerDependencies: - supports-color @@ -8519,42 +9579,76 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(vite@7.0.2(@types/node@20.19.11)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1))': + '@vitest/expect@4.0.15': + dependencies: + '@standard-schema/spec': 1.0.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.0.15 + '@vitest/utils': 4.0.15 + chai: 6.2.1 + tinyrainbow: 3.0.3 + + '@vitest/mocker@3.2.4(vite@7.0.2(@types/node@20.19.11)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 7.0.2(@types/node@20.19.11)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) + vite: 7.0.2(@types/node@20.19.11)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) - '@vitest/mocker@3.2.4(vite@7.0.2(@types/node@24.0.14)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1))': + '@vitest/mocker@3.2.4(vite@7.0.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 7.0.2(@types/node@24.0.14)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) + vite: 7.0.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) + + '@vitest/mocker@4.0.15(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1))': + dependencies: + '@vitest/spy': 4.0.15 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) '@vitest/pretty-format@3.2.4': dependencies: tinyrainbow: 2.0.0 + '@vitest/pretty-format@4.0.15': + dependencies: + tinyrainbow: 3.0.3 + '@vitest/runner@3.2.4': dependencies: '@vitest/utils': 3.2.4 pathe: 2.0.3 strip-literal: 3.0.0 + '@vitest/runner@4.0.15': + dependencies: + '@vitest/utils': 4.0.15 + pathe: 2.0.3 + '@vitest/snapshot@3.2.4': dependencies: '@vitest/pretty-format': 3.2.4 magic-string: 0.30.17 pathe: 2.0.3 + '@vitest/snapshot@4.0.15': + dependencies: + '@vitest/pretty-format': 4.0.15 + magic-string: 0.30.21 + pathe: 2.0.3 + '@vitest/spy@3.2.4': dependencies: tinyspy: 4.0.3 + '@vitest/spy@4.0.15': {} + '@vitest/ui@3.2.4(vitest@3.2.4)': dependencies: '@vitest/utils': 3.2.4 @@ -8564,7 +9658,7 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.14 tinyrainbow: 2.0.0 - vitest: 3.2.4(@edge-runtime/vm@3.2.0)(@types/node@20.19.11)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) + vitest: 3.2.4(@edge-runtime/vm@3.2.0)(@types/node@20.19.11)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) '@vitest/utils@3.2.4': dependencies: @@ -8572,6 +9666,11 @@ snapshots: loupe: 3.1.4 tinyrainbow: 2.0.0 + '@vitest/utils@4.0.15': + dependencies: + '@vitest/pretty-format': 4.0.15 + tinyrainbow: 3.0.3 + JSONStream@1.3.5: dependencies: jsonparse: 1.3.1 @@ -8618,7 +9717,7 @@ snapshots: ansi-colors@4.1.3: {} - ansi-escapes@7.0.0: + ansi-escapes@7.2.0: dependencies: environment: 1.1.0 @@ -8634,6 +9733,8 @@ snapshots: ansi-styles@6.2.1: {} + ansis@4.2.0: {} + any-promise@1.3.0: {} arg@4.1.0: {} @@ -8662,6 +9763,11 @@ snapshots: assertion-error@2.0.1: {} + ast-kit@2.2.0: + dependencies: + '@babel/parser': 7.28.5 + pathe: 2.0.3 + async-listen@1.2.0: {} async-listen@3.0.0: {} @@ -8696,6 +9802,8 @@ snapshots: birpc@0.2.14: {} + birpc@2.8.0: {} + blake3-wasm@2.1.5: {} body-parser@2.2.0: @@ -8784,12 +9892,9 @@ snapshots: loupe: 3.1.4 pathval: 2.0.1 - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 + chai@6.2.1: {} - chalk@5.4.1: {} + chalk@5.6.2: {} chardet@0.7.0: {} @@ -8819,12 +9924,6 @@ snapshots: client-only@0.0.1: {} - clipboardy@4.0.0: - dependencies: - execa: 8.0.1 - is-wsl: 3.1.0 - is64bit: 2.0.0 - cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -8839,7 +9938,7 @@ snapshots: cloudflare@4.5.0: dependencies: - '@types/node': 18.19.123 + '@types/node': 18.19.130 '@types/node-fetch': 2.6.13 abort-controller: 3.0.0 agentkeepalive: 4.6.0 @@ -8873,6 +9972,8 @@ snapshots: commander@11.1.0: {} + commander@14.0.2: {} + commander@2.20.3: {} compare-func@2.0.0: @@ -8919,13 +10020,13 @@ snapshots: cookie@0.7.2: {} - cookie@1.0.2: {} + cookie@1.1.1: {} core-js@3.46.0: {} - cosmiconfig-typescript-loader@6.1.0(@types/node@24.0.14)(cosmiconfig@9.0.0(typescript@5.8.3))(typescript@5.8.3): + cosmiconfig-typescript-loader@6.1.0(@types/node@24.10.1)(cosmiconfig@9.0.0(typescript@5.8.3))(typescript@5.8.3): dependencies: - '@types/node': 24.0.14 + '@types/node': 24.10.1 cosmiconfig: 9.0.0(typescript@5.8.3) jiti: 2.4.2 typescript: 5.8.3 @@ -9025,6 +10126,10 @@ snapshots: dotenv@16.6.1: {} + dotenv@17.2.3: {} + + dts-resolver@2.1.3: {} + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -9201,6 +10306,35 @@ snapshots: esbuild-windows-64: 0.14.47 esbuild-windows-arm64: 0.14.47 + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + esbuild@0.25.4: optionalDependencies: '@esbuild/aix-ppc64': 0.25.4 @@ -9287,19 +10421,7 @@ snapshots: signal-exit: 3.0.7 strip-final-newline: 2.0.0 - execa@8.0.1: - dependencies: - cross-spawn: 7.0.6 - get-stream: 8.0.1 - human-signals: 5.0.0 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.3.0 - onetime: 6.0.0 - signal-exit: 4.1.0 - strip-final-newline: 3.0.0 - - execa@9.6.0: + execa@9.6.1: dependencies: '@sindresorhus/merge-streams': 4.0.0 cross-spawn: 7.0.6 @@ -9401,6 +10523,14 @@ snapshots: optionalDependencies: picomatch: 4.0.2 + fdir@6.4.6(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + fflate@0.4.8: {} fflate@0.8.2: {} @@ -9525,8 +10655,6 @@ snapshots: get-stream@6.0.1: {} - get-stream@8.0.1: {} - get-stream@9.0.1: dependencies: '@sec-ant/readable-stream': 0.4.1 @@ -9536,6 +10664,10 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + get-tsconfig@4.13.0: + dependencies: + resolve-pkg-maps: 1.0.0 + giget@2.0.0: dependencies: citty: 0.1.6 @@ -9609,7 +10741,7 @@ snapshots: dependencies: duplexer: 0.1.2 - has-flag@4.0.0: {} + has-flag@5.0.1: {} has-symbols@1.1.0: {} @@ -9623,6 +10755,8 @@ snapshots: hono@4.9.4: {} + hookable@5.5.3: {} + html-encoding-sniffer@4.0.0: dependencies: whatwg-encoding: 3.1.1 @@ -9666,8 +10800,6 @@ snapshots: human-signals@2.1.0: {} - human-signals@5.0.0: {} - human-signals@8.0.1: {} humanize-ms@1.2.1: @@ -9693,6 +10825,8 @@ snapshots: import-meta-resolve@4.1.0: {} + import-without-cache@0.2.0: {} + indent-string@4.0.0: {} inherits@2.0.1: {} @@ -9709,8 +10843,6 @@ snapshots: is-buffer@2.0.5: {} - is-docker@3.0.0: {} - is-extglob@2.1.1: {} is-fullwidth-code-point@3.0.0: {} @@ -9719,10 +10851,6 @@ snapshots: dependencies: is-extglob: 2.1.1 - is-inside-container@1.0.0: - dependencies: - is-docker: 3.0.0 - is-node-process@1.2.0: {} is-number@7.0.0: {} @@ -9737,8 +10865,6 @@ snapshots: is-stream@2.0.1: {} - is-stream@3.0.0: {} - is-stream@4.0.1: {} is-subdir@1.2.0: @@ -9753,14 +10879,6 @@ snapshots: is-windows@1.0.2: {} - is-wsl@3.1.0: - dependencies: - is-inside-container: 1.0.0 - - is64bit@2.0.0: - dependencies: - system-architecture: 0.1.0 - isarray@0.0.1: {} isexe@2.0.0: {} @@ -9781,6 +10899,9 @@ snapshots: jiti@2.5.1: {} + jiti@2.6.1: + optional: true + jose@5.9.6: {} js-tokens@4.0.0: {} @@ -9948,6 +11069,10 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.4 + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + make-error@1.3.6: {} math-intrinsics@1.1.0: {} @@ -9993,8 +11118,6 @@ snapshots: mimic-fn@2.1.0: {} - mimic-fn@4.0.0: {} - min-indent@1.0.1: {} miniflare@4.20250617.5: @@ -10147,10 +11270,6 @@ snapshots: dependencies: path-key: 3.1.1 - npm-run-path@5.3.0: - dependencies: - path-key: 4.0.0 - npm-run-path@6.0.0: dependencies: path-key: 4.0.0 @@ -10162,7 +11281,7 @@ snapshots: consola: 3.4.2 pathe: 2.0.3 pkg-types: 2.2.0 - tinyexec: 1.0.1 + tinyexec: 1.0.2 object-inspect@1.13.4: {} @@ -10170,6 +11289,8 @@ snapshots: obliterator@1.6.1: {} + obug@2.1.1: {} + ohash@2.0.11: {} on-finished@2.4.1: @@ -10188,9 +11309,7 @@ snapshots: dependencies: mimic-fn: 2.1.0 - onetime@6.0.0: - dependencies: - mimic-fn: 4.0.0 + openapi-types@12.1.3: {} os-paths@4.4.0: {} @@ -10337,6 +11456,8 @@ snapshots: picomatch@4.0.2: {} + picomatch@4.0.3: {} + pify@4.0.1: {} pkg-types@2.2.0: @@ -10433,8 +11554,12 @@ snapshots: quansync@0.2.10: {} + quansync@0.2.11: {} + queue-microtask@1.2.3: {} + radash@12.1.1: {} + range-parser@1.2.1: {} raw-body@2.4.1: @@ -10502,6 +11627,43 @@ snapshots: reusify@1.1.0: {} + rolldown-plugin-dts@0.18.1(rolldown@1.0.0-beta.52)(typescript@5.9.3): + dependencies: + '@babel/generator': 7.28.5 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + ast-kit: 2.2.0 + birpc: 2.8.0 + dts-resolver: 2.1.3 + get-tsconfig: 4.13.0 + magic-string: 0.30.21 + obug: 2.1.1 + rolldown: 1.0.0-beta.52 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - oxc-resolver + + rolldown@1.0.0-beta.52: + dependencies: + '@oxc-project/types': 0.99.0 + '@rolldown/pluginutils': 1.0.0-beta.52 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.0-beta.52 + '@rolldown/binding-darwin-arm64': 1.0.0-beta.52 + '@rolldown/binding-darwin-x64': 1.0.0-beta.52 + '@rolldown/binding-freebsd-x64': 1.0.0-beta.52 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.52 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.52 + '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.52 + '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.52 + '@rolldown/binding-linux-x64-musl': 1.0.0-beta.52 + '@rolldown/binding-openharmony-arm64': 1.0.0-beta.52 + '@rolldown/binding-wasm32-wasi': 1.0.0-beta.52 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.52 + '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.52 + '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.52 + rollup@4.44.2: dependencies: '@types/estree': 1.0.8 @@ -10528,6 +11690,34 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.44.2 fsevents: 2.3.3 + rollup@4.53.3: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.53.3 + '@rollup/rollup-android-arm64': 4.53.3 + '@rollup/rollup-darwin-arm64': 4.53.3 + '@rollup/rollup-darwin-x64': 4.53.3 + '@rollup/rollup-freebsd-arm64': 4.53.3 + '@rollup/rollup-freebsd-x64': 4.53.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.53.3 + '@rollup/rollup-linux-arm-musleabihf': 4.53.3 + '@rollup/rollup-linux-arm64-gnu': 4.53.3 + '@rollup/rollup-linux-arm64-musl': 4.53.3 + '@rollup/rollup-linux-loong64-gnu': 4.53.3 + '@rollup/rollup-linux-ppc64-gnu': 4.53.3 + '@rollup/rollup-linux-riscv64-gnu': 4.53.3 + '@rollup/rollup-linux-riscv64-musl': 4.53.3 + '@rollup/rollup-linux-s390x-gnu': 4.53.3 + '@rollup/rollup-linux-x64-gnu': 4.53.3 + '@rollup/rollup-linux-x64-musl': 4.53.3 + '@rollup/rollup-openharmony-arm64': 4.53.3 + '@rollup/rollup-win32-arm64-msvc': 4.53.3 + '@rollup/rollup-win32-ia32-msvc': 4.53.3 + '@rollup/rollup-win32-x64-gnu': 4.53.3 + '@rollup/rollup-win32-x64-msvc': 4.53.3 + fsevents: 2.3.3 + router@2.2.0: dependencies: debug: 4.4.1 @@ -10562,6 +11752,8 @@ snapshots: semver@7.7.2: {} + semver@7.7.3: {} + send@1.2.0: dependencies: debug: 4.4.1 @@ -10595,7 +11787,7 @@ snapshots: dependencies: color: 4.2.3 detect-libc: 2.0.4 - semver: 7.7.2 + semver: 7.7.3 optionalDependencies: '@img/sharp-darwin-arm64': 0.33.5 '@img/sharp-darwin-x64': 0.33.5 @@ -10621,7 +11813,7 @@ snapshots: dependencies: color: 4.2.3 detect-libc: 2.0.4 - semver: 7.7.2 + semver: 7.7.3 optionalDependencies: '@img/sharp-darwin-arm64': 0.34.3 '@img/sharp-darwin-x64': 0.34.3 @@ -10736,6 +11928,8 @@ snapshots: statuses@2.0.1: {} + std-env@3.10.0: {} + std-env@3.9.0: {} stoppable@1.1.0: {} @@ -10780,8 +11974,6 @@ snapshots: strip-final-newline@2.0.0: {} - strip-final-newline@3.0.0: {} - strip-final-newline@4.0.0: {} strip-indent@3.0.0: @@ -10803,20 +11995,16 @@ snapshots: optionalDependencies: '@babel/core': 7.28.4 - supports-color@10.2.0: {} - - supports-color@7.2.0: - dependencies: - has-flag: 4.0.0 + supports-color@10.2.2: {} - supports-hyperlinks@3.2.0: + supports-hyperlinks@4.3.0: dependencies: - has-flag: 4.0.0 - supports-color: 7.2.0 + has-flag: 5.0.1 + supports-color: 10.2.2 symbol-tree@3.2.4: {} - system-architecture@0.1.0: {} + tagged-tag@1.0.0: {} tailwindcss@4.1.12: {} @@ -10842,10 +12030,10 @@ snapshots: term-size@2.2.1: {} - terminal-link@4.0.0: + terminal-link@5.0.0: dependencies: - ansi-escapes: 7.0.0 - supports-hyperlinks: 3.2.0 + ansi-escapes: 7.2.0 + supports-hyperlinks: 4.3.0 terser@5.16.9: dependencies: @@ -10870,15 +12058,24 @@ snapshots: tinyexec@1.0.1: {} + tinyexec@1.0.2: {} + tinyglobby@0.2.14: dependencies: - fdir: 6.4.6(picomatch@4.0.2) - picomatch: 4.0.2 + fdir: 6.4.6(picomatch@4.0.3) + picomatch: 4.0.3 + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 tinypool@1.1.1: {} tinyrainbow@2.0.0: {} + tinyrainbow@3.0.3: {} + tinyspy@4.0.3: {} tldts-core@7.0.14: {} @@ -10913,6 +12110,14 @@ snapshots: tree-kill@1.2.2: {} + trpc-cli@0.12.1(@orpc/server@1.12.2(ws@8.18.3))(effect@3.16.12)(zod@4.1.13): + dependencies: + commander: 14.0.2 + optionalDependencies: + '@orpc/server': 1.12.2(ws@8.18.3) + effect: 3.16.12 + zod: 4.1.13 + ts-morph@12.0.0: dependencies: '@ts-morph/common': 0.11.1 @@ -10944,6 +12149,31 @@ snapshots: optionalDependencies: typescript: 5.8.3 + tsdown@0.17.0-beta.5(typescript@5.9.3): + dependencies: + ansis: 4.2.0 + cac: 6.7.14 + empathic: 2.0.0 + hookable: 5.5.3 + import-without-cache: 0.2.0 + obug: 2.1.1 + rolldown: 1.0.0-beta.52 + rolldown-plugin-dts: 0.18.1(rolldown@1.0.0-beta.52)(typescript@5.9.3) + semver: 7.7.3 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tree-kill: 1.2.2 + unconfig-core: 7.4.1 + unrun: 0.2.15 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@ts-macro/tsc' + - '@typescript/native-preview' + - oxc-resolver + - synckit + - vue-tsc + tslib@1.14.1: {} tslib@2.8.1: {} @@ -10955,6 +12185,10 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + type-fest@5.3.0: + dependencies: + tagged-tag: 1.0.0 + type-is@2.0.1: dependencies: content-type: 1.0.5 @@ -10965,15 +12199,22 @@ snapshots: typescript@5.8.3: {} + typescript@5.9.3: {} + ufo@1.6.1: {} uid-promise@1.0.0: {} + unconfig-core@7.4.1: + dependencies: + '@quansync/fs': 0.1.5 + quansync: 0.2.11 + undici-types@5.26.5: {} undici-types@6.21.0: {} - undici-types@7.8.0: {} + undici-types@7.16.0: {} undici@5.28.4: dependencies: @@ -11011,6 +12252,11 @@ snapshots: unpipe@1.0.0: {} + unrun@0.2.15: + dependencies: + '@oxc-project/runtime': 0.99.0 + rolldown: 1.0.0-beta.52 + update-browserslist-db@1.1.3(browserslist@4.26.2): dependencies: browserslist: 4.26.2 @@ -11031,20 +12277,20 @@ snapshots: vary@1.1.2: {} - vercel@46.0.2(rollup@4.44.2): + vercel@46.0.2(rollup@4.53.3): dependencies: '@vercel/blob': 1.0.2 '@vercel/build-utils': 11.0.1 - '@vercel/express': 0.0.6(rollup@4.44.2) + '@vercel/express': 0.0.6(rollup@4.53.3) '@vercel/fun': 1.1.6 '@vercel/go': 3.2.3 - '@vercel/hono': 0.0.14(rollup@4.44.2) + '@vercel/hono': 0.0.14(rollup@4.53.3) '@vercel/hydrogen': 1.2.3 - '@vercel/next': 4.11.3(rollup@4.44.2) - '@vercel/node': 5.3.13(rollup@4.44.2) + '@vercel/next': 4.11.3(rollup@4.53.3) + '@vercel/node': 5.3.13(rollup@4.53.3) '@vercel/python': 5.0.0 - '@vercel/redwood': 2.3.4(rollup@4.44.2) - '@vercel/remix-builder': 5.4.10(rollup@4.44.2) + '@vercel/redwood': 2.3.4(rollup@4.53.3) + '@vercel/remix-builder': 5.4.10(rollup@4.53.3) '@vercel/ruby': 2.2.1 '@vercel/static-build': 2.7.18 chokidar: 4.0.0 @@ -11056,13 +12302,13 @@ snapshots: - rollup - supports-color - vite-node@3.2.4(@types/node@20.19.11)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1): + vite-node@3.2.4(@types/node@20.19.11)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1): dependencies: cac: 6.7.14 debug: 4.4.1 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.0.2(@types/node@20.19.11)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) + vite: 7.0.2(@types/node@20.19.11)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) transitivePeerDependencies: - '@types/node' - jiti @@ -11077,13 +12323,13 @@ snapshots: - tsx - yaml - vite-node@3.2.4(@types/node@24.0.14)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1): + vite-node@3.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1): dependencies: cac: 6.7.14 debug: 4.4.1 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.0.2(@types/node@24.0.14)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) + vite: 7.0.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) transitivePeerDependencies: - '@types/node' - jiti @@ -11098,18 +12344,18 @@ snapshots: - tsx - yaml - vite-tsconfig-paths@5.1.4(typescript@5.8.3)(vite@7.0.2(@types/node@20.19.11)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1)): + vite-tsconfig-paths@5.1.4(typescript@5.8.3)(vite@7.2.6(@types/node@20.19.11)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1)): dependencies: debug: 4.4.1 globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.8.3) optionalDependencies: - vite: 7.0.2(@types/node@20.19.11)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) + vite: 7.2.6(@types/node@20.19.11)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) transitivePeerDependencies: - supports-color - typescript - vite@7.0.2(@types/node@20.19.11)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1): + vite@7.0.2(@types/node@20.19.11)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1): dependencies: esbuild: 0.25.5 fdir: 6.4.6(picomatch@4.0.2) @@ -11120,13 +12366,13 @@ snapshots: optionalDependencies: '@types/node': 20.19.11 fsevents: 2.3.3 - jiti: 2.5.1 + jiti: 2.6.1 lightningcss: 1.30.1 terser: 5.16.9 tsx: 4.20.4 yaml: 2.8.1 - vite@7.0.2(@types/node@24.0.14)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1): + vite@7.0.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1): dependencies: esbuild: 0.25.5 fdir: 6.4.6(picomatch@4.0.2) @@ -11135,19 +12381,53 @@ snapshots: rollup: 4.44.2 tinyglobby: 0.2.14 optionalDependencies: - '@types/node': 24.0.14 + '@types/node': 24.10.1 fsevents: 2.3.3 - jiti: 2.5.1 + jiti: 2.6.1 + lightningcss: 1.30.1 + terser: 5.16.9 + tsx: 4.20.4 + yaml: 2.8.1 + + vite@7.2.6(@types/node@20.19.11)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.53.3 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 20.19.11 + fsevents: 2.3.3 + jiti: 2.6.1 lightningcss: 1.30.1 terser: 5.16.9 tsx: 4.20.4 yaml: 2.8.1 - vitest@3.2.4(@edge-runtime/vm@3.2.0)(@types/node@20.19.11)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1): + vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.53.3 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 24.10.1 + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.30.1 + terser: 5.16.9 + tsx: 4.20.4 + yaml: 2.8.1 + + vitest@3.2.4(@edge-runtime/vm@3.2.0)(@types/node@20.19.11)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.0.2(@types/node@20.19.11)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1)) + '@vitest/mocker': 3.2.4(vite@7.0.2(@types/node@20.19.11)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -11165,8 +12445,8 @@ snapshots: tinyglobby: 0.2.14 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.0.2(@types/node@20.19.11)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) - vite-node: 3.2.4(@types/node@20.19.11)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) + vite: 7.0.2(@types/node@20.19.11)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) + vite-node: 3.2.4(@types/node@20.19.11)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: '@edge-runtime/vm': 3.2.0 @@ -11187,11 +12467,11 @@ snapshots: - tsx - yaml - vitest@3.2.4(@edge-runtime/vm@3.2.0)(@types/node@24.0.14)(@vitest/ui@3.2.4)(jiti@2.5.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1): + vitest@3.2.4(@edge-runtime/vm@3.2.0)(@types/node@24.10.1)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.0.2(@types/node@24.0.14)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1)) + '@vitest/mocker': 3.2.4(vite@7.0.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -11209,12 +12489,12 @@ snapshots: tinyglobby: 0.2.14 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.0.2(@types/node@24.0.14)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) - vite-node: 3.2.4(@types/node@24.0.14)(jiti@2.5.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) + vite: 7.0.2(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) + vite-node: 3.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: '@edge-runtime/vm': 3.2.0 - '@types/node': 24.0.14 + '@types/node': 24.10.1 '@vitest/ui': 3.2.4(vitest@3.2.4) jsdom: 27.0.0(postcss@8.5.6) transitivePeerDependencies: @@ -11231,6 +12511,45 @@ snapshots: - tsx - yaml + vitest@4.0.15(@edge-runtime/vm@3.2.0)(@types/node@24.10.1)(jiti@2.6.1)(jsdom@27.0.0(postcss@8.5.6))(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1): + dependencies: + '@vitest/expect': 4.0.15 + '@vitest/mocker': 4.0.15(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1)) + '@vitest/pretty-format': 4.0.15 + '@vitest/runner': 4.0.15 + '@vitest/snapshot': 4.0.15 + '@vitest/spy': 4.0.15 + '@vitest/utils': 4.0.15 + es-module-lexer: 1.7.0 + expect-type: 1.2.2 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.16.9)(tsx@4.20.4)(yaml@2.8.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@edge-runtime/vm': 3.2.0 + '@types/node': 24.10.1 + jsdom: 27.0.0(postcss@8.5.6) + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml + w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0 @@ -11429,9 +12748,11 @@ snapshots: '@poppinss/colors': 4.1.5 '@poppinss/dumper': 0.6.4 '@speed-highlight/core': 1.2.7 - cookie: 1.0.2 + cookie: 1.1.1 youch-core: 0.3.3 zod@3.22.3: {} - zod@3.25.75: {} + zod@3.25.76: {} + + zod@4.1.13: {} From 209691d0dcc6e6ca09f51dd0b0566e18f638f4de Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Wed, 3 Dec 2025 01:51:45 +0530 Subject: [PATCH 02/22] chore: test --- .github/workflows/tests.yml | 6 +++ create-db/__tests__/create.test.ts | 67 ++++++++++++++++++++++++------ create-db/src/index.ts | 4 +- 3 files changed, 61 insertions(+), 16 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b3d334c..d53b221 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -41,8 +41,14 @@ jobs: run: pnpm install --frozen-lockfile working-directory: ./create-db + - name: Build create-db + run: pnpm build + working-directory: ./create-db + - name: Run create-db tests run: pnpm test working-directory: ./create-db env: NODE_ENV: test + CREATE_DB_WORKER_URL: ${{ secrets.CREATE_DB_WORKER_URL }} + CLAIM_DB_WORKER_URL: ${{ secrets.CLAIM_DB_WORKER_URL }} diff --git a/create-db/__tests__/create.test.ts b/create-db/__tests__/create.test.ts index 29dee72..3aa3a3e 100644 --- a/create-db/__tests__/create.test.ts +++ b/create-db/__tests__/create.test.ts @@ -10,30 +10,71 @@ const runCli = async ( args: string[] = [], options: { env?: Record;[key: string]: unknown } = {} ) => { - return execa("node", [CLI_PATH, ...args], { + const result = await execa("node", [CLI_PATH, ...args], { ...options, env: { ...process.env, ...options.env }, reject: false, - timeout: 15000, + timeout: 20000, }); + // Combine stdout and stderr since clack outputs to stderr + return { + ...result, + all: result.stdout + result.stderr, + }; }; describe("CLI database creation", () => { it("creates database with default command", async () => { - const { stdout } = await runCli([]); - expect(stdout).toContain("Database created successfully!"); - }, 20000); + const result = await runCli([]); + if (result.exitCode !== 0) { + console.error("CLI failed with exit code:", result.exitCode); + console.error("stdout:", result.stdout); + console.error("stderr:", result.stderr); + } + expect(result.exitCode).toBe(0); + // clack outputs to stderr, so check all output + const allOutput = result.all || result.stdout + result.stderr; + if (!allOutput.includes("Database created successfully!")) { + console.error("All output:", allOutput); + } + expect(allOutput).toContain("Database created successfully!"); + }, 30000); it("creates database with --json flag", async () => { - const { stdout } = await runCli(["--json"]); - const result = JSON.parse(stdout); - expect(result).toHaveProperty("success"); - expect(result).toHaveProperty("connectionString"); - expect(result).toHaveProperty("claimUrl"); - }, 20000); + const result = await runCli(["--json"]); + if (result.exitCode !== 0) { + console.error("CLI failed with exit code:", result.exitCode); + console.error("stdout:", result.stdout); + console.error("stderr:", result.stderr); + } + expect(result.exitCode).toBe(0); + // JSON should be in stdout + expect(result.stdout).toBeTruthy(); + const trimmed = result.stdout.trim(); + if (!trimmed.match(/^\s*\{/)) { + console.error("stdout doesn't start with JSON:", trimmed); + } + expect(trimmed).toMatch(/^\s*\{/); + const parsed = JSON.parse(trimmed); + expect(parsed).toHaveProperty("success"); + expect(parsed).toHaveProperty("connectionString"); + expect(parsed).toHaveProperty("claimUrl"); + expect(parsed.success).toBe(true); + }, 30000); it("lists regions with regions command", async () => { - const { stdout } = await runCli(["regions"]); - expect(stdout).toContain("Available Prisma Postgres regions"); + const result = await runCli(["regions"]); + if (result.exitCode !== 0) { + console.error("CLI failed with exit code:", result.exitCode); + console.error("stdout:", result.stdout); + console.error("stderr:", result.stderr); + } + expect(result.exitCode).toBe(0); + // clack outputs to stderr + const allOutput = result.all || result.stdout + result.stderr; + if (!allOutput.includes("Available Prisma Postgres regions")) { + console.error("All output:", allOutput); + } + expect(allOutput).toContain("Available Prisma Postgres regions"); }, 20000); }); diff --git a/create-db/src/index.ts b/create-db/src/index.ts index b080d7d..f2d94e2 100644 --- a/create-db/src/index.ts +++ b/create-db/src/index.ts @@ -531,7 +531,7 @@ const router = os.router({ regions: os .meta({ description: "List available Prisma Postgres regions" }) - .handler(async (): Promise => { + .handler(async (): Promise => { const regions = await getRegions(); log.message(""); @@ -541,8 +541,6 @@ const router = os.router({ log.message(` ${pc.green(r.id)} - ${r.name || r.id}`); } log.message(""); - - return regions; }), }); From 99989a18dbc20e6c4128c48ccf6c394ceefa20ea Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Wed, 3 Dec 2025 01:55:03 +0530 Subject: [PATCH 03/22] chore: use isCancel --- create-db/src/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/create-db/src/index.ts b/create-db/src/index.ts index f2d94e2..c73e4a8 100644 --- a/create-db/src/index.ts +++ b/create-db/src/index.ts @@ -1,4 +1,4 @@ -import { intro, outro, cancel, select, spinner, log } from "@clack/prompts"; +import { intro, outro, cancel, select, spinner, log, isCancel } from "@clack/prompts"; import { createRouterClient, os } from "@orpc/server"; import { randomUUID } from "crypto"; import dotenv from "dotenv"; @@ -413,7 +413,7 @@ const router = os.router({ regions.find((r) => r.id === region)?.id || regions[0]?.id, }); - if (selectedRegion === null) { + if (isCancel(selectedRegion)) { cancel(pc.red("Operation cancelled.")); await flushAnalytics(); process.exit(0); @@ -466,7 +466,7 @@ const router = os.router({ regions.find((r) => r.id === region)?.id || regions[0]?.id, }); - if (selectedRegion === null) { + if (isCancel(selectedRegion)) { cancel(pc.red("Operation cancelled.")); await flushAnalytics(); process.exit(0); From 1d96c16b24d10e45e3a117be2559366531dd6f5e Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Wed, 3 Dec 2025 19:29:07 +0530 Subject: [PATCH 04/22] feat: change behaviour of --env flag to write directly to .env --- create-db/src/index.ts | 51 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/create-db/src/index.ts b/create-db/src/index.ts index c73e4a8..45e0cdf 100644 --- a/create-db/src/index.ts +++ b/create-db/src/index.ts @@ -361,10 +361,11 @@ const router = os.router({ .describe("Output machine-readable JSON") .meta({ alias: "j" }), env: z - .boolean() + .string() .optional() - .default(false) - .describe("Output DATABASE_URL format for .env files") + .describe( + "Write DATABASE_URL and CLAIM_URL to the specified .env file" + ) .meta({ alias: "e" }), }) ) @@ -385,7 +386,7 @@ const router = os.router({ "has-region-flag": !!input.region, "has-interactive-flag": input.interactive, "has-json-flag": input.json, - "has-env-flag": input.env, + "has-env-flag": !!input.env, "has-user-agent-from-env": !!userAgent, "node-version": process.version, platform: process.platform, @@ -401,7 +402,11 @@ const router = os.router({ region = getRegionClosestToLocation(userLocation) ?? region; } - if (input.json || input.env) { + const envPath = input.env; + const envEnabled = + typeof envPath === "string" && envPath.trim().length > 0; + + if (input.json || envEnabled) { if (input.interactive) { await checkOnline(); const regions = await getRegions(); @@ -448,8 +453,40 @@ const router = os.router({ process.exit(1); } - console.log(`DATABASE_URL="${result.connectionString}"`); - console.error(`\n# Claim your database at: ${result.claimUrl}`); + try { + const targetEnvPath = envPath!; + const lines = [ + `DATABASE_URL="${result.connectionString ?? ""}"`, + `CLAIM_URL="${result.claimUrl}"`, + "", + ]; + + let prefix = ""; + if (fs.existsSync(targetEnvPath)) { + const existing = fs.readFileSync(targetEnvPath, "utf8"); + if (existing.length > 0 && !existing.endsWith("\n")) { + prefix = "\n"; + } + } + + fs.appendFileSync(targetEnvPath, prefix + lines.join("\n"), { + encoding: "utf8", + }); + + console.log( + pc.green(`Wrote DATABASE_URL and CLAIM_URL to ${targetEnvPath}`) + ); + } catch (err) { + console.error( + pc.red( + `Failed to write environment variables to ${envPath}: ${err instanceof Error ? err.message : String(err) + }` + ) + ); + process.exit(1); + } + + return; } await checkOnline(); From 550b435c487c6d1df13a2f70edb2a71024f7705c Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Wed, 3 Dec 2025 23:30:36 +0530 Subject: [PATCH 05/22] chore: improve jsdoc --- create-db/src/index.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/create-db/src/index.ts b/create-db/src/index.ts index 45e0cdf..d4ba97c 100644 --- a/create-db/src/index.ts +++ b/create-db/src/index.ts @@ -28,6 +28,7 @@ export type { RegionId, CreateDatabaseResult, DatabaseResult, + DatabaseError, ProgrammaticCreateOptions, } from "./types.js"; @@ -595,6 +596,11 @@ const caller = createRouterClient(router, { context: {} }); /** * Create a new Prisma Postgres database programmatically. * + * @param options - Options for creating the database + * @param options.region - The AWS region for the database (optional) + * @param options.userAgent - Custom user agent string (optional) + * @returns A promise that resolves to either a {@link DatabaseResult} or {@link DatabaseError} + * * @example * ```typescript * import { create } from "create-db"; @@ -604,6 +610,8 @@ const caller = createRouterClient(router, { context: {} }); * if (result.success) { * console.log(`Connection string: ${result.connectionString}`); * console.log(`Claim URL: ${result.claimUrl}`); + * } else { + * console.error(`Error: ${result.message}`); * } * ``` */ From 3d5dd13b919676acd29f50e14540923a2cd082d7 Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Thu, 4 Dec 2025 00:18:40 +0530 Subject: [PATCH 06/22] chore: test action --- .github/workflows/preview.yml | 148 ----------------- .github/workflows/publish.yml | 292 ++++++++++++++++++++++++++++++++++ .github/workflows/release.yml | 92 ----------- 3 files changed, 292 insertions(+), 240 deletions(-) delete mode 100644 .github/workflows/preview.yml create mode 100644 .github/workflows/publish.yml delete mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml deleted file mode 100644 index b2cf177..0000000 --- a/.github/workflows/preview.yml +++ /dev/null @@ -1,148 +0,0 @@ -# .github/workflows/preview.yml -name: Preview deploy all Workers and CLIs - -on: - pull_request: - branches: - - main - types: - - opened - - reopened - - synchronize - -env: - # each folder under the repo root that contains one of your CLIs - WORKSPACES: create-db create-pg create-postgres - CREATE_DB_WORKER_URL: ${{ secrets.CREATE_DB_WORKER_URL }} - CLAIM_DB_WORKER_URL: ${{ secrets.CLAIM_DB_WORKER_URL }} - POSTHOG_API_HOST: ${{ secrets.POSTHOG_API_HOST }} - POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }} - -jobs: - preview: - name: 🚧 Preview release (PR #${{ github.event.number }}) - runs-on: ubuntu-latest - - steps: - - name: šŸ›Žļø Checkout full history - uses: actions/checkout@v3 - with: - fetch-depth: 0 - persist-credentials: true - - - name: 🤐 Disable Husky - run: echo "HUSKY=0" >> $GITHUB_ENV - - - name: šŸ“¦ Setup pnpm - uses: pnpm/action-setup@v4 - with: - version: 8 - - - name: šŸ”§ Install dependencies - run: pnpm install - - - name: āŒ Disable pnpm git-checks - run: pnpm config set git-checks false - - - name: šŸ“„ Copy README to child CLIs - run: | - for pkg in create-pg create-postgres; do - cp create-db/README.md "$pkg/README.md" - done - - - name: šŸ”– Create unique preview tag - run: | - SAFE_REF=$(echo "${{ github.event.pull_request.head.ref }}" | tr '/' '-') - echo "PRE_TAG=pr${{ github.event.number }}-${SAFE_REF}-${{ github.run_id }}" >> $GITHUB_ENV - - # — CF Worker preview deploys commented out; we will use pre-built URLs - # - name: ā˜ļø Deploy create-db-worker (preview) - # uses: cloudflare/wrangler-action@v3 - # with: - # apiToken: ${{ secrets.CF_API_TOKEN }} - # accountId: ${{ secrets.CF_ACCOUNT_ID }} - # environment: ${{ env.PRE_TAG }} - # workingDirectory: create-db-worker - # - # - name: ā˜ļø Deploy claim-db-worker (preview) - # uses: cloudflare/wrangler-action@v3 - # with: - # apiToken: ${{ secrets.CF_API_TOKEN }} - # accountId: ${{ secrets.CF_ACCOUNT_ID }} - # environment: ${{ env.PRE_TAG }} - # workingDirectory: claim-db-worker - - - name: šŸ”‘ Configure npm auth - run: echo "//registry.npmjs.org/:_authToken=${{ secrets.CREATE_DB_TOKEN_NPM }}" > ~/.npmrc - - - name: šŸš€ Bump & publish CLI previews - env: - WORKSPACES: create-db create-pg create-postgres - POSTHOG_API_HOST: ${{ secrets.POSTHOG_API_HOST }} - POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }} - CREATE_DB_WORKER_URL: ${{ env.CREATE_DB_WORKER_URL }} - CLAIM_DB_WORKER_URL: ${{ env.CLAIM_DB_WORKER_URL }} - run: | - # Resolve URLs with fallback - CREATE_DB_WORKER_URL="${{ steps.deploy-db.outputs.deployment-url || secrets.CREATE_DB_WORKER_URL }}" - CLAIM_DB_WORKER_URL="${{ steps.deploy-claim.outputs.deployment-url || secrets.CLAIM_DB_WORKER_URL }}" - - # Persist for next steps - echo "CREATE_DB_WORKER_URL=$CREATE_DB_WORKER_URL" >> $GITHUB_ENV - echo "CLAIM_DB_WORKER_URL=$CLAIM_DB_WORKER_URL" >> $GITHUB_ENV - - - echo "Using CREATE_DB_WORKER_URL=$CREATE_DB_WORKER_URL" - echo "Using CLAIM_DB_WORKER_URL=$CLAIM_DB_WORKER_URL" - echo "Using POSTHOG_API_HOST=${POSTHOG_API_HOST}" - - for pkg in $WORKSPACES; do - cd "$pkg" - export CREATE_DB_WORKER_URL - export CLAIM_DB_WORKER_URL - export POSTHOG_API_HOST="${POSTHOG_API_HOST}" - export POSTHOG_API_KEY="${POSTHOG_API_KEY}" - - npm version prerelease \ - --preid "$PRE_TAG" \ - --no-git-tag-version - pnpm publish --access public --tag pr${{ github.event.number }} - cd - >/dev/null - done - - - name: šŸ’¬ Post preview-testing instructions - uses: actions/github-script@v6 - env: - PRE_TAG: ${{ env.PRE_TAG }} - CREATE_DB_WORKER_URL: ${{ steps.deploy-db.outputs.deployment-url }} - CLAIM_DB_WORKER_URL: ${{ steps.deploy-claim.outputs.deployment-url }} - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const tag = process.env.PRE_TAG; - const dbUrl = process.env.CREATE_DB_WORKER_URL; - const clUrl = process.env.CLAIM_DB_WORKER_URL; - - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.payload.pull_request.number, - body: ` - āœ… **Preview CLIs & Workers are live!** - - Test the CLIs locally under tag \`${tag}\`: - - \`\`\`bash - npx create-db@pr${{ github.event.number }} - npx create-pg@pr${{ github.event.number }} - npx create-postgres@$pr${{ github.event.number }} - \`\`\` - - **Worker URLs** - • Create-DB Worker: ${dbUrl} - • Claim-DB Worker: ${clUrl} - - > These will live as long as this PR exists under tag \`${tag}\`.` - }); - - name: 🧹 Cleanup npm auth - run: rm -f ~/.npmrc diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..78074ab --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,292 @@ +name: Publish CLIs + +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: + +concurrency: ${{ github.workflow }}-${{ github.ref }} + +env: + WORKSPACES: create-db create-pg create-postgres + POSTHOG_API_HOST: ${{ secrets.POSTHOG_API_HOST }} + POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }} + CREATE_DB_WORKER_URL: ${{ secrets.CREATE_DB_WORKER_URL }} + CLAIM_DB_WORKER_URL: ${{ secrets.CLAIM_DB_WORKER_URL }} + +permissions: + contents: read + +jobs: + build: + name: Build & Verify + runs-on: ubuntu-latest + steps: + - name: šŸ›Žļø Checkout Repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: 🤐 Disable Husky + run: echo "HUSKY=0" >> $GITHUB_ENV + + - name: šŸ“¦ Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 8 + + - name: šŸ”§ Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "lts/*" + registry-url: "https://registry.npmjs.org" + cache: "pnpm" + + - name: šŸ“¦ Update npm to support Trusted Publishers + run: npm install -g npm@11.5.1 + + - name: šŸ”§ Install dependencies + run: pnpm install + + - name: āŒ Disable pnpm git-checks + run: pnpm config set git-checks false + + - name: šŸ“„ Copy README to child CLIs + if: github.event_name == 'pull_request' + run: | + for pkg in create-pg create-postgres; do + cp create-db/README.md "$pkg/README.md" + done + + - name: šŸ”Ø Build packages + run: | + for pkg in ${{ env.WORKSPACES }}; do + if [ -f "$pkg/package.json" ]; then + cd "$pkg" + if grep -q '"build"' package.json; then + echo "Building $pkg..." + pnpm run build || echo "Build skipped for $pkg (no build script)" + fi + cd - >/dev/null + fi + done + + - name: āœ… Verify package integrity + run: | + for pkg in ${{ env.WORKSPACES }}; do + if [ -f "$pkg/package.json" ]; then + cd "$pkg" + echo "Verifying $pkg..." + pnpm pack --dry-run + cd - >/dev/null + fi + done + + release: + name: šŸš€ Release CLIs + needs: build + if: (github.event_name == 'push' && github.ref == 'refs/heads/main') || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + permissions: + contents: write # to create changeset PRs and version commits + pull-requests: write # to create changeset PRs + id-token: write # required for OIDC / Trusted Publishers + steps: + - name: šŸ›Žļø Checkout Repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + persist-credentials: true + + - name: 🤐 Disable Husky + run: echo "HUSKY=0" >> $GITHUB_ENV + + - name: šŸ“¦ Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 8 + + - name: šŸ”§ Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "lts/*" + registry-url: "https://registry.npmjs.org" + cache: "pnpm" + + - name: šŸ”§ Install dependencies + run: pnpm install + + - name: āŒ Disable pnpm git-checks + run: pnpm config set git-checks false + + - name: šŸš€ Publish Each CLI Package + env: + CREATE_DB_WORKER_URL: ${{ secrets.CREATE_DB_WORKER_URL }} + CLAIM_DB_WORKER_URL: ${{ secrets.CLAIM_DB_WORKER_URL }} + POSTHOG_API_HOST: ${{ secrets.POSTHOG_API_HOST }} + POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }} + run: | + echo "Using CREATE_DB_WORKER_URL=${CREATE_DB_WORKER_URL}" + echo "Using CLAIM_DB_WORKER_URL=${CLAIM_DB_WORKER_URL}" + echo "Using POSTHOG_API_HOST=${POSTHOG_API_HOST}" + + for pkg in ${{ env.WORKSPACES }}; do + echo "Publishing $pkg to npm..." + cd "$pkg" + export POSTHOG_API_HOST="${POSTHOG_API_HOST}" + export POSTHOG_API_KEY="${POSTHOG_API_KEY}" + export CREATE_DB_WORKER_URL="${CREATE_DB_WORKER_URL}" + export CLAIM_DB_WORKER_URL="${CLAIM_DB_WORKER_URL}" + + # First try to publish + if ! pnpm publish --access public --no-git-checks; then + echo "Publish failed, trying to bump version and retry..." + npm version patch --no-git-tag-version + pnpm publish --access public --no-git-checks || echo "Publish failed again for $pkg" + fi + cd - >/dev/null + done + + - name: šŸ“ Ensure Changeset Exists + run: | + if [ -z "$(ls -A .changeset 2>/dev/null)" ]; then + echo "No changeset found. Creating a default one..." + pnpm changeset add --empty --message "chore(release): auto-generated changeset" + fi + + - name: šŸ“ Prepare Changesets PR + id: changesets + uses: changesets/action@v1 + with: + version: pnpm changeset version + commit: "chore(release): version packages" + title: "chore(release): version packages" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + experimental-release: + name: 🚧 Preview Release + needs: build + if: github.event_name == 'pull_request' + runs-on: ubuntu-latest + permissions: + contents: write # For publishing to GitHub releases + pull-requests: write # To comment on PRs + id-token: write # required for OIDC / Trusted Publishers + steps: + - name: šŸ›Žļø Checkout full history + uses: actions/checkout@v4 + with: + fetch-depth: 0 + persist-credentials: true + + - name: 🤐 Disable Husky + run: echo "HUSKY=0" >> $GITHUB_ENV + + - name: šŸ“¦ Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 8 + + - name: šŸ”§ Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "lts/*" + registry-url: "https://registry.npmjs.org" + cache: "pnpm" + + - name: šŸ”§ Install dependencies + run: pnpm install + + - name: āŒ Disable pnpm git-checks + run: pnpm config set git-checks false + + - name: šŸ“„ Copy README to child CLIs + run: | + for pkg in create-pg create-postgres; do + cp create-db/README.md "$pkg/README.md" + done + + - name: šŸ”– Create unique preview tag + run: | + SAFE_REF=$(echo "${{ github.event.pull_request.head.ref }}" | tr '/' '-') + echo "PRE_TAG=pr${{ github.event.number }}-${SAFE_REF}-${{ github.run_id }}" >> $GITHUB_ENV + + - name: šŸ”Ø Build packages + run: | + for pkg in ${{ env.WORKSPACES }}; do + if [ -f "$pkg/package.json" ]; then + cd "$pkg" + if grep -q '"build"' package.json; then + echo "Building $pkg..." + pnpm run build || echo "Build skipped for $pkg (no build script)" + fi + cd - >/dev/null + fi + done + + - name: šŸš€ Bump & publish CLI previews + env: + PRE_TAG: ${{ env.PRE_TAG }} + CREATE_DB_WORKER_URL: ${{ secrets.CREATE_DB_WORKER_URL }} + CLAIM_DB_WORKER_URL: ${{ secrets.CLAIM_DB_WORKER_URL }} + POSTHOG_API_HOST: ${{ secrets.POSTHOG_API_HOST }} + POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }} + run: | + echo "Using CREATE_DB_WORKER_URL=${CREATE_DB_WORKER_URL}" + echo "Using CLAIM_DB_WORKER_URL=${CLAIM_DB_WORKER_URL}" + echo "Using POSTHOG_API_HOST=${POSTHOG_API_HOST}" + + for pkg in ${{ env.WORKSPACES }}; do + cd "$pkg" + export CREATE_DB_WORKER_URL + export CLAIM_DB_WORKER_URL + export POSTHOG_API_HOST="${POSTHOG_API_HOST}" + export POSTHOG_API_KEY="${POSTHOG_API_KEY}" + + npm version prerelease \ + --preid "${PRE_TAG}" \ + --no-git-tag-version + + pnpm publish --access public --tag pr${{ github.event.number }} --no-git-checks + cd - >/dev/null + done + + - name: šŸ’¬ Post preview-testing instructions + uses: actions/github-script@v6 + env: + PRE_TAG: ${{ env.PRE_TAG }} + CREATE_DB_WORKER_URL: ${{ secrets.CREATE_DB_WORKER_URL }} + CLAIM_DB_WORKER_URL: ${{ secrets.CLAIM_DB_WORKER_URL }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const tag = process.env.PRE_TAG; + const dbUrl = process.env.CREATE_DB_WORKER_URL; + const clUrl = process.env.CLAIM_DB_WORKER_URL; + + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.pull_request.number, + body: ` + āœ… **Preview CLIs & Workers are live!** + + Test the CLIs locally under tag \`${tag}\`: + + \`\`\`bash + npx create-db@pr${{ github.event.number }} + npx create-pg@pr${{ github.event.number }} + npx create-postgres@pr${{ github.event.number }} + \`\`\` + + **Worker URLs** + • Create-DB Worker: ${dbUrl} + • Claim-DB Worker: ${clUrl} + + > These will live as long as this PR exists under tag \`${tag}\`.` + }); diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 3e533c5..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,92 +0,0 @@ -name: Release CLIs - -on: - workflow_dispatch: - push: - branches: - - main - -concurrency: ${{ github.workflow }}-${{ github.ref }} - -env: - WORKSPACES: create-db create-pg create-postgres - POSTHOG_API_HOST: ${{ secrets.POSTHOG_API_HOST }} - POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }} - -jobs: - release: - name: šŸš€ Release CLIs - runs-on: ubuntu-latest - - steps: - - name: šŸ›Žļø Checkout Repo - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: 🤐 Disable Husky - run: echo "HUSKY=0" >> $GITHUB_ENV - - - name: šŸ“¦ Setup pnpm - uses: pnpm/action-setup@v4 - with: - version: 8 - - - name: šŸ”§ Install dependencies - run: pnpm install - - - name: āŒ Disable pnpm git-checks - run: pnpm config set git-checks false - - - name: šŸ”‘ Configure npm auth - run: echo "//registry.npmjs.org/:_authToken=${{ secrets.CREATE_DB_TOKEN_NPM }}" > ~/.npmrc - - # Publish each CLI package using the version in package.json - - name: šŸš€ Publish Each CLI Package - run: | - echo "Using CREATE_DB_WORKER_URL=${CREATE_DB_WORKER_URL}" - echo "Using CLAIM_DB_WORKER_URL=${CLAIM_DB_WORKER_URL}" - echo "Using POSTHOG_API_HOST=${POSTHOG_API_HOST}" - - for pkg in $WORKSPACES; do - echo "Publishing $pkg to npm..." - cd "$pkg" - export POSTHOG_API_HOST="${{ secrets.POSTHOG_API_HOST }}" - export POSTHOG_API_KEY="${{ secrets.POSTHOG_API_KEY }}" - # pnpm publish --access public || echo "Publish failed for $pkg" - # First try to publish - if ! pnpm publish --access public; then - echo "Publish failed, trying to bump version and retry..." - npm version patch --no-git-tag-version - pnpm publish --access public || echo "Publish failed again for $pkg" - fi - cd - >/dev/null - done - env: - NODE_AUTH_TOKEN: ${{ secrets.CREATE_DB_TOKEN_NPM }} - CREATE_DB_WORKER_URL: ${{ secrets.CREATE_DB_WORKER_URL }} - CLAIM_DB_WORKER_URL: ${{ secrets.CLAIM_DB_WORKER_URL }} - POSTHOG_API_HOST: ${{ secrets.POSTHOG_API_HOST }} - POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }} - - - name: 🧹 Cleanup npm auth - run: rm -f ~/.npmrc - - # Create a default changeset if none exist - - name: šŸ“ Ensure Changeset Exists - run: | - if [ -z "$(ls -A .changeset 2>/dev/null)" ]; then - echo "No changeset found. Creating a default one..." - pnpm changeset add --empty --message "chore(release): auto-generated changeset" - fi - - # Finally, create a PR for version bump + changelogs - - name: šŸ“ Prepare Changesets PR - id: changesets - uses: changesets/action@v1 - with: - version: pnpm changeset version - commit: "chore(release): version packages" - title: "chore(release): version packages" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 3a9d744476cd37b7d3250c3a603a960d64c4ed49 Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Thu, 4 Dec 2025 00:24:42 +0530 Subject: [PATCH 07/22] chore: test --- .github/workflows/publish.yml | 102 ++++++---------------------------- 1 file changed, 17 insertions(+), 85 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 78074ab..ed3ce9d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -7,6 +7,10 @@ on: pull_request: branches: - main + types: + - opened + - reopened + - synchronize workflow_dispatch: concurrency: ${{ github.workflow }}-${{ github.ref }} @@ -22,73 +26,8 @@ permissions: contents: read jobs: - build: - name: Build & Verify - runs-on: ubuntu-latest - steps: - - name: šŸ›Žļø Checkout Repo - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: 🤐 Disable Husky - run: echo "HUSKY=0" >> $GITHUB_ENV - - - name: šŸ“¦ Setup pnpm - uses: pnpm/action-setup@v4 - with: - version: 8 - - - name: šŸ”§ Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: "lts/*" - registry-url: "https://registry.npmjs.org" - cache: "pnpm" - - - name: šŸ“¦ Update npm to support Trusted Publishers - run: npm install -g npm@11.5.1 - - - name: šŸ”§ Install dependencies - run: pnpm install - - - name: āŒ Disable pnpm git-checks - run: pnpm config set git-checks false - - - name: šŸ“„ Copy README to child CLIs - if: github.event_name == 'pull_request' - run: | - for pkg in create-pg create-postgres; do - cp create-db/README.md "$pkg/README.md" - done - - - name: šŸ”Ø Build packages - run: | - for pkg in ${{ env.WORKSPACES }}; do - if [ -f "$pkg/package.json" ]; then - cd "$pkg" - if grep -q '"build"' package.json; then - echo "Building $pkg..." - pnpm run build || echo "Build skipped for $pkg (no build script)" - fi - cd - >/dev/null - fi - done - - - name: āœ… Verify package integrity - run: | - for pkg in ${{ env.WORKSPACES }}; do - if [ -f "$pkg/package.json" ]; then - cd "$pkg" - echo "Verifying $pkg..." - pnpm pack --dry-run - cd - >/dev/null - fi - done - release: name: šŸš€ Release CLIs - needs: build if: (github.event_name == 'push' && github.ref == 'refs/heads/main') || github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest permissions: @@ -168,9 +107,8 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - experimental-release: - name: 🚧 Preview Release - needs: build + preview: + name: 🚧 Preview release (PR #${{ github.event.number }}) if: github.event_name == 'pull_request' runs-on: ubuntu-latest permissions: @@ -216,19 +154,6 @@ jobs: SAFE_REF=$(echo "${{ github.event.pull_request.head.ref }}" | tr '/' '-') echo "PRE_TAG=pr${{ github.event.number }}-${SAFE_REF}-${{ github.run_id }}" >> $GITHUB_ENV - - name: šŸ”Ø Build packages - run: | - for pkg in ${{ env.WORKSPACES }}; do - if [ -f "$pkg/package.json" ]; then - cd "$pkg" - if grep -q '"build"' package.json; then - echo "Building $pkg..." - pnpm run build || echo "Build skipped for $pkg (no build script)" - fi - cd - >/dev/null - fi - done - - name: šŸš€ Bump & publish CLI previews env: PRE_TAG: ${{ env.PRE_TAG }} @@ -237,8 +162,16 @@ jobs: POSTHOG_API_HOST: ${{ secrets.POSTHOG_API_HOST }} POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }} run: | - echo "Using CREATE_DB_WORKER_URL=${CREATE_DB_WORKER_URL}" - echo "Using CLAIM_DB_WORKER_URL=${CLAIM_DB_WORKER_URL}" + # Resolve URLs with fallback + CREATE_DB_WORKER_URL="${{ secrets.CREATE_DB_WORKER_URL }}" + CLAIM_DB_WORKER_URL="${{ secrets.CLAIM_DB_WORKER_URL }}" + + # Persist for next steps + echo "CREATE_DB_WORKER_URL=$CREATE_DB_WORKER_URL" >> $GITHUB_ENV + echo "CLAIM_DB_WORKER_URL=$CLAIM_DB_WORKER_URL" >> $GITHUB_ENV + + echo "Using CREATE_DB_WORKER_URL=$CREATE_DB_WORKER_URL" + echo "Using CLAIM_DB_WORKER_URL=$CLAIM_DB_WORKER_URL" echo "Using POSTHOG_API_HOST=${POSTHOG_API_HOST}" for pkg in ${{ env.WORKSPACES }}; do @@ -249,9 +182,8 @@ jobs: export POSTHOG_API_KEY="${POSTHOG_API_KEY}" npm version prerelease \ - --preid "${PRE_TAG}" \ + --preid "$PRE_TAG" \ --no-git-tag-version - pnpm publish --access public --tag pr${{ github.event.number }} --no-git-checks cd - >/dev/null done From 3dbbffdf48b40fe0e95b287941f90207f8d1b572 Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Thu, 4 Dec 2025 00:31:07 +0530 Subject: [PATCH 08/22] chore: test --- .github/workflows/{publish.yml => release.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{publish.yml => release.yml} (100%) diff --git a/.github/workflows/publish.yml b/.github/workflows/release.yml similarity index 100% rename from .github/workflows/publish.yml rename to .github/workflows/release.yml From d0c942736b9f16e3a0cdfd02470b8fdb9db8cb75 Mon Sep 17 00:00:00 2001 From: Aidan McAlister Date: Fri, 5 Dec 2025 07:23:00 -0500 Subject: [PATCH 09/22] chore: bump claim-worker to react `19.1.2` --- claim-db-worker/package.json | 4 +- pnpm-lock.yaml | 76 ++++++++++++++++++------------------ 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/claim-db-worker/package.json b/claim-db-worker/package.json index 321d57b..1e37988 100644 --- a/claim-db-worker/package.json +++ b/claim-db-worker/package.json @@ -27,8 +27,8 @@ "pg": "^8.16.3", "posthog-js": "^1.282.0", "prisma": "^6.14.0", - "react": "19.1.0", - "react-dom": "19.1.0", + "react": "19.1.2", + "react-dom": "19.1.2", "react-hot-toast": "^2.6.0" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 47cc60d..808c2d6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -31,22 +31,22 @@ importers: dependencies: '@monaco-editor/react': specifier: ^4.7.0 - version: 4.7.0(monaco-editor@0.52.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 4.7.0(monaco-editor@0.52.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) '@opennextjs/cloudflare': specifier: ^1.6.4 version: 1.6.4(wrangler@4.30.0) '@prisma/studio-core': specifier: ^0.5.2 - version: 0.5.2(@prisma/client@6.14.0(prisma@6.14.0(typescript@5.8.3))(typescript@5.8.3))(@types/react@19.1.10)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 0.5.2(@prisma/client@6.14.0(prisma@6.14.0(typescript@5.8.3))(typescript@5.8.3))(@types/react@19.1.10)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) '@types/pg': specifier: ^8.15.5 version: 8.15.5 lucide-react: specifier: ^0.541.0 - version: 0.541.0(react@19.1.0) + version: 0.541.0(react@19.1.2) next: specifier: 15.4.6 - version: 15.4.6(@babel/core@7.28.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 15.4.6(@babel/core@7.28.4)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) pg: specifier: ^8.16.3 version: 8.16.3 @@ -57,14 +57,14 @@ importers: specifier: ^6.14.0 version: 6.14.0(typescript@5.8.3) react: - specifier: 19.1.0 - version: 19.1.0 + specifier: 19.1.2 + version: 19.1.2 react-dom: - specifier: 19.1.0 - version: 19.1.0(react@19.1.0) + specifier: 19.1.2 + version: 19.1.2(react@19.1.2) react-hot-toast: specifier: ^2.6.0 - version: 2.6.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 2.6.0(react-dom@19.1.2(react@19.1.2))(react@19.1.2) devDependencies: '@tailwindcss/postcss': specifier: ^4 @@ -77,7 +77,7 @@ importers: version: 6.8.0 '@testing-library/react': specifier: ^16.3.0 - version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + version: 16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) '@types/node': specifier: ^20.19.10 version: 20.19.11 @@ -5021,10 +5021,10 @@ packages: rc9@2.1.2: resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} - react-dom@19.1.0: - resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==} + react-dom@19.1.2: + resolution: {integrity: sha512-dEoydsCp50i7kS1xHOmPXq4zQYoGWedUsvqv9H6zdif2r7yLHygyfP9qou71TulRN0d6ng9EbRVsQhSqfUc19g==} peerDependencies: - react: ^19.1.0 + react: ^19.1.2 react-hot-toast@2.6.0: resolution: {integrity: sha512-bH+2EBMZ4sdyou/DPrfgIouFpcRLCJ+HoCA32UoAYHn6T3Ur5yfcDCeSr5mwldl6pFOsiocmrXMuoCJ1vV8bWg==} @@ -5040,8 +5040,8 @@ packages: resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} engines: {node: '>=0.10.0'} - react@19.1.0: - resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} + react@19.1.2: + resolution: {integrity: sha512-MdWVitvLbQULD+4DP8GYjZUrepGW7d+GQkNVqJEzNxE+e9WIa4egVFE/RDfVb1u9u/Jw7dNMmPB4IqxzbFYJ0w==} engines: {node: '>=0.10.0'} read-yaml-file@1.1.0: @@ -8121,12 +8121,12 @@ snapshots: dependencies: state-local: 1.0.7 - '@monaco-editor/react@4.7.0(monaco-editor@0.52.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@monaco-editor/react@4.7.0(monaco-editor@0.52.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)': dependencies: '@monaco-editor/loader': 1.5.0 monaco-editor: 0.52.2 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) '@napi-rs/wasm-runtime@1.1.0': dependencies: @@ -8402,12 +8402,12 @@ snapshots: dependencies: '@prisma/debug': 6.14.0 - '@prisma/studio-core@0.5.2(@prisma/client@6.14.0(prisma@6.14.0(typescript@5.8.3))(typescript@5.8.3))(@types/react@19.1.10)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@prisma/studio-core@0.5.2(@prisma/client@6.14.0(prisma@6.14.0(typescript@5.8.3))(typescript@5.8.3))(@types/react@19.1.10)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)': dependencies: '@prisma/client': 6.14.0(prisma@6.14.0(typescript@5.8.3))(typescript@5.8.3) '@types/react': 19.1.10 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) '@quansync/fs@0.1.5': dependencies: @@ -9269,12 +9269,12 @@ snapshots: picocolors: 1.1.1 redent: 3.0.0 - '@testing-library/react@16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + '@testing-library/react@16.3.0(@testing-library/dom@10.4.1)(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)': dependencies: '@babel/runtime': 7.27.6 '@testing-library/dom': 10.4.1 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) optionalDependencies: '@types/react': 19.1.10 '@types/react-dom': 19.1.7(@types/react@19.1.10) @@ -11059,9 +11059,9 @@ snapshots: dependencies: yallist: 4.0.0 - lucide-react@0.541.0(react@19.1.0): + lucide-react@0.541.0(react@19.1.2): dependencies: - react: 19.1.0 + react: 19.1.2 lz-string@1.5.0: {} @@ -11219,15 +11219,15 @@ snapshots: negotiator@1.0.0: {} - next@15.4.6(@babel/core@7.28.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + next@15.4.6(@babel/core@7.28.4)(react-dom@19.1.2(react@19.1.2))(react@19.1.2): dependencies: '@next/env': 15.4.6 '@swc/helpers': 0.5.15 caniuse-lite: 1.0.30001735 postcss: 8.4.31 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - styled-jsx: 5.1.6(@babel/core@7.28.4)(react@19.1.0) + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) + styled-jsx: 5.1.6(@babel/core@7.28.4)(react@19.1.2) optionalDependencies: '@next/swc-darwin-arm64': 15.4.6 '@next/swc-darwin-x64': 15.4.6 @@ -11581,23 +11581,23 @@ snapshots: defu: 6.1.4 destr: 2.0.5 - react-dom@19.1.0(react@19.1.0): + react-dom@19.1.2(react@19.1.2): dependencies: - react: 19.1.0 + react: 19.1.2 scheduler: 0.26.0 - react-hot-toast@2.6.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + react-hot-toast@2.6.0(react-dom@19.1.2(react@19.1.2))(react@19.1.2): dependencies: csstype: 3.1.3 goober: 2.1.16(csstype@3.1.3) - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) react-is@17.0.2: {} react-refresh@0.17.0: {} - react@19.1.0: {} + react@19.1.2: {} read-yaml-file@1.1.0: dependencies: @@ -11988,10 +11988,10 @@ snapshots: strnum@2.1.1: {} - styled-jsx@5.1.6(@babel/core@7.28.4)(react@19.1.0): + styled-jsx@5.1.6(@babel/core@7.28.4)(react@19.1.2): dependencies: client-only: 0.0.1 - react: 19.1.0 + react: 19.1.2 optionalDependencies: '@babel/core': 7.28.4 From 07ed43ca05105a8fb5f9a2532fc5760a2ab2f611 Mon Sep 17 00:00:00 2001 From: Aidan McAlister Date: Mon, 8 Dec 2025 01:03:39 -0500 Subject: [PATCH 10/22] fix: console url fixed --- .../app/api/auth/callback/route.ts | 50 +++++++- claim-db-worker/app/success/page.tsx | 9 +- claim-db-worker/app/web/connect/page.tsx | 113 ++++++------------ claim-db-worker/lib/project-transfer.ts | 22 +++- claim-db-worker/lib/response-utils.ts | 6 +- claim-db-worker/package.json | 2 +- claim-db-worker/wrangler.jsonc | 2 +- schema-api-routes/src/routes/schema/pull.ts | 1 - 8 files changed, 118 insertions(+), 87 deletions(-) diff --git a/claim-db-worker/app/api/auth/callback/route.ts b/claim-db-worker/app/api/auth/callback/route.ts index 2c2b544..b8ac49f 100644 --- a/claim-db-worker/app/api/auth/callback/route.ts +++ b/claim-db-worker/app/api/auth/callback/route.ts @@ -114,9 +114,10 @@ export async function GET(request: NextRequest) { ); } - // Validate project exists + // Validate project exists and get project data + let projectData; try { - await validateProject(projectID); + projectData = await validateProject(projectID); } catch (error) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; @@ -141,16 +142,57 @@ export async function GET(request: NextRequest) { projectID, tokenData.access_token ); - if (transferResult.success) { + // Fetch project details with user's token to get workspace ID + const projectDetailsRes = await fetch( + `https://api.prisma.io/v1/projects/${projectID}`, + { + headers: { + Authorization: `Bearer ${tokenData.access_token}`, + "Content-Type": "application/json", + }, + } + ); + const projectDetails = (await projectDetailsRes.json()) as { + data?: { workspace?: { id?: string } }; + }; + const workspaceId = (projectDetails.data?.workspace?.id ?? "").replace( + /^wksp_/, + "" + ); + + // Fetch databases to get database ID + const databasesRes = await fetch( + `https://api.prisma.io/v1/projects/${projectID}/databases`, + { + headers: { + Authorization: `Bearer ${tokenData.access_token}`, + "Content-Type": "application/json", + }, + } + ); + const databases = (await databasesRes.json()) as { + data?: Array<{ id?: string }>; + }; + const databaseId = (databases.data?.[0]?.id ?? "").replace(/^db_/, ""); + await sendServerAnalyticsEvent( "create_db:claim_successful", { "project-id": projectID, + "workspace-id": workspaceId, + "database-id": databaseId, }, request ); - return redirectToSuccess(request, projectID); + + const cleanProjectId = projectID.replace(/^proj_/, ""); + return redirectToSuccess( + request, + cleanProjectId, + workspaceId, + databaseId + ); } else { await sendServerAnalyticsEvent( "create_db:claim_failed", diff --git a/claim-db-worker/app/success/page.tsx b/claim-db-worker/app/success/page.tsx index 092d096..57ce09c 100644 --- a/claim-db-worker/app/success/page.tsx +++ b/claim-db-worker/app/success/page.tsx @@ -8,6 +8,13 @@ import { Suspense } from "react"; function SuccessContent() { const searchParams = useSearchParams(); const projectID = searchParams.get("projectID"); + const workspaceId = searchParams.get("workspaceId"); + const databaseId = searchParams.get("databaseId"); + + const consoleUrl = + workspaceId && projectID && databaseId + ? `https://console.prisma.io/${workspaceId}/${projectID}/${databaseId}` + : "https://console.prisma.io/"; return (
@@ -23,7 +30,7 @@ function SuccessContent() {

= { - prisma: [ - { - title: "Install Prisma", - content: null, - code: "npm install prisma @prisma/client", - }, - { - title: "Initialize Prisma", - content: null, - code: "npx prisma init", - }, - { - title: "Set connection string in .env", - content: null, - code: 'DATABASE_URL=""', - }, - { - title: "Pull the database schema", - content: null, - code: "npx prisma db pull", - }, - { - title: "Generate Prisma Client", - content: null, - code: "npx prisma generate", - }, - { - title: "Start querying", - content: Import and use Prisma Client in your application, - code: `import { PrismaClient } from "@prisma/client"; +const buildSteps: BuildStep[] = [ + { + title: "Install Prisma", + content: null, + code: "npm install prisma @prisma/client", + }, + { + title: "Initialize Prisma", + content: null, + code: "npx prisma init", + }, + { + title: "Set connection string in .env", + content: null, + code: 'DATABASE_URL=""', + }, + { + title: "Pull the database schema", + content: null, + code: "npx prisma db pull", + }, + { + title: "Generate Prisma Client", + content: null, + code: "npx prisma generate", + }, + { + title: "Start querying", + content: Import and use Prisma Client in your application, + code: `import { PrismaClient } from "@prisma/client"; const prisma = new PrismaClient(); const users = await prisma.user.findMany(); console.log(users);`, - }, - ], - direct: [ - { - title: "Install node-postgres", - content: null, - code: "npm install pg", - }, - { - title: "Set connection string in .env", - content: null, - code: 'DATABASE_URL=""', - }, - { - title: "Set up connection", - content: null, - code: `import { Pool } from "pg"; - -const pool = new Pool({ - connectionString: process.env.DATABASE_URL -});`, - }, - { - title: "Query your database", - content: null, - code: `const { rows } = await pool.query('SELECT * FROM "User"'); - -console.log(rows);`, - }, - ], -}; + }, +]; const StepItem = ({ number, @@ -138,15 +106,10 @@ const StepItem = ({ export default function ConnectPage() { const { dbInfo } = useDatabase(); - const [connectionType, setConnectionType] = - useState("prisma"); const [copied, setCopied] = useState(false); const [showPassword, setShowPassword] = useState(false); - const connectionString = - connectionType === "prisma" - ? dbInfo.connectionString - : dbInfo.directConnectionString; + const connectionString = dbInfo.connectionString; const handleCopyConnectionString = async () => { try { @@ -162,7 +125,7 @@ export default function ConnectPage() { return (
{/* Connection type toggle */} -
+ {/*
{(["prisma", "direct"] as const).map((type) => (
+
*/} {/* Connection string input */}
@@ -229,12 +192,10 @@ export default function ConnectPage() {

- {connectionType === "prisma" - ? "Connect with Prisma ORM" - : "Connect with node-postgres"} + Connect with Prisma ORM

- {buildSteps[connectionType].map((step, index) => ( + {buildSteps.map((step, index) => ( { +): Promise<{ + success: boolean; + error?: string; + status?: number; + transferResponse: any; +}> { const env = getEnv(); const requestBody = JSON.stringify({ @@ -24,13 +29,26 @@ export async function transferProject( ); if (transferResponse.ok) { - return { success: true }; + const responseText = await transferResponse.text(); + let responseData = null; + + if (responseText) { + try { + responseData = JSON.parse(responseText); + } catch (e) { + console.log("Transfer response (not JSON):", responseText); + responseData = { rawResponse: responseText }; + } + } + + return { success: true, transferResponse: responseData }; } else { const responseText = await transferResponse.text(); return { success: false, error: responseText, status: transferResponse.status, + transferResponse: null, }; } } diff --git a/claim-db-worker/lib/response-utils.ts b/claim-db-worker/lib/response-utils.ts index 2afd953..1c62564 100644 --- a/claim-db-worker/lib/response-utils.ts +++ b/claim-db-worker/lib/response-utils.ts @@ -26,10 +26,14 @@ export function redirectToError( export function redirectToSuccess( request: NextRequest, - projectID: string + projectID: string, + workspaceId: string, + databaseId: string ): Response { const params = new URLSearchParams({ projectID: projectID, + workspaceId: workspaceId, + databaseId: databaseId, }); const baseUrl = getBaseUrl(request); diff --git a/claim-db-worker/package.json b/claim-db-worker/package.json index 1e37988..e7a42aa 100644 --- a/claim-db-worker/package.json +++ b/claim-db-worker/package.json @@ -26,7 +26,7 @@ "next": "15.4.6", "pg": "^8.16.3", "posthog-js": "^1.282.0", - "prisma": "^6.14.0", + "prisma": "^7.1.0", "react": "19.1.2", "react-dom": "19.1.2", "react-hot-toast": "^2.6.0" diff --git a/claim-db-worker/wrangler.jsonc b/claim-db-worker/wrangler.jsonc index e723ff7..285911f 100644 --- a/claim-db-worker/wrangler.jsonc +++ b/claim-db-worker/wrangler.jsonc @@ -2,7 +2,7 @@ "$schema": "node_modules/wrangler/config-schema.json", "name": "claim-db-worker", "main": ".open-next/worker.js", - "account_id": "16b32bbb36161aca01a6357a37bc453e", + "account_id": "0ef7f922ce028e16c1a44d98c86511b0", "compatibility_date": "2025-08-13", "compatibility_flags": ["nodejs_compat", "global_fetch_strictly_public"], "observability": { diff --git a/schema-api-routes/src/routes/schema/pull.ts b/schema-api-routes/src/routes/schema/pull.ts index c12848a..14e111d 100644 --- a/schema-api-routes/src/routes/schema/pull.ts +++ b/schema-api-routes/src/routes/schema/pull.ts @@ -13,7 +13,6 @@ app.post("/", async (c) => { datasource db { provider = "postgresql" - url = env("DATABASE_URL") }`; try { From 978104e93f50f2bda35df38a588a146c462c3706 Mon Sep 17 00:00:00 2001 From: Aidan McAlister Date: Mon, 8 Dec 2025 10:37:31 -0500 Subject: [PATCH 11/22] feat: updated to prisma 7 --- claim-db-worker/app/web/connect/page.tsx | 66 +++++++++++++++---- .../lib/prismaSchemaEditor/defaultSchema.ts | 1 - 2 files changed, 52 insertions(+), 15 deletions(-) diff --git a/claim-db-worker/app/web/connect/page.tsx b/claim-db-worker/app/web/connect/page.tsx index 7fdec19..4b30d3f 100644 --- a/claim-db-worker/app/web/connect/page.tsx +++ b/claim-db-worker/app/web/connect/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { Check, Copy, Eye, EyeClosed, Lightbulb } from "lucide-react"; +import { Check, Copy, Eye, EyeClosed, Lightbulb, Zap } from "lucide-react"; import React, { useState } from "react"; import { useDatabase } from "../DatabaseContext"; import { customToast } from "@/lib/custom-toast"; @@ -25,9 +25,10 @@ type BuildStep = { const buildSteps: BuildStep[] = [ { - title: "Install Prisma", + title: "Install Dependencies", content: null, - code: "npm install prisma @prisma/client", + code: `npm install prisma tsx @types/pg --save-dev +npm install @prisma/client @prisma/adapter-pg dotenv pg`, }, { title: "Initialize Prisma", @@ -52,11 +53,18 @@ const buildSteps: BuildStep[] = [ { title: "Start querying", content: Import and use Prisma Client in your application, - code: `import { PrismaClient } from "@prisma/client"; + code: `import { PrismaClient } from "./generated/prisma/client.js"; +import { PrismaPg } from "@prisma/adapter-pg"; -const prisma = new PrismaClient(); -const users = await prisma.user.findMany(); -console.log(users);`, +const adapter = new PrismaPg({ + connectionString: process.env.DATABASE_URL, +}); + +const prisma = new PrismaClient({ + adapter, +}); + +export default prisma;`, }, ]; @@ -106,14 +114,18 @@ const StepItem = ({ export default function ConnectPage() { const { dbInfo } = useDatabase(); - const [copied, setCopied] = useState(false); + const [copiedDirect, setCopiedDirect] = useState(false); + const [copiedAccel, setCopiedAccel] = useState(false); const [showPassword, setShowPassword] = useState(false); - const connectionString = dbInfo.connectionString; + const connectionString = dbInfo.directConnectionString; - const handleCopyConnectionString = async () => { + const handleCopyConnectionString = async ( + copyString: string, + setCopied: (copied: boolean) => void + ) => { try { - await navigator.clipboard.writeText(connectionString || ""); + await navigator.clipboard.writeText(copyString); setCopied(true); customToast("success", "Connection string copied to clipboard"); setTimeout(() => setCopied(false), 2000); @@ -172,20 +184,46 @@ export default function ConnectPage() { +
diff --git a/claim-db-worker/lib/prismaSchemaEditor/defaultSchema.ts b/claim-db-worker/lib/prismaSchemaEditor/defaultSchema.ts index 0661ce6..6884c9e 100644 --- a/claim-db-worker/lib/prismaSchemaEditor/defaultSchema.ts +++ b/claim-db-worker/lib/prismaSchemaEditor/defaultSchema.ts @@ -8,5 +8,4 @@ generator client { datasource db { provider = "postgresql" - url = env("DATABASE_URL") }`; From c9f991452f424a467211da33598f653d37129da7 Mon Sep 17 00:00:00 2001 From: Aidan McAlister Date: Mon, 8 Dec 2025 13:57:51 -0500 Subject: [PATCH 12/22] fix: console log removed --- create-db-worker/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create-db-worker/src/index.ts b/create-db-worker/src/index.ts index eb7c60b..9f14415 100644 --- a/create-db-worker/src/index.ts +++ b/create-db-worker/src/index.ts @@ -142,7 +142,7 @@ export default { if (!region || !name) { return new Response('Missing region or name in request body', { status: 400 }); } - console.log('userAgent:', userAgent); + const prismaResponse = await fetch('https://api.prisma.io/v1/projects', { method: 'POST', headers: { From 860814f59ec908afa94c569c4c00e09c811edd26 Mon Sep 17 00:00:00 2001 From: Aidan McAlister Date: Mon, 8 Dec 2025 15:33:07 -0500 Subject: [PATCH 13/22] feat: file broken down --- create-db/__tests__/geolocation.test.ts | 201 ++++++ create-db/src/analytics.ts | 39 ++ create-db/src/database.ts | 135 ++++ create-db/src/env-utils.ts | 23 + create-db/src/geolocation.ts | 173 +++++ create-db/src/index.ts | 856 +++++++++--------------- create-db/src/regions.ts | 46 ++ 7 files changed, 929 insertions(+), 544 deletions(-) create mode 100644 create-db/__tests__/geolocation.test.ts create mode 100644 create-db/src/analytics.ts create mode 100644 create-db/src/database.ts create mode 100644 create-db/src/env-utils.ts create mode 100644 create-db/src/geolocation.ts create mode 100644 create-db/src/regions.ts diff --git a/create-db/__tests__/geolocation.test.ts b/create-db/__tests__/geolocation.test.ts new file mode 100644 index 0000000..8c1c5eb --- /dev/null +++ b/create-db/__tests__/geolocation.test.ts @@ -0,0 +1,201 @@ +import { describe, it, expect } from "vitest"; +import { execa } from "execa"; +import { + calculateHaversineDistance, + getRegionClosestToLocation, + REGION_COORDINATES, +} from "../src/geolocation.js"; + +describe("calculateHaversineDistance", () => { + it("calculates distance between two points", () => { + // Distance from New York to Los Angeles (approximately 3944 km) + const distance = calculateHaversineDistance( + 40.7128, + -74.006, // New York + 34.0522, + -118.2437 // Los Angeles + ); + expect(distance).toBeGreaterThan(3900); + expect(distance).toBeLessThan(4000); + }); + + it("returns 0 for same location", () => { + const distance = calculateHaversineDistance( + 35.6762, + 139.6503, + 35.6762, + 139.6503 + ); + expect(distance).toBe(0); + }); +}); + +describe("getRegionClosestToLocation", () => { + it("selects ap-southeast-1 for Singapore coordinates", () => { + const region = getRegionClosestToLocation({ + latitude: 1.3521, + longitude: 103.8198, + }); + expect(region).toBe("ap-southeast-1"); + }); + + it("selects ap-northeast-1 for Tokyo coordinates", () => { + const region = getRegionClosestToLocation({ + latitude: 35.6762, + longitude: 139.6503, + }); + expect(region).toBe("ap-northeast-1"); + }); + + it("selects eu-central-1 for Frankfurt coordinates", () => { + const region = getRegionClosestToLocation({ + latitude: 50.1109, + longitude: 8.6821, + }); + expect(region).toBe("eu-central-1"); + }); + + it("selects eu-west-3 for Paris coordinates", () => { + const region = getRegionClosestToLocation({ + latitude: 48.8566, + longitude: 2.3522, + }); + expect(region).toBe("eu-west-3"); + }); + + it("selects us-east-1 for Virginia coordinates", () => { + const region = getRegionClosestToLocation({ + latitude: 38.9072, + longitude: -77.0369, + }); + expect(region).toBe("us-east-1"); + }); + + it("selects us-west-1 for California coordinates", () => { + const region = getRegionClosestToLocation({ + latitude: 37.7749, + longitude: -122.4194, + }); + expect(region).toBe("us-west-1"); + }); + + it("selects closest region for location between regions", () => { + // London coordinates - should be closest to eu-central-1 or eu-west-3 + const region = getRegionClosestToLocation({ + latitude: 51.5074, + longitude: -0.1278, + }); + expect(region).toMatch(/^eu-(central-1|west-3)$/); + }); + + it("handles string coordinates", () => { + const region = getRegionClosestToLocation({ + latitude: "35.6762", + longitude: "139.6503", + }); + expect(region).toBe("ap-northeast-1"); + }); + + it("returns null for invalid coordinates", () => { + expect(getRegionClosestToLocation(null)).toBe(null); + expect( + getRegionClosestToLocation({ latitude: undefined, longitude: undefined }) + ).toBe(null); + expect( + getRegionClosestToLocation({ latitude: NaN, longitude: NaN }) + ).toBe(null); + expect( + getRegionClosestToLocation({ latitude: "invalid", longitude: "invalid" }) + ).toBe(null); + }); +}); + +describe("REGION_COORDINATES", () => { + it("contains all expected regions", () => { + const expectedRegions = [ + "ap-southeast-1", + "ap-northeast-1", + "eu-central-1", + "eu-west-3", + "us-east-1", + "us-west-1", + ]; + + for (const region of expectedRegions) { + expect(REGION_COORDINATES).toHaveProperty(region); + expect(REGION_COORDINATES[region as keyof typeof REGION_COORDINATES]).toHaveProperty("lat"); + expect(REGION_COORDINATES[region as keyof typeof REGION_COORDINATES]).toHaveProperty("lng"); + } + }); + + it("has valid coordinate values", () => { + for (const [regionId, coords] of Object.entries(REGION_COORDINATES)) { + expect(coords.lat).toBeGreaterThanOrEqual(-90); + expect(coords.lat).toBeLessThanOrEqual(90); + expect(coords.lng).toBeGreaterThanOrEqual(-180); + expect(coords.lng).toBeLessThanOrEqual(180); + } + }); +}); + +describe("CLI database creation with explicit regions", () => { + it("creates database in ap-southeast-1 (Singapore)", async () => { + const { stdout } = await execa("node", ["./dist/cli.mjs", "create", "--region", "ap-southeast-1", "--json"]); + const result = JSON.parse(stdout); + + expect(result.success).toBe(true); + expect(result.region).toBe("ap-southeast-1"); + expect(result.connectionString).toBeTruthy(); + expect(result.claimUrl).toBeTruthy(); + }, 30000); + + it("creates database in ap-northeast-1 (Tokyo)", async () => { + const { stdout } = await execa("node", ["./dist/cli.mjs", "create", "--region", "ap-northeast-1", "--json"]); + const result = JSON.parse(stdout); + + expect(result.success).toBe(true); + expect(result.region).toBe("ap-northeast-1"); + expect(result.connectionString).toBeTruthy(); + expect(result.claimUrl).toBeTruthy(); + }, 30000); + + it("creates database in eu-central-1 (Frankfurt)", async () => { + const { stdout } = await execa("node", ["./dist/cli.mjs", "create", "--region", "eu-central-1", "--json"]); + const result = JSON.parse(stdout); + + expect(result.success).toBe(true); + expect(result.region).toBe("eu-central-1"); + expect(result.connectionString).toBeTruthy(); + expect(result.claimUrl).toBeTruthy(); + }, 30000); + + it("creates database in eu-west-3 (Paris)", async () => { + const { stdout } = await execa("node", ["./dist/cli.mjs", "create", "--region", "eu-west-3", "--json"]); + const result = JSON.parse(stdout); + + expect(result.success).toBe(true); + expect(result.region).toBe("eu-west-3"); + expect(result.connectionString).toBeTruthy(); + expect(result.claimUrl).toBeTruthy(); + }, 30000); + + it("creates database in us-east-1 (Virginia)", async () => { + const { stdout } = await execa("node", ["./dist/cli.mjs", "create", "--region", "us-east-1", "--json"]); + const result = JSON.parse(stdout); + + expect(result.success).toBe(true); + expect(result.region).toBe("us-east-1"); + expect(result.connectionString).toBeTruthy(); + expect(result.claimUrl).toBeTruthy(); + }, 30000); + + it("creates database in us-west-1 (California)", async () => { + const { stdout } = await execa("node", ["./dist/cli.mjs", "create", "--region", "us-west-1", "--json"]); + const result = JSON.parse(stdout); + + expect(result.success).toBe(true); + expect(result.region).toBe("us-west-1"); + expect(result.connectionString).toBeTruthy(); + expect(result.claimUrl).toBeTruthy(); + }, 30000); +}); diff --git a/create-db/src/analytics.ts b/create-db/src/analytics.ts new file mode 100644 index 0000000..587fbea --- /dev/null +++ b/create-db/src/analytics.ts @@ -0,0 +1,39 @@ +const pendingAnalytics: Promise[] = []; + +export async function sendAnalytics( + eventName: string, + properties: Record, + cliRunId: string, + workerUrl: string +): Promise { + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), 5000); + + const promise = (async () => { + try { + await fetch(`${workerUrl}/analytics`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + eventName, + properties: { distinct_id: cliRunId, ...properties }, + }), + signal: controller.signal, + }); + } catch { + // Analytics failures should not block CLI + } finally { + clearTimeout(timer); + } + })(); + + pendingAnalytics.push(promise); +} + +export async function flushAnalytics(maxWaitMs = 500): Promise { + if (pendingAnalytics.length === 0) return; + await Promise.race([ + Promise.all(pendingAnalytics), + new Promise((resolve) => setTimeout(resolve, maxWaitMs)), + ]); +} diff --git a/create-db/src/database.ts b/create-db/src/database.ts new file mode 100644 index 0000000..0f6d0cd --- /dev/null +++ b/create-db/src/database.ts @@ -0,0 +1,135 @@ +import { randomUUID } from "crypto"; +import type { CreateDatabaseResult, ApiResponse } from "./types.js"; +import { sendAnalytics } from "./analytics.js"; + +export function getCommandName(): string { + const executable = process.argv[1] || "create-db"; + if (executable.includes("create-pg")) return "create-pg"; + if (executable.includes("create-postgres")) return "create-postgres"; + return "create-db"; +} + +export async function createDatabaseCore( + region: string, + createDbWorkerUrl: string, + claimDbWorkerUrl: string, + userAgent?: string, + cliRunId?: string +): Promise { + const name = new Date().toISOString(); + const runId = cliRunId ?? randomUUID(); + + const resp = await fetch(`${createDbWorkerUrl}/create`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + region, + name, + utm_source: getCommandName(), + userAgent, + }), + }); + + if (resp.status === 429) { + void sendAnalytics( + "create_db:database_creation_failed", + { region, "error-type": "rate_limit", "status-code": 429 }, + runId, + createDbWorkerUrl + ); + return { + success: false, + error: "rate_limit_exceeded", + message: + "We're experiencing a high volume of requests. Please try again later.", + status: 429, + }; + } + + let result: ApiResponse; + let raw = ""; + try { + raw = await resp.text(); + result = JSON.parse(raw) as ApiResponse; + } catch { + void sendAnalytics( + "create_db:database_creation_failed", + { region, "error-type": "invalid_json", "status-code": resp.status }, + runId, + createDbWorkerUrl + ); + return { + success: false, + error: "invalid_json", + message: "Unexpected response from create service.", + raw, + status: resp.status, + }; + } + + if (result.error) { + void sendAnalytics( + "create_db:database_creation_failed", + { + region, + "error-type": "api_error", + "error-message": result.error.message, + }, + runId, + createDbWorkerUrl + ); + return { + success: false, + error: "api_error", + message: result.error.message || "Unknown error", + details: result.error, + status: result.error.status ?? resp.status, + }; + } + + const database = result.data?.database ?? result.databases?.[0]; + const projectId = result.data?.id ?? result.id ?? ""; + + const apiKeys = database?.apiKeys; + const directConnDetails = result.data + ? apiKeys?.[0]?.directConnection + : result.databases?.[0]?.apiKeys?.[0]?.ppgDirectConnection; + + const directUser = directConnDetails?.user + ? encodeURIComponent(String(directConnDetails.user)) + : ""; + const directPass = directConnDetails?.pass + ? encodeURIComponent(String(directConnDetails.pass)) + : ""; + const directHost = directConnDetails?.host; + const directPort = directConnDetails?.port + ? `:${directConnDetails.port}` + : ""; + const directDbName = directConnDetails?.database || "postgres"; + + const connectionString = + directConnDetails && directHost + ? `postgresql://${directUser}:${directPass}@${directHost}${directPort}/${directDbName}?sslmode=require` + : null; + + const claimUrl = `${claimDbWorkerUrl}/claim?projectID=${projectId}&utm_source=${userAgent || getCommandName()}&utm_medium=cli`; + const expiryDate = new Date(Date.now() + 24 * 60 * 60 * 1000); + + void sendAnalytics( + "create_db:database_created", + { region, utm_source: getCommandName() }, + runId, + createDbWorkerUrl + ); + + return { + success: true, + connectionString, + claimUrl, + deletionDate: expiryDate.toISOString(), + region: database?.region?.id || region, + name: database?.name ?? name, + projectId, + userAgent, + }; +} diff --git a/create-db/src/env-utils.ts b/create-db/src/env-utils.ts new file mode 100644 index 0000000..91f449b --- /dev/null +++ b/create-db/src/env-utils.ts @@ -0,0 +1,23 @@ +import fs from "fs"; +import path from "path"; + +export function readUserEnvFile(): Record { + const envPath = path.join(process.cwd(), ".env"); + if (!fs.existsSync(envPath)) return {}; + + const envContent = fs.readFileSync(envPath, "utf8"); + const envVars: Record = {}; + + for (const line of envContent.split("\n")) { + const trimmed = line.trim(); + if (trimmed && !trimmed.startsWith("#")) { + const [key, ...valueParts] = trimmed.split("="); + if (key && valueParts.length > 0) { + const value = valueParts.join("=").replace(/^["']|["']$/g, ""); + envVars[key.trim()] = value.trim(); + } + } + } + + return envVars; +} diff --git a/create-db/src/geolocation.ts b/create-db/src/geolocation.ts new file mode 100644 index 0000000..f358d4d --- /dev/null +++ b/create-db/src/geolocation.ts @@ -0,0 +1,173 @@ +import type { + UserLocation, + RegionId, + RegionCoordinates, + GeoLocationResponse, +} from "./types.js"; + +// Test locations for geolocation testing +// Set TEST_LOCATION to one of these to simulate being in that location +// Set to null to use real IP-based geolocation +const TEST_LOCATIONS = { + singapore: { latitude: 1.3521, longitude: 103.8198 }, // ap-southeast-1 + tokyo: { latitude: 35.6762, longitude: 139.6503 }, // ap-northeast-1 + frankfurt: { latitude: 50.1109, longitude: 8.6821 }, // eu-central-1 + paris: { latitude: 48.8566, longitude: 2.3522 }, // eu-west-3 + virginia: { latitude: 38.9072, longitude: -77.0369 }, // us-east-1 + california: { latitude: 37.7749, longitude: -122.4194 }, // us-west-1 +}; + +// Set this to simulate a location (e.g., TEST_LOCATIONS.tokyo) +// or null for real geolocation +const TEST_LOCATION: { latitude: number; longitude: number } | null = null; + +export const REGION_COORDINATES: Record = { + "ap-southeast-1": { lat: 1.3521, lng: 103.8198 }, + "ap-northeast-1": { lat: 35.6762, lng: 139.6503 }, + "eu-central-1": { lat: 50.1109, lng: 8.6821 }, + "eu-west-3": { lat: 48.8566, lng: 2.3522 }, + "us-east-1": { lat: 38.9072, lng: -77.0369 }, + "us-west-1": { lat: 37.7749, lng: -122.4194 }, +}; + +/** + * Calculate the great-circle distance between two points on Earth using the Haversine formula. + * @param lat1 Latitude of first point in degrees + * @param lng1 Longitude of first point in degrees + * @param lat2 Latitude of second point in degrees + * @param lng2 Longitude of second point in degrees + * @returns Distance in kilometers + */ +export function calculateHaversineDistance( + lat1: number, + lng1: number, + lat2: number, + lng2: number +): number { + const EARTH_RADIUS_KM = 6371; + const toRadians = (degrees: number) => (degrees * Math.PI) / 180; + + const lat1Rad = toRadians(lat1); + const lat2Rad = toRadians(lat2); + const deltaLatRad = toRadians(lat2 - lat1); + const deltaLngRad = toRadians(lng2 - lng1); + + const a = + Math.sin(deltaLatRad / 2) ** 2 + + Math.cos(lat1Rad) * Math.cos(lat2Rad) * Math.sin(deltaLngRad / 2) ** 2; + const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + + return EARTH_RADIUS_KM * c; +} + +/** + * Detect user's location via IP geolocation API or test location override. + * Returns null if detection fails or times out. + */ +export async function detectUserLocation(): Promise { + // FOR TESTING: Return test location if configured + if (TEST_LOCATION !== null) { + return { + country: "TEST", + continent: "TEST", + city: "Test City", + region: "Test Region", + latitude: TEST_LOCATION.latitude, + longitude: TEST_LOCATION.longitude, + }; + } + + // Real geolocation via IP API + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), 3000); + + try { + const response = await fetch("https://ipapi.co/json/", { + method: "GET", + headers: { "User-Agent": "create-db-cli/1.0" }, + signal: controller.signal, + }); + + if (!response.ok) return null; + + const data = (await response.json()) as GeoLocationResponse; + + // Validate that we have valid coordinates + if ( + typeof data.latitude !== "number" || + typeof data.longitude !== "number" || + !Number.isFinite(data.latitude) || + !Number.isFinite(data.longitude) + ) { + return null; + } + + return { + country: data.country_code, + continent: data.continent_code, + city: data.city, + region: data.region, + latitude: data.latitude, + longitude: data.longitude, + }; + } catch { + // Return null on any error (timeout, network, etc.) + return null; + } finally { + clearTimeout(timeout); + } +} + +/** + * Find the closest AWS region to a given location using Haversine distance. + * Returns null if the location is invalid or missing coordinates. + */ +export function getRegionClosestToLocation( + userLocation: { + latitude?: number | string; + longitude?: number | string; + } | null +): RegionId | null { + // Validate input + if ( + !userLocation || + userLocation.latitude == null || + userLocation.longitude == null + ) { + return null; + } + + // Parse and validate coordinates + const userLat = + typeof userLocation.latitude === "number" + ? userLocation.latitude + : parseFloat(String(userLocation.latitude)); + const userLng = + typeof userLocation.longitude === "number" + ? userLocation.longitude + : parseFloat(String(userLocation.longitude)); + + if (!Number.isFinite(userLat) || !Number.isFinite(userLng)) { + return null; + } + + // Find closest region + let closestRegion: RegionId | null = null; + let minDistance = Infinity; + + for (const [regionId, coords] of Object.entries(REGION_COORDINATES)) { + const distance = calculateHaversineDistance( + userLat, + userLng, + coords.lat, + coords.lng + ); + + if (distance < minDistance) { + minDistance = distance; + closestRegion = regionId as RegionId; + } + } + + return closestRegion; +} diff --git a/create-db/src/index.ts b/create-db/src/index.ts index d4ba97c..ea85b24 100644 --- a/create-db/src/index.ts +++ b/create-db/src/index.ts @@ -1,594 +1,359 @@ -import { intro, outro, cancel, select, spinner, log, isCancel } from "@clack/prompts"; +import { + intro, + outro, + cancel, + select, + spinner, + log, + isCancel, +} from "@clack/prompts"; import { createRouterClient, os } from "@orpc/server"; import { randomUUID } from "crypto"; import dotenv from "dotenv"; import fs from "fs"; -import path from "path"; import pc from "picocolors"; import terminalLink from "terminal-link"; import { createCli } from "trpc-cli"; import { z } from "zod"; import { - type UserLocation, - type Region, - type RegionCoordinates, - type CreateDatabaseResult, - type DatabaseResult, - type ApiResponse, - type GeoLocationResponse, - type RegionsResponse, - type ProgrammaticCreateOptions, - type RegionId, - RegionSchema, + type Region, + type CreateDatabaseResult, + type DatabaseResult, + type ProgrammaticCreateOptions, + type RegionId, + RegionSchema, } from "./types.js"; +import { sendAnalytics, flushAnalytics } from "./analytics.js"; +import { createDatabaseCore, getCommandName } from "./database.js"; +import { readUserEnvFile } from "./env-utils.js"; +import { + detectUserLocation, + getRegionClosestToLocation, +} from "./geolocation.js"; +import { checkOnline, getRegions, validateRegion } from "./regions.js"; export type { - Region, - RegionId, - CreateDatabaseResult, - DatabaseResult, - DatabaseError, - ProgrammaticCreateOptions, + Region, + RegionId, + CreateDatabaseResult, + DatabaseResult, + DatabaseError, + ProgrammaticCreateOptions, } from "./types.js"; export { isDatabaseError, isDatabaseSuccess, RegionSchema } from "./types.js"; dotenv.config({ - quiet: true + quiet: true, }); const CREATE_DB_WORKER_URL = - process.env.CREATE_DB_WORKER_URL || "https://create-db-temp.prisma.io"; + process.env.CREATE_DB_WORKER_URL || "https://create-db-temp.prisma.io"; const CLAIM_DB_WORKER_URL = - process.env.CLAIM_DB_WORKER_URL || "https://create-db.prisma.io"; - -const REGION_COORDINATES: Record = { - "ap-southeast-1": { lat: 1.3521, lng: 103.8198 }, - "ap-northeast-1": { lat: 35.6762, lng: 139.6503 }, - "eu-central-1": { lat: 50.1109, lng: 8.6821 }, - "eu-west-3": { lat: 48.8566, lng: 2.3522 }, - "us-east-1": { lat: 38.9072, lng: -77.0369 }, - "us-west-1": { lat: 37.7749, lng: -122.4194 }, + process.env.CLAIM_DB_WORKER_URL || "https://create-db.prisma.io"; + +// Wrapper functions that include worker URLs +const sendAnalyticsWithUrl = ( + eventName: string, + properties: Record, + cliRunId: string +) => sendAnalytics(eventName, properties, cliRunId, CREATE_DB_WORKER_URL); + +const checkOnlineWithUrl = async () => { + try { + await checkOnline(CREATE_DB_WORKER_URL); + } catch { + await flushAnalytics(); + process.exit(1); + } }; -const pendingAnalytics: Promise[] = []; - -async function sendAnalytics( - eventName: string, - properties: Record, - cliRunId: string -): Promise { - const controller = new AbortController(); - const timer = setTimeout(() => controller.abort(), 5000); - - const promise = (async () => { - try { - await fetch(`${CREATE_DB_WORKER_URL}/analytics`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - eventName, - properties: { distinct_id: cliRunId, ...properties }, - }), - signal: controller.signal, - }); - } catch { - // Analytics failures should not block CLI - } finally { - clearTimeout(timer); - } - })(); - - pendingAnalytics.push(promise); -} - -async function flushAnalytics(maxWaitMs = 500): Promise { - if (pendingAnalytics.length === 0) return; - await Promise.race([ - Promise.all(pendingAnalytics), - new Promise((resolve) => setTimeout(resolve, maxWaitMs)), - ]); -} - -function getCommandName(): string { - const executable = process.argv[1] || "create-db"; - if (executable.includes("create-pg")) return "create-pg"; - if (executable.includes("create-postgres")) return "create-postgres"; - return "create-db"; -} - -async function detectUserLocation(): Promise { - try { - const response = await fetch("https://ipapi.co/json/", { - method: "GET", - headers: { "User-Agent": "create-db-cli/1.0" }, - }); - - if (!response.ok) return null; - - const data = (await response.json()) as GeoLocationResponse; - return { - country: data.country_code, - continent: data.continent_code, - city: data.city, - region: data.region, - latitude: data.latitude, - longitude: data.longitude, - }; - } catch { - return null; - } -} - -function getRegionClosestToLocation( - userLocation: { latitude?: number | string; longitude?: number | string } | null -): RegionId | null { - if (!userLocation) return null; - - const userLat = parseFloat(String(userLocation.latitude)); - const userLng = parseFloat(String(userLocation.longitude)); +const getRegionsWithUrl = () => getRegions(CREATE_DB_WORKER_URL); - if (isNaN(userLat) || isNaN(userLng)) return null; +const validateRegionWithUrl = (region: string) => + validateRegion(region, CREATE_DB_WORKER_URL); - let closestRegion: RegionId | null = null; - let minDistance = Infinity; +const createDatabaseCoreWithUrl = ( + region: string, + userAgent?: string, + cliRunId?: string +) => + createDatabaseCore( + region, + CREATE_DB_WORKER_URL, + CLAIM_DB_WORKER_URL, + userAgent, + cliRunId + ); - for (const [region, coordinates] of Object.entries(REGION_COORDINATES)) { - const latDiff = ((userLat - coordinates.lat) * Math.PI) / 180; - const lngDiff = ((userLng - coordinates.lng) * Math.PI) / 180; - 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; - const distance = 6371 * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); - - if (distance < minDistance) { - minDistance = distance; - closestRegion = region as RegionId; +const router = os.router({ + create: os + .meta({ + description: "Create a new Prisma Postgres database", + default: true, + }) + .input( + z.object({ + region: RegionSchema.optional() + .describe("AWS region for the database") + .meta({ alias: "r" }), + interactive: z + .boolean() + .optional() + .default(false) + .describe("Run in interactive mode to select a region") + .meta({ alias: "i" }), + json: z + .boolean() + .optional() + .default(false) + .describe("Output machine-readable JSON") + .meta({ alias: "j" }), + env: z + .string() + .optional() + .describe( + "Write DATABASE_URL and CLAIM_URL to the specified .env file" + ) + .meta({ alias: "e" }), + }) + ) + .handler(async ({ input }) => { + const cliRunId = randomUUID(); + const CLI_NAME = getCommandName(); + + let userAgent: string | undefined; + const userEnvVars = readUserEnvFile(); + if (userEnvVars.PRISMA_ACTOR_NAME && userEnvVars.PRISMA_ACTOR_PROJECT) { + userAgent = `${userEnvVars.PRISMA_ACTOR_NAME}/${userEnvVars.PRISMA_ACTOR_PROJECT}`; + } + + void sendAnalyticsWithUrl( + "create_db:cli_command_ran", + { + command: CLI_NAME, + "has-region-flag": !!input.region, + "has-interactive-flag": input.interactive, + "has-json-flag": input.json, + "has-env-flag": !!input.env, + "has-user-agent-from-env": !!userAgent, + "node-version": process.version, + platform: process.platform, + arch: process.arch, + }, + cliRunId + ); + + let region: RegionId = input.region ?? "us-east-1"; + + if (!input.region) { + const userLocation = await detectUserLocation(); + region = getRegionClosestToLocation(userLocation) ?? region; + } + + const envPath = input.env; + const envEnabled = + typeof envPath === "string" && envPath.trim().length > 0; + + if (input.json || envEnabled) { + if (input.interactive) { + await checkOnlineWithUrl(); + const regions = await getRegionsWithUrl(); + + const selectedRegion = await select({ + message: "Choose a region:", + options: regions.map((r) => ({ + value: r.id, + label: r.name || r.id, + })), + initialValue: + regions.find((r) => r.id === region)?.id || regions[0]?.id, + }); + + if (isCancel(selectedRegion)) { + cancel(pc.red("Operation cancelled.")); + await flushAnalytics(); + process.exit(0); + } + + region = selectedRegion as RegionId; + void sendAnalyticsWithUrl( + "create_db:region_selected", + { region, "selection-method": "interactive" }, + cliRunId + ); + } else if (input.region) { + await validateRegionWithUrl(region); + void sendAnalyticsWithUrl( + "create_db:region_selected", + { region, "selection-method": "flag" }, + cliRunId + ); } - } - return closestRegion; -} + await checkOnlineWithUrl(); + const result = await createDatabaseCoreWithUrl( + region, + userAgent, + cliRunId + ); + await flushAnalytics(); -function readUserEnvFile(): Record { - const envPath = path.join(process.cwd(), ".env"); - if (!fs.existsSync(envPath)) return {}; + if (input.json) { + console.log(JSON.stringify(result, null, 2)); + return; + } - const envContent = fs.readFileSync(envPath, "utf8"); - const envVars: Record = {}; + if (!result.success) { + console.error(result.message); + process.exit(1); + } - for (const line of envContent.split("\n")) { - const trimmed = line.trim(); - if (trimmed && !trimmed.startsWith("#")) { - const [key, ...valueParts] = trimmed.split("="); - if (key && valueParts.length > 0) { - const value = valueParts.join("=").replace(/^["']|["']$/g, ""); - envVars[key.trim()] = value.trim(); + try { + const targetEnvPath = envPath!; + const lines = [ + `DATABASE_URL="${result.connectionString ?? ""}"`, + `CLAIM_URL="${result.claimUrl}"`, + "", + ]; + + let prefix = ""; + if (fs.existsSync(targetEnvPath)) { + const existing = fs.readFileSync(targetEnvPath, "utf8"); + if (existing.length > 0 && !existing.endsWith("\n")) { + prefix = "\n"; } + } + + fs.appendFileSync(targetEnvPath, prefix + lines.join("\n"), { + encoding: "utf8", + }); + + console.log( + pc.green(`Wrote DATABASE_URL and CLAIM_URL to ${targetEnvPath}`) + ); + } catch (err) { + console.error( + pc.red( + `Failed to write environment variables to ${envPath}: ${ + err instanceof Error ? err.message : String(err) + }` + ) + ); + process.exit(1); } - } - return envVars; -} + return; + } -async function checkOnline(): Promise { - try { - const res = await fetch(`${CREATE_DB_WORKER_URL}/health`); - if (!res.ok) throw new Error("API not available"); - } catch { - console.error( - pc.bold(pc.red("\nāœ– Error: Cannot reach Prisma Postgres API server.\n")) - ); - console.error( - pc.dim( - `Check your internet connection or visit ${pc.green("https://www.prisma-status.com/")}\n` - ) - ); - await flushAnalytics(); - process.exit(1); - } -} + await checkOnlineWithUrl(); -async function getRegions(): Promise { - const res = await fetch(`${CREATE_DB_WORKER_URL}/regions`); + intro(pc.bold(pc.cyan("šŸš€ Creating a Prisma Postgres database"))); - if (!res.ok) { - throw new Error( - `Failed to fetch regions. Status: ${res.status} ${res.statusText}` - ); - } + if (input.interactive) { + const regions = await getRegionsWithUrl(); - const data = (await res.json()) as RegionsResponse; - const regions: Region[] = Array.isArray(data) ? data : (data.data ?? []); - return regions.filter((region) => region.status === "available"); -} + const selectedRegion = await select({ + message: "Choose a region:", + options: regions.map((r) => ({ value: r.id, label: r.name || r.id })), + initialValue: + regions.find((r) => r.id === region)?.id || regions[0]?.id, + }); -async function validateRegion(region: string): Promise { - const regions = await getRegions(); - const regionIds = regions.map((r) => r.id); + if (isCancel(selectedRegion)) { + cancel(pc.red("Operation cancelled.")); + await flushAnalytics(); + process.exit(0); + } - if (!regionIds.includes(region)) { - throw new Error( - `Invalid region: ${region}. Available regions: ${regionIds.join(", ")}` + region = selectedRegion as RegionId; + void sendAnalyticsWithUrl( + "create_db:region_selected", + { region, "selection-method": "interactive" }, + cliRunId + ); + } else if (input.region) { + await validateRegionWithUrl(region); + void sendAnalyticsWithUrl( + "create_db:region_selected", + { region, "selection-method": "flag" }, + cliRunId ); - } + } - return region; -} + const s = spinner(); + s.start(`Creating database in ${pc.cyan(region)}...`); -async function createDatabaseCore( - region: string, - userAgent?: string, - cliRunId?: string -): Promise { - const name = new Date().toISOString(); - const runId = cliRunId ?? randomUUID(); - - const resp = await fetch(`${CREATE_DB_WORKER_URL}/create`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - region, - name, - utm_source: getCommandName(), - userAgent, - }), - }); - - if (resp.status === 429) { - void sendAnalytics( - "create_db:database_creation_failed", - { region, "error-type": "rate_limit", "status-code": 429 }, - runId - ); - return { - success: false, - error: "rate_limit_exceeded", - message: - "We're experiencing a high volume of requests. Please try again later.", - status: 429, - }; - } - - let result: ApiResponse; - let raw = ""; - try { - raw = await resp.text(); - result = JSON.parse(raw) as ApiResponse; - } catch { - void sendAnalytics( - "create_db:database_creation_failed", - { region, "error-type": "invalid_json", "status-code": resp.status }, - runId - ); - return { - success: false, - error: "invalid_json", - message: "Unexpected response from create service.", - raw, - status: resp.status, - }; - } - - if (result.error) { - void sendAnalytics( - "create_db:database_creation_failed", - { - region, - "error-type": "api_error", - "error-message": result.error.message, - }, - runId - ); - return { - success: false, - error: "api_error", - message: result.error.message || "Unknown error", - details: result.error, - status: result.error.status ?? resp.status, - }; - } - - const database = result.data?.database ?? result.databases?.[0]; - const projectId = result.data?.id ?? result.id ?? ""; - - const apiKeys = database?.apiKeys; - const directConnDetails = result.data - ? apiKeys?.[0]?.directConnection - : result.databases?.[0]?.apiKeys?.[0]?.ppgDirectConnection; - - const directUser = directConnDetails?.user - ? encodeURIComponent(String(directConnDetails.user)) - : ""; - const directPass = directConnDetails?.pass - ? encodeURIComponent(String(directConnDetails.pass)) - : ""; - const directHost = directConnDetails?.host; - const directPort = directConnDetails?.port - ? `:${directConnDetails.port}` - : ""; - const directDbName = directConnDetails?.database || "postgres"; - - const connectionString = - directConnDetails && directHost - ? `postgresql://${directUser}:${directPass}@${directHost}${directPort}/${directDbName}?sslmode=require` - : null; - - const claimUrl = `${CLAIM_DB_WORKER_URL}/claim?projectID=${projectId}&utm_source=${userAgent || getCommandName()}&utm_medium=cli`; - const expiryDate = new Date(Date.now() + 24 * 60 * 60 * 1000); - - void sendAnalytics( - "create_db:database_created", - { region, utm_source: getCommandName() }, - runId - ); - - return { - success: true, - connectionString, - claimUrl, - deletionDate: expiryDate.toISOString(), - region: database?.region?.id || region, - name: database?.name ?? name, - projectId, + const result = await createDatabaseCoreWithUrl( + region, userAgent, - }; -} + cliRunId + ); -const router = os.router({ - create: os - .meta({ - description: "Create a new Prisma Postgres database", - default: true, - }) - .input( - z.object({ - region: RegionSchema.optional() - .describe("AWS region for the database") - .meta({ alias: "r" }), - interactive: z - .boolean() - .optional() - .default(false) - .describe("Run in interactive mode to select a region") - .meta({ alias: "i" }), - json: z - .boolean() - .optional() - .default(false) - .describe("Output machine-readable JSON") - .meta({ alias: "j" }), - env: z - .string() - .optional() - .describe( - "Write DATABASE_URL and CLAIM_URL to the specified .env file" - ) - .meta({ alias: "e" }), - }) + if (!result.success) { + s.stop(pc.red(`Error: ${result.message}`)); + await flushAnalytics(); + process.exit(1); + } + + s.stop(pc.green("Database created successfully!")); + + const expiryFormatted = new Date(result.deletionDate).toLocaleString(); + const clickableUrl = terminalLink(result.claimUrl, result.claimUrl, { + fallback: false, + }); + + log.message(""); + log.info(pc.bold("Database Connection")); + log.message(""); + + if (result.connectionString) { + log.message(pc.cyan(" Connection String:")); + log.message(" " + pc.yellow(result.connectionString)); + log.message(""); + } else { + log.warning(pc.yellow(" Connection details are not available.")); + log.message(""); + } + + log.success(pc.bold("Claim Your Database")); + log.message(pc.cyan(" Keep your database for free:")); + log.message(" " + pc.yellow(clickableUrl)); + log.message( + pc.italic( + pc.dim( + ` Database will be deleted on ${expiryFormatted} if not claimed.` + ) ) - .handler(async ({ input }) => { - const cliRunId = randomUUID(); - const CLI_NAME = getCommandName(); - - let userAgent: string | undefined; - const userEnvVars = readUserEnvFile(); - if (userEnvVars.PRISMA_ACTOR_NAME && userEnvVars.PRISMA_ACTOR_PROJECT) { - userAgent = `${userEnvVars.PRISMA_ACTOR_NAME}/${userEnvVars.PRISMA_ACTOR_PROJECT}`; - } - - void sendAnalytics( - "create_db:cli_command_ran", - { - command: CLI_NAME, - "has-region-flag": !!input.region, - "has-interactive-flag": input.interactive, - "has-json-flag": input.json, - "has-env-flag": !!input.env, - "has-user-agent-from-env": !!userAgent, - "node-version": process.version, - platform: process.platform, - arch: process.arch, - }, - cliRunId - ); - - let region: RegionId = input.region ?? "us-east-1"; - - if (!input.region) { - const userLocation = await detectUserLocation(); - region = getRegionClosestToLocation(userLocation) ?? region; - } - - const envPath = input.env; - const envEnabled = - typeof envPath === "string" && envPath.trim().length > 0; - - if (input.json || envEnabled) { - if (input.interactive) { - await checkOnline(); - const regions = await getRegions(); - - const selectedRegion = await select({ - message: "Choose a region:", - options: regions.map((r) => ({ value: r.id, label: r.name || r.id })), - initialValue: - regions.find((r) => r.id === region)?.id || regions[0]?.id, - }); - - if (isCancel(selectedRegion)) { - cancel(pc.red("Operation cancelled.")); - await flushAnalytics(); - process.exit(0); - } - - region = selectedRegion as RegionId; - void sendAnalytics( - "create_db:region_selected", - { region, "selection-method": "interactive" }, - cliRunId - ); - } else if (input.region) { - await validateRegion(region); - void sendAnalytics( - "create_db:region_selected", - { region, "selection-method": "flag" }, - cliRunId - ); - } - - await checkOnline(); - const result = await createDatabaseCore(region, userAgent, cliRunId); - await flushAnalytics(); - - if (input.json) { - console.log(JSON.stringify(result, null, 2)); - return; - } - - if (!result.success) { - console.error(result.message); - process.exit(1); - } - - try { - const targetEnvPath = envPath!; - const lines = [ - `DATABASE_URL="${result.connectionString ?? ""}"`, - `CLAIM_URL="${result.claimUrl}"`, - "", - ]; - - let prefix = ""; - if (fs.existsSync(targetEnvPath)) { - const existing = fs.readFileSync(targetEnvPath, "utf8"); - if (existing.length > 0 && !existing.endsWith("\n")) { - prefix = "\n"; - } - } - - fs.appendFileSync(targetEnvPath, prefix + lines.join("\n"), { - encoding: "utf8", - }); - - console.log( - pc.green(`Wrote DATABASE_URL and CLAIM_URL to ${targetEnvPath}`) - ); - } catch (err) { - console.error( - pc.red( - `Failed to write environment variables to ${envPath}: ${err instanceof Error ? err.message : String(err) - }` - ) - ); - process.exit(1); - } - - return; - } - - await checkOnline(); - - intro(pc.bold(pc.cyan("šŸš€ Creating a Prisma Postgres database"))); - - if (input.interactive) { - const regions = await getRegions(); - - const selectedRegion = await select({ - message: "Choose a region:", - options: regions.map((r) => ({ value: r.id, label: r.name || r.id })), - initialValue: - regions.find((r) => r.id === region)?.id || regions[0]?.id, - }); - - if (isCancel(selectedRegion)) { - cancel(pc.red("Operation cancelled.")); - await flushAnalytics(); - process.exit(0); - } - - region = selectedRegion as RegionId; - void sendAnalytics( - "create_db:region_selected", - { region, "selection-method": "interactive" }, - cliRunId - ); - } else if (input.region) { - await validateRegion(region); - void sendAnalytics( - "create_db:region_selected", - { region, "selection-method": "flag" }, - cliRunId - ); - } - - const s = spinner(); - s.start(`Creating database in ${pc.cyan(region)}...`); - - const result = await createDatabaseCore(region, userAgent, cliRunId); - - if (!result.success) { - s.stop(pc.red(`Error: ${result.message}`)); - await flushAnalytics(); - process.exit(1); - } - - s.stop(pc.green("Database created successfully!")); - - const expiryFormatted = new Date(result.deletionDate).toLocaleString(); - const clickableUrl = terminalLink(result.claimUrl, result.claimUrl, { - fallback: false, - }); - - log.message(""); - log.info(pc.bold("Database Connection")); - log.message(""); - - if (result.connectionString) { - log.message(pc.cyan(" Connection String:")); - log.message(" " + pc.yellow(result.connectionString)); - log.message(""); - } else { - log.warning(pc.yellow(" Connection details are not available.")); - log.message(""); - } - - log.success(pc.bold("Claim Your Database")); - log.message(pc.cyan(" Keep your database for free:")); - log.message(" " + pc.yellow(clickableUrl)); - log.message( - pc.italic(pc.dim(` Database will be deleted on ${expiryFormatted} if not claimed.`)) - ); - - outro(pc.dim("Done!")); - await flushAnalytics(); - }), - - regions: os - .meta({ description: "List available Prisma Postgres regions" }) - .handler(async (): Promise => { - const regions = await getRegions(); - - log.message(""); - log.info(pc.bold(pc.cyan("Available Prisma Postgres regions:"))); - log.message(""); - for (const r of regions) { - log.message(` ${pc.green(r.id)} - ${r.name || r.id}`); - } - log.message(""); - }), + ); + + outro(pc.dim("Done!")); + await flushAnalytics(); + }), + + regions: os + .meta({ description: "List available Prisma Postgres regions" }) + .handler(async (): Promise => { + const regions = await getRegionsWithUrl(); + + log.message(""); + log.info(pc.bold(pc.cyan("Available Prisma Postgres regions:"))); + log.message(""); + for (const r of regions) { + log.message(` ${pc.green(r.id)} - ${r.name || r.id}`); + } + log.message(""); + }), }); export function createDbCli() { - return createCli({ - router, - name: getCommandName(), - version: "1.1.0", - description: "Instantly create a temporary Prisma Postgres database", - }); + return createCli({ + router, + name: getCommandName(), + version: "1.1.0", + description: "Instantly create a temporary Prisma Postgres database", + }); } const caller = createRouterClient(router, { context: {} }); @@ -616,9 +381,12 @@ const caller = createRouterClient(router, { context: {} }); * ``` */ export async function create( - options?: ProgrammaticCreateOptions + options?: ProgrammaticCreateOptions ): Promise { - return createDatabaseCore(options?.region || "us-east-1", options?.userAgent); + return createDatabaseCoreWithUrl( + options?.region || "us-east-1", + options?.userAgent + ); } /** @@ -633,5 +401,5 @@ export async function create( * ``` */ export async function regions(): Promise { - return getRegions(); + return getRegionsWithUrl(); } diff --git a/create-db/src/regions.ts b/create-db/src/regions.ts new file mode 100644 index 0000000..7a0a5f6 --- /dev/null +++ b/create-db/src/regions.ts @@ -0,0 +1,46 @@ +import pc from "picocolors"; +import type { Region, RegionsResponse } from "./types.js"; + +export async function checkOnline(workerUrl: string): Promise { + try { + const res = await fetch(`${workerUrl}/health`); + if (!res.ok) throw new Error("API not available"); + } catch { + console.error( + pc.bold(pc.red("\nāœ– Error: Cannot reach Prisma Postgres API server.\n")) + ); + console.error( + pc.dim( + `Check your internet connection or visit ${pc.green("https://www.prisma-status.com/")}\n` + ) + ); + throw new Error("Cannot reach API server"); + } +} + +export async function getRegions(workerUrl: string): Promise { + const res = await fetch(`${workerUrl}/regions`); + + if (!res.ok) { + throw new Error( + `Failed to fetch regions. Status: ${res.status} ${res.statusText}` + ); + } + + const data = (await res.json()) as RegionsResponse; + const regions: Region[] = Array.isArray(data) ? data : (data.data ?? []); + return regions.filter((region) => region.status === "available"); +} + +export async function validateRegion(region: string, workerUrl: string): Promise { + const regions = await getRegions(workerUrl); + const regionIds = regions.map((r) => r.id); + + if (!regionIds.includes(region)) { + throw new Error( + `Invalid region: ${region}. Available regions: ${regionIds.join(", ")}` + ); + } + + return region; +} From 7dea8680066d059694951c62fd8fdc83516acdfe Mon Sep 17 00:00:00 2001 From: Aidan McAlister Date: Mon, 8 Dec 2025 15:46:00 -0500 Subject: [PATCH 14/22] fix: formatting --- .vscode/settings.json | 3 - create-db/__tests__/create.test.ts | 2 +- create-db/__tests__/geolocation.test.ts | 62 +++++-- create-db/package.json | 2 +- create-db/src/analytics.ts | 58 +++--- create-db/src/cli.ts | 1 - create-db/src/database.ts | 228 ++++++++++++------------ create-db/src/env-utils.ts | 28 +-- create-db/src/regions.ts | 67 +++---- create-db/src/types.ts | 132 +++++++------- create-db/vitest.config.ts | 1 - 11 files changed, 311 insertions(+), 273 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 18399cb..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "folder-color.pathColors": [] -} diff --git a/create-db/__tests__/create.test.ts b/create-db/__tests__/create.test.ts index 3aa3a3e..33116a6 100644 --- a/create-db/__tests__/create.test.ts +++ b/create-db/__tests__/create.test.ts @@ -8,7 +8,7 @@ const CLI_PATH = path.resolve(__dirname, "../dist/cli.mjs"); const runCli = async ( args: string[] = [], - options: { env?: Record;[key: string]: unknown } = {} + options: { env?: Record; [key: string]: unknown } = {} ) => { const result = await execa("node", [CLI_PATH, ...args], { ...options, diff --git a/create-db/__tests__/geolocation.test.ts b/create-db/__tests__/geolocation.test.ts index 8c1c5eb..3c03ee7 100644 --- a/create-db/__tests__/geolocation.test.ts +++ b/create-db/__tests__/geolocation.test.ts @@ -101,9 +101,9 @@ describe("getRegionClosestToLocation", () => { expect( getRegionClosestToLocation({ latitude: undefined, longitude: undefined }) ).toBe(null); - expect( - getRegionClosestToLocation({ latitude: NaN, longitude: NaN }) - ).toBe(null); + expect(getRegionClosestToLocation({ latitude: NaN, longitude: NaN })).toBe( + null + ); expect( getRegionClosestToLocation({ latitude: "invalid", longitude: "invalid" }) ).toBe(null); @@ -123,8 +123,12 @@ describe("REGION_COORDINATES", () => { for (const region of expectedRegions) { expect(REGION_COORDINATES).toHaveProperty(region); - expect(REGION_COORDINATES[region as keyof typeof REGION_COORDINATES]).toHaveProperty("lat"); - expect(REGION_COORDINATES[region as keyof typeof REGION_COORDINATES]).toHaveProperty("lng"); + expect( + REGION_COORDINATES[region as keyof typeof REGION_COORDINATES] + ).toHaveProperty("lat"); + expect( + REGION_COORDINATES[region as keyof typeof REGION_COORDINATES] + ).toHaveProperty("lng"); } }); @@ -140,7 +144,13 @@ describe("REGION_COORDINATES", () => { describe("CLI database creation with explicit regions", () => { it("creates database in ap-southeast-1 (Singapore)", async () => { - const { stdout } = await execa("node", ["./dist/cli.mjs", "create", "--region", "ap-southeast-1", "--json"]); + const { stdout } = await execa("node", [ + "./dist/cli.mjs", + "create", + "--region", + "ap-southeast-1", + "--json", + ]); const result = JSON.parse(stdout); expect(result.success).toBe(true); @@ -150,7 +160,13 @@ describe("CLI database creation with explicit regions", () => { }, 30000); it("creates database in ap-northeast-1 (Tokyo)", async () => { - const { stdout } = await execa("node", ["./dist/cli.mjs", "create", "--region", "ap-northeast-1", "--json"]); + const { stdout } = await execa("node", [ + "./dist/cli.mjs", + "create", + "--region", + "ap-northeast-1", + "--json", + ]); const result = JSON.parse(stdout); expect(result.success).toBe(true); @@ -160,7 +176,13 @@ describe("CLI database creation with explicit regions", () => { }, 30000); it("creates database in eu-central-1 (Frankfurt)", async () => { - const { stdout } = await execa("node", ["./dist/cli.mjs", "create", "--region", "eu-central-1", "--json"]); + const { stdout } = await execa("node", [ + "./dist/cli.mjs", + "create", + "--region", + "eu-central-1", + "--json", + ]); const result = JSON.parse(stdout); expect(result.success).toBe(true); @@ -170,7 +192,13 @@ describe("CLI database creation with explicit regions", () => { }, 30000); it("creates database in eu-west-3 (Paris)", async () => { - const { stdout } = await execa("node", ["./dist/cli.mjs", "create", "--region", "eu-west-3", "--json"]); + const { stdout } = await execa("node", [ + "./dist/cli.mjs", + "create", + "--region", + "eu-west-3", + "--json", + ]); const result = JSON.parse(stdout); expect(result.success).toBe(true); @@ -180,7 +208,13 @@ describe("CLI database creation with explicit regions", () => { }, 30000); it("creates database in us-east-1 (Virginia)", async () => { - const { stdout } = await execa("node", ["./dist/cli.mjs", "create", "--region", "us-east-1", "--json"]); + const { stdout } = await execa("node", [ + "./dist/cli.mjs", + "create", + "--region", + "us-east-1", + "--json", + ]); const result = JSON.parse(stdout); expect(result.success).toBe(true); @@ -190,7 +224,13 @@ describe("CLI database creation with explicit regions", () => { }, 30000); it("creates database in us-west-1 (California)", async () => { - const { stdout } = await execa("node", ["./dist/cli.mjs", "create", "--region", "us-west-1", "--json"]); + const { stdout } = await execa("node", [ + "./dist/cli.mjs", + "create", + "--region", + "us-west-1", + "--json", + ]); const result = JSON.parse(stdout); expect(result.success).toBe(true); diff --git a/create-db/package.json b/create-db/package.json index 2136de8..0a71e02 100644 --- a/create-db/package.json +++ b/create-db/package.json @@ -70,4 +70,4 @@ "typescript": "^5.9.3", "vitest": "^4.0.15" } -} \ No newline at end of file +} diff --git a/create-db/src/analytics.ts b/create-db/src/analytics.ts index 587fbea..8e191a1 100644 --- a/create-db/src/analytics.ts +++ b/create-db/src/analytics.ts @@ -1,39 +1,39 @@ const pendingAnalytics: Promise[] = []; export async function sendAnalytics( - eventName: string, - properties: Record, - cliRunId: string, - workerUrl: string + eventName: string, + properties: Record, + cliRunId: string, + workerUrl: string ): Promise { - const controller = new AbortController(); - const timer = setTimeout(() => controller.abort(), 5000); + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), 5000); - const promise = (async () => { - try { - await fetch(`${workerUrl}/analytics`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - eventName, - properties: { distinct_id: cliRunId, ...properties }, - }), - signal: controller.signal, - }); - } catch { - // Analytics failures should not block CLI - } finally { - clearTimeout(timer); - } - })(); + const promise = (async () => { + try { + await fetch(`${workerUrl}/analytics`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + eventName, + properties: { distinct_id: cliRunId, ...properties }, + }), + signal: controller.signal, + }); + } catch { + // Analytics failures should not block CLI + } finally { + clearTimeout(timer); + } + })(); - pendingAnalytics.push(promise); + pendingAnalytics.push(promise); } export async function flushAnalytics(maxWaitMs = 500): Promise { - if (pendingAnalytics.length === 0) return; - await Promise.race([ - Promise.all(pendingAnalytics), - new Promise((resolve) => setTimeout(resolve, maxWaitMs)), - ]); + if (pendingAnalytics.length === 0) return; + await Promise.race([ + Promise.all(pendingAnalytics), + new Promise((resolve) => setTimeout(resolve, maxWaitMs)), + ]); } diff --git a/create-db/src/cli.ts b/create-db/src/cli.ts index 45b13e9..8c21feb 100644 --- a/create-db/src/cli.ts +++ b/create-db/src/cli.ts @@ -1,4 +1,3 @@ import { createDbCli } from "./index.js"; createDbCli().run(); - diff --git a/create-db/src/database.ts b/create-db/src/database.ts index 0f6d0cd..8c21252 100644 --- a/create-db/src/database.ts +++ b/create-db/src/database.ts @@ -3,133 +3,133 @@ import type { CreateDatabaseResult, ApiResponse } from "./types.js"; import { sendAnalytics } from "./analytics.js"; export function getCommandName(): string { - const executable = process.argv[1] || "create-db"; - if (executable.includes("create-pg")) return "create-pg"; - if (executable.includes("create-postgres")) return "create-postgres"; - return "create-db"; + const executable = process.argv[1] || "create-db"; + if (executable.includes("create-pg")) return "create-pg"; + if (executable.includes("create-postgres")) return "create-postgres"; + return "create-db"; } export async function createDatabaseCore( - region: string, - createDbWorkerUrl: string, - claimDbWorkerUrl: string, - userAgent?: string, - cliRunId?: string + region: string, + createDbWorkerUrl: string, + claimDbWorkerUrl: string, + userAgent?: string, + cliRunId?: string ): Promise { - const name = new Date().toISOString(); - const runId = cliRunId ?? randomUUID(); + const name = new Date().toISOString(); + const runId = cliRunId ?? randomUUID(); - const resp = await fetch(`${createDbWorkerUrl}/create`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - region, - name, - utm_source: getCommandName(), - userAgent, - }), - }); + const resp = await fetch(`${createDbWorkerUrl}/create`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + region, + name, + utm_source: getCommandName(), + userAgent, + }), + }); - if (resp.status === 429) { - void sendAnalytics( - "create_db:database_creation_failed", - { region, "error-type": "rate_limit", "status-code": 429 }, - runId, - createDbWorkerUrl - ); - return { - success: false, - error: "rate_limit_exceeded", - message: - "We're experiencing a high volume of requests. Please try again later.", - status: 429, - }; - } + if (resp.status === 429) { + void sendAnalytics( + "create_db:database_creation_failed", + { region, "error-type": "rate_limit", "status-code": 429 }, + runId, + createDbWorkerUrl + ); + return { + success: false, + error: "rate_limit_exceeded", + message: + "We're experiencing a high volume of requests. Please try again later.", + status: 429, + }; + } - let result: ApiResponse; - let raw = ""; - try { - raw = await resp.text(); - result = JSON.parse(raw) as ApiResponse; - } catch { - void sendAnalytics( - "create_db:database_creation_failed", - { region, "error-type": "invalid_json", "status-code": resp.status }, - runId, - createDbWorkerUrl - ); - return { - success: false, - error: "invalid_json", - message: "Unexpected response from create service.", - raw, - status: resp.status, - }; - } + let result: ApiResponse; + let raw = ""; + try { + raw = await resp.text(); + result = JSON.parse(raw) as ApiResponse; + } catch { + void sendAnalytics( + "create_db:database_creation_failed", + { region, "error-type": "invalid_json", "status-code": resp.status }, + runId, + createDbWorkerUrl + ); + return { + success: false, + error: "invalid_json", + message: "Unexpected response from create service.", + raw, + status: resp.status, + }; + } - if (result.error) { - void sendAnalytics( - "create_db:database_creation_failed", - { - region, - "error-type": "api_error", - "error-message": result.error.message, - }, - runId, - createDbWorkerUrl - ); - return { - success: false, - error: "api_error", - message: result.error.message || "Unknown error", - details: result.error, - status: result.error.status ?? resp.status, - }; - } + if (result.error) { + void sendAnalytics( + "create_db:database_creation_failed", + { + region, + "error-type": "api_error", + "error-message": result.error.message, + }, + runId, + createDbWorkerUrl + ); + return { + success: false, + error: "api_error", + message: result.error.message || "Unknown error", + details: result.error, + status: result.error.status ?? resp.status, + }; + } - const database = result.data?.database ?? result.databases?.[0]; - const projectId = result.data?.id ?? result.id ?? ""; + const database = result.data?.database ?? result.databases?.[0]; + const projectId = result.data?.id ?? result.id ?? ""; - const apiKeys = database?.apiKeys; - const directConnDetails = result.data - ? apiKeys?.[0]?.directConnection - : result.databases?.[0]?.apiKeys?.[0]?.ppgDirectConnection; + const apiKeys = database?.apiKeys; + const directConnDetails = result.data + ? apiKeys?.[0]?.directConnection + : result.databases?.[0]?.apiKeys?.[0]?.ppgDirectConnection; - const directUser = directConnDetails?.user - ? encodeURIComponent(String(directConnDetails.user)) - : ""; - const directPass = directConnDetails?.pass - ? encodeURIComponent(String(directConnDetails.pass)) - : ""; - const directHost = directConnDetails?.host; - const directPort = directConnDetails?.port - ? `:${directConnDetails.port}` - : ""; - const directDbName = directConnDetails?.database || "postgres"; + const directUser = directConnDetails?.user + ? encodeURIComponent(String(directConnDetails.user)) + : ""; + const directPass = directConnDetails?.pass + ? encodeURIComponent(String(directConnDetails.pass)) + : ""; + const directHost = directConnDetails?.host; + const directPort = directConnDetails?.port + ? `:${directConnDetails.port}` + : ""; + const directDbName = directConnDetails?.database || "postgres"; - const connectionString = - directConnDetails && directHost - ? `postgresql://${directUser}:${directPass}@${directHost}${directPort}/${directDbName}?sslmode=require` - : null; + const connectionString = + directConnDetails && directHost + ? `postgresql://${directUser}:${directPass}@${directHost}${directPort}/${directDbName}?sslmode=require` + : null; - const claimUrl = `${claimDbWorkerUrl}/claim?projectID=${projectId}&utm_source=${userAgent || getCommandName()}&utm_medium=cli`; - const expiryDate = new Date(Date.now() + 24 * 60 * 60 * 1000); + const claimUrl = `${claimDbWorkerUrl}/claim?projectID=${projectId}&utm_source=${userAgent || getCommandName()}&utm_medium=cli`; + const expiryDate = new Date(Date.now() + 24 * 60 * 60 * 1000); - void sendAnalytics( - "create_db:database_created", - { region, utm_source: getCommandName() }, - runId, - createDbWorkerUrl - ); + void sendAnalytics( + "create_db:database_created", + { region, utm_source: getCommandName() }, + runId, + createDbWorkerUrl + ); - return { - success: true, - connectionString, - claimUrl, - deletionDate: expiryDate.toISOString(), - region: database?.region?.id || region, - name: database?.name ?? name, - projectId, - userAgent, - }; + return { + success: true, + connectionString, + claimUrl, + deletionDate: expiryDate.toISOString(), + region: database?.region?.id || region, + name: database?.name ?? name, + projectId, + userAgent, + }; } diff --git a/create-db/src/env-utils.ts b/create-db/src/env-utils.ts index 91f449b..4a1df83 100644 --- a/create-db/src/env-utils.ts +++ b/create-db/src/env-utils.ts @@ -2,22 +2,22 @@ import fs from "fs"; import path from "path"; export function readUserEnvFile(): Record { - const envPath = path.join(process.cwd(), ".env"); - if (!fs.existsSync(envPath)) return {}; + const envPath = path.join(process.cwd(), ".env"); + if (!fs.existsSync(envPath)) return {}; - const envContent = fs.readFileSync(envPath, "utf8"); - const envVars: Record = {}; + const envContent = fs.readFileSync(envPath, "utf8"); + const envVars: Record = {}; - for (const line of envContent.split("\n")) { - const trimmed = line.trim(); - if (trimmed && !trimmed.startsWith("#")) { - const [key, ...valueParts] = trimmed.split("="); - if (key && valueParts.length > 0) { - const value = valueParts.join("=").replace(/^["']|["']$/g, ""); - envVars[key.trim()] = value.trim(); - } - } + for (const line of envContent.split("\n")) { + const trimmed = line.trim(); + if (trimmed && !trimmed.startsWith("#")) { + const [key, ...valueParts] = trimmed.split("="); + if (key && valueParts.length > 0) { + const value = valueParts.join("=").replace(/^["']|["']$/g, ""); + envVars[key.trim()] = value.trim(); + } } + } - return envVars; + return envVars; } diff --git a/create-db/src/regions.ts b/create-db/src/regions.ts index 7a0a5f6..d781a86 100644 --- a/create-db/src/regions.ts +++ b/create-db/src/regions.ts @@ -2,45 +2,48 @@ import pc from "picocolors"; import type { Region, RegionsResponse } from "./types.js"; export async function checkOnline(workerUrl: string): Promise { - try { - const res = await fetch(`${workerUrl}/health`); - if (!res.ok) throw new Error("API not available"); - } catch { - console.error( - pc.bold(pc.red("\nāœ– Error: Cannot reach Prisma Postgres API server.\n")) - ); - console.error( - pc.dim( - `Check your internet connection or visit ${pc.green("https://www.prisma-status.com/")}\n` - ) - ); - throw new Error("Cannot reach API server"); - } + try { + const res = await fetch(`${workerUrl}/health`); + if (!res.ok) throw new Error("API not available"); + } catch { + console.error( + pc.bold(pc.red("\nāœ– Error: Cannot reach Prisma Postgres API server.\n")) + ); + console.error( + pc.dim( + `Check your internet connection or visit ${pc.green("https://www.prisma-status.com/")}\n` + ) + ); + throw new Error("Cannot reach API server"); + } } export async function getRegions(workerUrl: string): Promise { - const res = await fetch(`${workerUrl}/regions`); + const res = await fetch(`${workerUrl}/regions`); - if (!res.ok) { - throw new Error( - `Failed to fetch regions. Status: ${res.status} ${res.statusText}` - ); - } + if (!res.ok) { + throw new Error( + `Failed to fetch regions. Status: ${res.status} ${res.statusText}` + ); + } - const data = (await res.json()) as RegionsResponse; - const regions: Region[] = Array.isArray(data) ? data : (data.data ?? []); - return regions.filter((region) => region.status === "available"); + const data = (await res.json()) as RegionsResponse; + const regions: Region[] = Array.isArray(data) ? data : (data.data ?? []); + return regions.filter((region) => region.status === "available"); } -export async function validateRegion(region: string, workerUrl: string): Promise { - const regions = await getRegions(workerUrl); - const regionIds = regions.map((r) => r.id); +export async function validateRegion( + region: string, + workerUrl: string +): Promise { + const regions = await getRegions(workerUrl); + const regionIds = regions.map((r) => r.id); - if (!regionIds.includes(region)) { - throw new Error( - `Invalid region: ${region}. Available regions: ${regionIds.join(", ")}` - ); - } + if (!regionIds.includes(region)) { + throw new Error( + `Invalid region: ${region}. Available regions: ${regionIds.join(", ")}` + ); + } - return region; + return region; } diff --git a/create-db/src/types.ts b/create-db/src/types.ts index 54f8b34..3654c0f 100644 --- a/create-db/src/types.ts +++ b/create-db/src/types.ts @@ -1,129 +1,129 @@ import z from "zod"; export const RegionSchema = z.enum([ - "ap-southeast-1", - "ap-northeast-1", - "eu-central-1", - "eu-west-3", - "us-east-1", - "us-west-1", + "ap-southeast-1", + "ap-northeast-1", + "eu-central-1", + "eu-west-3", + "us-east-1", + "us-west-1", ]); export type RegionId = z.infer; export interface UserLocation { - country: string; - continent: string; - city: string; - region: string; - latitude: number; - longitude: number; + country: string; + continent: string; + city: string; + region: string; + latitude: number; + longitude: number; } export interface PartialUserLocation { - latitude?: number | string; - longitude?: number | string; + latitude?: number | string; + longitude?: number | string; } export interface RegionCoordinates { - lat: number; - lng: number; + lat: number; + lng: number; } export interface Region { - id: string; - name?: string; - status: string; + id: string; + name?: string; + status: string; } export interface DatabaseResult { - success: true; - connectionString: string | null; - claimUrl: string; - deletionDate: string; - region: string; - name: string; - projectId: string; - userAgent?: string; + success: true; + connectionString: string | null; + claimUrl: string; + deletionDate: string; + region: string; + name: string; + projectId: string; + userAgent?: string; } export interface DatabaseError { - success: false; - error: string; - message: string; - raw?: string; - details?: unknown; - status?: number; + success: false; + error: string; + message: string; + raw?: string; + details?: unknown; + status?: number; } export type CreateDatabaseResult = DatabaseResult | DatabaseError; export function isDatabaseError( - result: CreateDatabaseResult + result: CreateDatabaseResult ): result is DatabaseError { - return !result.success; + return !result.success; } export function isDatabaseSuccess( - result: CreateDatabaseResult + result: CreateDatabaseResult ): result is DatabaseResult { - return result.success; + return result.success; } export interface ApiResponseData { - id?: string; - database?: DatabaseRecord; + id?: string; + database?: DatabaseRecord; } export interface DatabaseRecord { - name?: string; - region?: { - id?: string; - }; - apiKeys?: ApiKey[]; + name?: string; + region?: { + id?: string; + }; + apiKeys?: ApiKey[]; } export interface ApiKey { - directConnection?: ConnectionDetails; - ppgDirectConnection?: ConnectionDetails; + directConnection?: ConnectionDetails; + ppgDirectConnection?: ConnectionDetails; } export interface ConnectionDetails { - user?: string; - pass?: string; - host?: string; - port?: number | string; - database?: string; + user?: string; + pass?: string; + host?: string; + port?: number | string; + database?: string; } export interface ApiResponse { - data?: ApiResponseData; - databases?: DatabaseRecord[]; - id?: string; - error?: ApiErrorInfo; + data?: ApiResponseData; + databases?: DatabaseRecord[]; + id?: string; + error?: ApiErrorInfo; } export interface ApiErrorInfo { - message?: string; - status?: number; + message?: string; + status?: number; } export interface GeoLocationResponse { - country_code: string; - continent_code: string; - city: string; - region: string; - latitude: number; - longitude: number; + country_code: string; + continent_code: string; + city: string; + region: string; + latitude: number; + longitude: number; } export interface RegionsApiResponse { - data?: Region[]; + data?: Region[]; } export type RegionsResponse = Region[] | RegionsApiResponse; export interface ProgrammaticCreateOptions { - region?: RegionId; - userAgent?: string; + region?: RegionId; + userAgent?: string; } diff --git a/create-db/vitest.config.ts b/create-db/vitest.config.ts index 739d065..9243ae4 100644 --- a/create-db/vitest.config.ts +++ b/create-db/vitest.config.ts @@ -5,4 +5,3 @@ export default defineConfig({ include: ["__tests__/**/*.test.ts"], }, }); - From 3f1666fe8eac7943e43e27e424c00082d7e47785 Mon Sep 17 00:00:00 2001 From: Aidan McAlister Date: Tue, 9 Dec 2025 09:18:07 -0500 Subject: [PATCH 15/22] fix: remove geolocation test --- create-db/__tests__/geolocation.test.ts | 241 ------------------------ 1 file changed, 241 deletions(-) delete mode 100644 create-db/__tests__/geolocation.test.ts diff --git a/create-db/__tests__/geolocation.test.ts b/create-db/__tests__/geolocation.test.ts deleted file mode 100644 index 3c03ee7..0000000 --- a/create-db/__tests__/geolocation.test.ts +++ /dev/null @@ -1,241 +0,0 @@ -import { describe, it, expect } from "vitest"; -import { execa } from "execa"; -import { - calculateHaversineDistance, - getRegionClosestToLocation, - REGION_COORDINATES, -} from "../src/geolocation.js"; - -describe("calculateHaversineDistance", () => { - it("calculates distance between two points", () => { - // Distance from New York to Los Angeles (approximately 3944 km) - const distance = calculateHaversineDistance( - 40.7128, - -74.006, // New York - 34.0522, - -118.2437 // Los Angeles - ); - expect(distance).toBeGreaterThan(3900); - expect(distance).toBeLessThan(4000); - }); - - it("returns 0 for same location", () => { - const distance = calculateHaversineDistance( - 35.6762, - 139.6503, - 35.6762, - 139.6503 - ); - expect(distance).toBe(0); - }); -}); - -describe("getRegionClosestToLocation", () => { - it("selects ap-southeast-1 for Singapore coordinates", () => { - const region = getRegionClosestToLocation({ - latitude: 1.3521, - longitude: 103.8198, - }); - expect(region).toBe("ap-southeast-1"); - }); - - it("selects ap-northeast-1 for Tokyo coordinates", () => { - const region = getRegionClosestToLocation({ - latitude: 35.6762, - longitude: 139.6503, - }); - expect(region).toBe("ap-northeast-1"); - }); - - it("selects eu-central-1 for Frankfurt coordinates", () => { - const region = getRegionClosestToLocation({ - latitude: 50.1109, - longitude: 8.6821, - }); - expect(region).toBe("eu-central-1"); - }); - - it("selects eu-west-3 for Paris coordinates", () => { - const region = getRegionClosestToLocation({ - latitude: 48.8566, - longitude: 2.3522, - }); - expect(region).toBe("eu-west-3"); - }); - - it("selects us-east-1 for Virginia coordinates", () => { - const region = getRegionClosestToLocation({ - latitude: 38.9072, - longitude: -77.0369, - }); - expect(region).toBe("us-east-1"); - }); - - it("selects us-west-1 for California coordinates", () => { - const region = getRegionClosestToLocation({ - latitude: 37.7749, - longitude: -122.4194, - }); - expect(region).toBe("us-west-1"); - }); - - it("selects closest region for location between regions", () => { - // London coordinates - should be closest to eu-central-1 or eu-west-3 - const region = getRegionClosestToLocation({ - latitude: 51.5074, - longitude: -0.1278, - }); - expect(region).toMatch(/^eu-(central-1|west-3)$/); - }); - - it("handles string coordinates", () => { - const region = getRegionClosestToLocation({ - latitude: "35.6762", - longitude: "139.6503", - }); - expect(region).toBe("ap-northeast-1"); - }); - - it("returns null for invalid coordinates", () => { - expect(getRegionClosestToLocation(null)).toBe(null); - expect( - getRegionClosestToLocation({ latitude: undefined, longitude: undefined }) - ).toBe(null); - expect(getRegionClosestToLocation({ latitude: NaN, longitude: NaN })).toBe( - null - ); - expect( - getRegionClosestToLocation({ latitude: "invalid", longitude: "invalid" }) - ).toBe(null); - }); -}); - -describe("REGION_COORDINATES", () => { - it("contains all expected regions", () => { - const expectedRegions = [ - "ap-southeast-1", - "ap-northeast-1", - "eu-central-1", - "eu-west-3", - "us-east-1", - "us-west-1", - ]; - - for (const region of expectedRegions) { - expect(REGION_COORDINATES).toHaveProperty(region); - expect( - REGION_COORDINATES[region as keyof typeof REGION_COORDINATES] - ).toHaveProperty("lat"); - expect( - REGION_COORDINATES[region as keyof typeof REGION_COORDINATES] - ).toHaveProperty("lng"); - } - }); - - it("has valid coordinate values", () => { - for (const [regionId, coords] of Object.entries(REGION_COORDINATES)) { - expect(coords.lat).toBeGreaterThanOrEqual(-90); - expect(coords.lat).toBeLessThanOrEqual(90); - expect(coords.lng).toBeGreaterThanOrEqual(-180); - expect(coords.lng).toBeLessThanOrEqual(180); - } - }); -}); - -describe("CLI database creation with explicit regions", () => { - it("creates database in ap-southeast-1 (Singapore)", async () => { - const { stdout } = await execa("node", [ - "./dist/cli.mjs", - "create", - "--region", - "ap-southeast-1", - "--json", - ]); - const result = JSON.parse(stdout); - - expect(result.success).toBe(true); - expect(result.region).toBe("ap-southeast-1"); - expect(result.connectionString).toBeTruthy(); - expect(result.claimUrl).toBeTruthy(); - }, 30000); - - it("creates database in ap-northeast-1 (Tokyo)", async () => { - const { stdout } = await execa("node", [ - "./dist/cli.mjs", - "create", - "--region", - "ap-northeast-1", - "--json", - ]); - const result = JSON.parse(stdout); - - expect(result.success).toBe(true); - expect(result.region).toBe("ap-northeast-1"); - expect(result.connectionString).toBeTruthy(); - expect(result.claimUrl).toBeTruthy(); - }, 30000); - - it("creates database in eu-central-1 (Frankfurt)", async () => { - const { stdout } = await execa("node", [ - "./dist/cli.mjs", - "create", - "--region", - "eu-central-1", - "--json", - ]); - const result = JSON.parse(stdout); - - expect(result.success).toBe(true); - expect(result.region).toBe("eu-central-1"); - expect(result.connectionString).toBeTruthy(); - expect(result.claimUrl).toBeTruthy(); - }, 30000); - - it("creates database in eu-west-3 (Paris)", async () => { - const { stdout } = await execa("node", [ - "./dist/cli.mjs", - "create", - "--region", - "eu-west-3", - "--json", - ]); - const result = JSON.parse(stdout); - - expect(result.success).toBe(true); - expect(result.region).toBe("eu-west-3"); - expect(result.connectionString).toBeTruthy(); - expect(result.claimUrl).toBeTruthy(); - }, 30000); - - it("creates database in us-east-1 (Virginia)", async () => { - const { stdout } = await execa("node", [ - "./dist/cli.mjs", - "create", - "--region", - "us-east-1", - "--json", - ]); - const result = JSON.parse(stdout); - - expect(result.success).toBe(true); - expect(result.region).toBe("us-east-1"); - expect(result.connectionString).toBeTruthy(); - expect(result.claimUrl).toBeTruthy(); - }, 30000); - - it("creates database in us-west-1 (California)", async () => { - const { stdout } = await execa("node", [ - "./dist/cli.mjs", - "create", - "--region", - "us-west-1", - "--json", - ]); - const result = JSON.parse(stdout); - - expect(result.success).toBe(true); - expect(result.region).toBe("us-west-1"); - expect(result.connectionString).toBeTruthy(); - expect(result.claimUrl).toBeTruthy(); - }, 30000); -}); From 26472da480dddbb26f36eaa13f123a3ffa24ebd9 Mon Sep 17 00:00:00 2001 From: Aidan McAlister Date: Tue, 9 Dec 2025 09:37:58 -0500 Subject: [PATCH 16/22] chore: like file updated --- pnpm-lock.yaml | 420 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 370 insertions(+), 50 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 808c2d6..dc208e5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -37,7 +37,7 @@ importers: version: 1.6.4(wrangler@4.30.0) '@prisma/studio-core': specifier: ^0.5.2 - version: 0.5.2(@prisma/client@6.14.0(prisma@6.14.0(typescript@5.8.3))(typescript@5.8.3))(@types/react@19.1.10)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + version: 0.5.2(@prisma/client@6.14.0(prisma@7.1.0(@types/react@19.1.10)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)(typescript@5.8.3))(typescript@5.8.3))(@types/react@19.1.10)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) '@types/pg': specifier: ^8.15.5 version: 8.15.5 @@ -54,8 +54,8 @@ importers: specifier: ^1.282.0 version: 1.282.0 prisma: - specifier: ^6.14.0 - version: 6.14.0(typescript@5.8.3) + specifier: ^7.1.0 + version: 7.1.0(@types/react@19.1.10)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)(typescript@5.8.3) react: specifier: 19.1.2 version: 19.1.2 @@ -134,7 +134,7 @@ importers: version: 5.0.0 trpc-cli: specifier: ^0.12.1 - version: 0.12.1(@orpc/server@1.12.2(ws@8.18.3))(effect@3.16.12)(zod@4.1.13) + version: 0.12.1(@orpc/server@1.12.2(ws@8.18.3))(effect@3.18.4)(valibot@1.2.0(typescript@5.9.3))(zod@4.1.13) zod: specifier: ^4.1.13 version: 4.1.13 @@ -720,6 +720,18 @@ packages: '@changesets/write@0.4.0': resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} + '@chevrotain/cst-dts-gen@10.5.0': + resolution: {integrity: sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==} + + '@chevrotain/gast@10.5.0': + resolution: {integrity: sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==} + + '@chevrotain/types@10.5.0': + resolution: {integrity: sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A==} + + '@chevrotain/utils@10.5.0': + resolution: {integrity: sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==} + '@clack/core@0.5.0': resolution: {integrity: sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow==} @@ -952,6 +964,20 @@ packages: resolution: {integrity: sha512-0dEVyRLM/lG4gp1R/Ik5bfPl/1wX00xFwd5KcNH602tzBa09oF7pbTKETEhR1GjZ75K6OJnYFu8II2dyMhONMw==} engines: {node: '>=16'} + '@electric-sql/pglite-socket@0.0.6': + resolution: {integrity: sha512-6RjmgzphIHIBA4NrMGJsjNWK4pu+bCWJlEWlwcxFTVY3WT86dFpKwbZaGWZV6C5Rd7sCk1Z0CI76QEfukLAUXw==} + hasBin: true + peerDependencies: + '@electric-sql/pglite': 0.3.2 + + '@electric-sql/pglite-tools@0.2.7': + resolution: {integrity: sha512-9dAccClqxx4cZB+Ar9B+FZ5WgxDc/Xvl9DPrTWv+dYTf0YNubLzi4wHHRGRGhrJv15XwnyKcGOZAP1VXSneSUg==} + peerDependencies: + '@electric-sql/pglite': 0.3.2 + + '@electric-sql/pglite@0.3.2': + resolution: {integrity: sha512-zfWWa+V2ViDCY/cmUfRqeWY1yLto+EpxjXnZzenB1TyxsTiXaTWeZFIZw6mac52BsuQm0RjCnisjBtdBaXOI6w==} + '@emnapi/core@1.7.1': resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} @@ -1430,6 +1456,12 @@ packages: peerDependencies: hono: ^4 + '@hono/node-server@1.19.6': + resolution: {integrity: sha512-Shz/KjlIeAhfiuE93NDKVdZ7HdBVLQAfdbaXEaoAVO3ic9ibRSLGIQGkcBbFyuLr+7/1D5ZCINM8B+6IvXeMtw==} + engines: {node: '>=18.14.1'} + peerDependencies: + hono: ^4 + '@img/sharp-darwin-arm64@0.33.5': resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1722,6 +1754,10 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + '@mrleebo/prisma-ast@0.12.1': + resolution: {integrity: sha512-JwqeCQ1U3fvccttHZq7Tk0m/TMC6WcFAQZdukypW3AzlJYKYTGNVd1ANU2GuhKnv4UQuOFj3oAl0LLG/gxFN1w==} + engines: {node: '>=16'} + '@napi-rs/wasm-runtime@1.1.0': resolution: {integrity: sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==} @@ -1911,38 +1947,50 @@ packages: typescript: optional: true - '@prisma/config@6.14.0': - resolution: {integrity: sha512-IwC7o5KNNGhmblLs23swnfBjADkacBb7wvyDXUWLwuvUQciKJZqyecU0jw0d7JRkswrj+XTL8fdr0y2/VerKQQ==} + '@prisma/config@7.1.0': + resolution: {integrity: sha512-Uz+I43Wn1RYNHtuYtOhOnUcNMWp2Pd3GUDDKs37xlHptCGpzEG3MRR9L+8Y2ISMsMI24z/Ni+ww6OB/OO8M0sQ==} '@prisma/debug@5.22.0': resolution: {integrity: sha512-AUt44v3YJeggO2ZU5BkXI7M4hu9BF2zzH2iF2V5pyXT/lRTyWiElZ7It+bRH1EshoMRxHgpYg4VB6rCM+mG5jQ==} - '@prisma/debug@6.14.0': - resolution: {integrity: sha512-j4Lf+y+5QIJgQD4sJWSbkOD7geKx9CakaLp/TyTy/UDu9Wo0awvWCBH/BAxTHUaCpIl9USA5VS/KJhDqKJSwug==} + '@prisma/debug@6.8.2': + resolution: {integrity: sha512-4muBSSUwJJ9BYth5N8tqts8JtiLT8QI/RSAzEogwEfpbYGFo9mYsInsVo8dqXdPO2+Rm5OG5q0qWDDE3nyUbVg==} + + '@prisma/debug@7.1.0': + resolution: {integrity: sha512-pPAckG6etgAsEBusmZiFwM9bldLSNkn++YuC4jCTJACdK5hLOVnOzX7eSL2FgaU6Gomd6wIw21snUX2dYroMZQ==} + + '@prisma/dev@0.15.0': + resolution: {integrity: sha512-KhWaipnFlS/fWEs6I6Oqjcy2S08vKGmxJ5LexqUl/3Ve0EgLUsZwdKF0MvqPM5F5ttw8GtfZarjM5y7VLwv9Ow==} '@prisma/engines-version@5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2': resolution: {integrity: sha512-2PTmxFR2yHW/eB3uqWtcgRcgAbG1rwG9ZriSvQw+nnb7c4uCr3RAcGMb6/zfE88SKlC1Nj2ziUvc96Z379mHgQ==} - '@prisma/engines-version@6.14.0-25.717184b7b35ea05dfa71a3236b7af656013e1e49': - resolution: {integrity: sha512-EgN9ODJpiX45yvwcngoStp3uQPJ3l+AEVoQ6dMMO2QvmwIlnxfApzKmJQExzdo7/hqQANrz5txHJdGYHzOnGHA==} + '@prisma/engines-version@7.1.0-6.ab635e6b9d606fa5c8fb8b1a7f909c3c3c1c98ba': + resolution: {integrity: sha512-qZUevUh+yPhGT28rDQnV8V2kLnFjirzhVD67elRPIJHRsUV/mkII10HSrJrhK/U2GYgAxXR2VEREtq7AsfS8qw==} '@prisma/engines@5.22.0': resolution: {integrity: sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==} - '@prisma/engines@6.14.0': - resolution: {integrity: sha512-LhJjqsALFEcoAtF07nSaOkVguaxw/ZsgfROIYZ8bAZDobe7y8Wy+PkYQaPOK1iLSsFgV2MhCO/eNrI1gdSOj6w==} + '@prisma/engines@7.1.0': + resolution: {integrity: sha512-KQlraOybdHAzVv45KWKJzpR9mJLkib7/TyApQpqrsL7FUHfgjIcy8jrVGt3iNfG6/GDDl+LNlJ84JSQwIfdzxA==} '@prisma/fetch-engine@5.22.0': resolution: {integrity: sha512-bkrD/Mc2fSvkQBV5EpoFcZ87AvOgDxbG99488a5cexp5Ccny+UM6MAe/UFkUC0wLYD9+9befNOqGiIJhhq+HbA==} - '@prisma/fetch-engine@6.14.0': - resolution: {integrity: sha512-MPzYPOKMENYOaY3AcAbaKrfvXVlvTc6iHmTXsp9RiwCX+bPyfDMqMFVUSVXPYrXnrvEzhGHfyiFy0PRLHPysNg==} + '@prisma/fetch-engine@7.1.0': + resolution: {integrity: sha512-GZYF5Q8kweXWGfn87hTu17kw7x1DgnehgKoE4Zg1BmHYF3y1Uu0QRY/qtSE4veH3g+LW8f9HKqA0tARG66bxxQ==} '@prisma/get-platform@5.22.0': resolution: {integrity: sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==} - '@prisma/get-platform@6.14.0': - resolution: {integrity: sha512-7VjuxKNwjnBhKfqPpMeWiHEa2sVjYzmHdl1slW6STuUCe9QnOY0OY1ljGSvz6wpG4U8DfbDqkG1yofd/1GINww==} + '@prisma/get-platform@6.8.2': + resolution: {integrity: sha512-vXSxyUgX3vm1Q70QwzwkjeYfRryIvKno1SXbIqwSptKwqKzskINnDUcx85oX+ys6ooN2ATGSD0xN2UTfg6Zcow==} + + '@prisma/get-platform@7.1.0': + resolution: {integrity: sha512-lq8hMdjKiZftuT5SssYB3EtQj8+YjL24/ZTLflQqzFquArKxBcyp6Xrblto+4lzIKJqnpOjfMiBjMvl7YuD7+Q==} + + '@prisma/query-plan-executor@6.18.0': + resolution: {integrity: sha512-jZ8cfzFgL0jReE1R10gT8JLHtQxjWYLiQ//wHmVYZ2rVkFHoh0DT8IXsxcKcFlfKN7ak7k6j0XMNn2xVNyr5cA==} '@prisma/studio-core@0.5.2': resolution: {integrity: sha512-F/LOafCIfNkSlCdWvg/1JW4tj+RAePP7/WaSLFWDR8TFXVRy0AhjRhzTmcc+sB+pG6B2LEH04yagVSyjXQ31yg==} @@ -1952,6 +2000,13 @@ packages: react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 + '@prisma/studio-core@0.8.2': + resolution: {integrity: sha512-/iAEWEUpTja+7gVMu1LtR2pPlvDmveAwMHdTWbDeGlT7yiv0ZTCPpmeAGdq/Y9aJ9Zj1cEGBXGRbmmNPj022PQ==} + peerDependencies: + '@types/react': ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + '@quansync/fs@0.1.5': resolution: {integrity: sha512-lNS9hL2aS2NZgNW7BBj+6EBl4rOf8l+tQ0eRY6JWCI8jI2kc53gSoqbjojU0OnAWhzoXiOjFyGsHcDGePB3lhA==} @@ -3105,6 +3160,10 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + aws-ssl-profiles@1.1.2: + resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==} + engines: {node: '>= 6.0.0'} + aws4fetch@1.0.20: resolution: {integrity: sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g==} @@ -3219,6 +3278,9 @@ packages: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} + chevrotain@10.5.0: + resolution: {integrity: sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A==} + chokidar@4.0.0: resolution: {integrity: sha512-mxIojEAQcuEvT/lyXq+jf/3cO/KoA6z4CeNDGGevTybECPOMFCnQy3OPahluUkbqgPNGw5Bi78UC7Po6Lhy+NA==} engines: {node: '>= 14.16.0'} @@ -3452,6 +3514,10 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + depd@1.1.2: resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} engines: {node: '>= 0.6'} @@ -3535,8 +3601,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - effect@3.16.12: - resolution: {integrity: sha512-N39iBk0K71F9nb442TLbTkjl24FLUzuvx2i1I2RsEAQsdAdUTuUoW0vlfUXgkMTUOnYqKnWcFfqw4hK4Pw27hg==} + effect@3.18.4: + resolution: {integrity: sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==} electron-to-chromium@1.5.218: resolution: {integrity: sha512-uwwdN0TUHs8u6iRgN8vKeWZMRll4gBkz+QMqdS7DDe49uiK68/UX92lFb61oiFPrpYZNeZIqa4bA7O6Aiasnzg==} @@ -3935,6 +4001,9 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + generate-function@2.3.1: + resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==} + generic-pool@3.4.2: resolution: {integrity: sha512-H7cUpwCQSiJmAHM4c/aFu6fUfrhWXW1ncyh8ftxEPMu6AiYkHw9K8br720TGPZJbk5eOH2bynjZD1yPvdDAmag==} engines: {node: '>= 4'} @@ -3955,6 +4024,9 @@ packages: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} + get-port-please@3.1.2: + resolution: {integrity: sha512-Gxc29eLs1fbn6LQ4jSU4vXjlwyZhF5HsGuMAa7gqBP4Rw4yxxltyDUuF5MBclFzDTXO+ACchGQoeela4DSfzdQ==} + get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} @@ -4028,6 +4100,9 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + grammex@3.1.12: + resolution: {integrity: sha512-6ufJOsSA7LcQehIJNCO7HIBykfM7DXQual0Ny780/DEcJIpBlHRvcqEBWGPYd7hrXL2GJ3oJI1MIhaXjWmLQOQ==} + gzip-size@6.0.0: resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==} engines: {node: '>=10'} @@ -4048,6 +4123,10 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + hono@4.10.6: + resolution: {integrity: sha512-BIdolzGpDO9MQ4nu3AUuDwHZZ+KViNm+EZ75Ae55eMXMqLVhDFqEMXxtUe9Qh8hjL+pIna/frs2j6Y2yD5Ua/g==} + engines: {node: '>=16.9.0'} + hono@4.9.4: resolution: {integrity: sha512-61hl6MF6ojTl/8QSRu5ran6GXt+6zsngIUN95KzF5v5UjiX/xnrLR358BNRawwIRO49JwUqJqQe3Rb2v559R8Q==} engines: {node: '>=16.9.0'} @@ -4075,6 +4154,9 @@ packages: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} + http-status-codes@2.3.0: + resolution: {integrity: sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==} + https-proxy-agent@7.0.6: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} @@ -4107,6 +4189,10 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} + iconv-lite@0.7.0: + resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} + engines: {node: '>=0.10.0'} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -4183,6 +4269,9 @@ packages: is-promise@4.0.0: resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-property@1.0.2: + resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} + is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -4359,6 +4448,10 @@ packages: resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} engines: {node: '>= 12.0.0'} + lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -4397,6 +4490,12 @@ packages: lodash.upperfirst@4.3.1: resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + loupe@3.1.4: resolution: {integrity: sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==} @@ -4414,6 +4513,10 @@ packages: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} + lru.min@1.1.3: + resolution: {integrity: sha512-Lkk/vx6ak3rYkRR0Nhu4lFUT2VDnQSxBe8Hbl7f36358p6ow8Bnvr8lrLt98H8J1aGxfhbX4Fs5tYg2+FTwr5Q==} + engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'} + lucide-react@0.541.0: resolution: {integrity: sha512-s0Vircsu5WaGv2KoJZ5+SoxiAJ3UXV5KqEM3eIFDHaHkcLIFdIWgXtZ412+Gh02UsdS7Was+jvEpBvPCWQISlg==} peerDependencies: @@ -4589,6 +4692,14 @@ packages: resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} hasBin: true + mysql2@3.15.3: + resolution: {integrity: sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==} + engines: {node: '>= 8.0'} + + named-placeholders@1.1.4: + resolution: {integrity: sha512-/qfG0Kk/bLJIvej4FcPQ2KYUJP8iQdU1CTxysNb/U2wUNb+/4K485yeio8iNoiwfqJnsTInXoRPTza0dZWHVJQ==} + engines: {node: '>=8.0.0'} + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -4925,6 +5036,10 @@ packages: resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} engines: {node: '>=0.10.0'} + postgres@3.4.7: + resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==} + engines: {node: '>=12'} + posthog-js@1.282.0: resolution: {integrity: sha512-kx7GyVILxR+Ty4SDA5yRpiIZ73yBZht7ZecnvqqOspqiMnzyoTgdBPn2NELMR0OrhjnfU2KhLkXwooUHmD0MAA==} @@ -4961,19 +5076,25 @@ packages: engines: {node: '>=16.13'} hasBin: true - prisma@6.14.0: - resolution: {integrity: sha512-QEuCwxu+Uq9BffFw7in8In+WfbSUN0ewnaSUKloLkbJd42w6EyFckux4M0f7VwwHlM3A8ssaz4OyniCXlsn0WA==} - engines: {node: '>=18.18'} + prisma@7.1.0: + resolution: {integrity: sha512-dy/3urE4JjhdiW5b09pGjVhGI7kPESK2VlCDrCqeYK5m5SslAtG5FCGnZWP7E8Sdg+Ow1wV2mhJH5RTFL5gEsw==} + engines: {node: ^20.19 || ^22.12 || >=24.0} hasBin: true peerDependencies: - typescript: '>=5.1.0' + better-sqlite3: '>=9.0.0' + typescript: '>=5.4.0' peerDependenciesMeta: + better-sqlite3: + optional: true typescript: optional: true promisepipe@3.0.0: resolution: {integrity: sha512-V6TbZDJ/ZswevgkDNpGt/YqNCiZP9ASfgU+p83uJE6NrGtvSGoOcHLiDCqkMs2+yg7F5qHdLV8d0aS8O26G/KA==} + proper-lockfile@4.1.2: + resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} + proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -5056,6 +5177,12 @@ packages: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} + regexp-to-ast@0.5.0: + resolution: {integrity: sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==} + + remeda@2.21.3: + resolution: {integrity: sha512-XXrZdLA10oEOQhLLzEJEiFFSKi21REGAkHdImIb4rt/XXy8ORGXh5HCcpUOsElfPNDb+X6TA/+wkh+p2KffYmg==} + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -5075,6 +5202,10 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + retry@0.13.1: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} engines: {node: '>= 4'} @@ -5163,6 +5294,9 @@ packages: resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} engines: {node: '>= 18'} + seq-queue@0.0.5: + resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==} + serve-static@2.2.0: resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} engines: {node: '>= 18'} @@ -5254,6 +5388,10 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + sqlstring@2.3.3: + resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} + engines: {node: '>= 0.6'} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -5579,6 +5717,10 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + type-fest@5.3.0: resolution: {integrity: sha512-d9CwU93nN0IA1QL+GSNDdwLAu1Ew5ZjTwupvedwg3WdfoH6pIDvYQ2hV0Uc2nKBLPq7NB5apCx57MLS5qlmO5g==} engines: {node: '>=20'} @@ -5691,6 +5833,14 @@ packages: v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + valibot@1.2.0: + resolution: {integrity: sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==} + peerDependencies: + typescript: '>=5' + peerDependenciesMeta: + typescript: + optional: true + vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} @@ -6061,6 +6211,9 @@ packages: youch@4.1.0-beta.10: resolution: {integrity: sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==} + zeptomatch@2.0.2: + resolution: {integrity: sha512-H33jtSKf8Ijtb5BW6wua3G5DhnFjbFML36eFu+VdOoVY4HD9e7ggjqdM6639B+L87rjnR6Y+XeRzBXZdy52B/g==} + zod@3.22.3: resolution: {integrity: sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==} @@ -7377,6 +7530,21 @@ snapshots: human-id: 4.1.1 prettier: 2.8.8 + '@chevrotain/cst-dts-gen@10.5.0': + dependencies: + '@chevrotain/gast': 10.5.0 + '@chevrotain/types': 10.5.0 + lodash: 4.17.21 + + '@chevrotain/gast@10.5.0': + dependencies: + '@chevrotain/types': 10.5.0 + lodash: 4.17.21 + + '@chevrotain/types@10.5.0': {} + + '@chevrotain/utils@10.5.0': {} + '@clack/core@0.5.0': dependencies: picocolors: 1.1.1 @@ -7617,6 +7785,16 @@ snapshots: dependencies: '@edge-runtime/primitives': 4.1.0 + '@electric-sql/pglite-socket@0.0.6(@electric-sql/pglite@0.3.2)': + dependencies: + '@electric-sql/pglite': 0.3.2 + + '@electric-sql/pglite-tools@0.2.7(@electric-sql/pglite@0.3.2)': + dependencies: + '@electric-sql/pglite': 0.3.2 + + '@electric-sql/pglite@0.3.2': {} + '@emnapi/core@1.7.1': dependencies: '@emnapi/wasi-threads': 1.1.0 @@ -7872,6 +8050,10 @@ snapshots: dependencies: hono: 4.9.4 + '@hono/node-server@1.19.6(hono@4.10.6)': + dependencies: + hono: 4.10.6 + '@img/sharp-darwin-arm64@0.33.5': optionalDependencies: '@img/sharp-libvips-darwin-arm64': 1.0.4 @@ -8128,6 +8310,11 @@ snapshots: react: 19.1.2 react-dom: 19.1.2(react@19.1.2) + '@mrleebo/prisma-ast@0.12.1': + dependencies: + chevrotain: 10.5.0 + lilconfig: 2.1.0 + '@napi-rs/wasm-runtime@1.1.0': dependencies: '@emnapi/core': 1.7.1 @@ -8346,27 +8533,51 @@ snapshots: '@posthog/core@1.4.0': {} - '@prisma/client@6.14.0(prisma@6.14.0(typescript@5.8.3))(typescript@5.8.3)': + '@prisma/client@6.14.0(prisma@7.1.0(@types/react@19.1.10)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)(typescript@5.8.3))(typescript@5.8.3)': optionalDependencies: - prisma: 6.14.0(typescript@5.8.3) + prisma: 7.1.0(@types/react@19.1.10)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)(typescript@5.8.3) typescript: 5.8.3 - '@prisma/config@6.14.0': + '@prisma/config@7.1.0': dependencies: c12: 3.1.0 deepmerge-ts: 7.1.5 - effect: 3.16.12 + effect: 3.18.4 empathic: 2.0.0 transitivePeerDependencies: - magicast '@prisma/debug@5.22.0': {} - '@prisma/debug@6.14.0': {} + '@prisma/debug@6.8.2': {} + + '@prisma/debug@7.1.0': {} + + '@prisma/dev@0.15.0(typescript@5.8.3)': + dependencies: + '@electric-sql/pglite': 0.3.2 + '@electric-sql/pglite-socket': 0.0.6(@electric-sql/pglite@0.3.2) + '@electric-sql/pglite-tools': 0.2.7(@electric-sql/pglite@0.3.2) + '@hono/node-server': 1.19.6(hono@4.10.6) + '@mrleebo/prisma-ast': 0.12.1 + '@prisma/get-platform': 6.8.2 + '@prisma/query-plan-executor': 6.18.0 + foreground-child: 3.3.1 + get-port-please: 3.1.2 + hono: 4.10.6 + http-status-codes: 2.3.0 + pathe: 2.0.3 + proper-lockfile: 4.1.2 + remeda: 2.21.3 + std-env: 3.9.0 + valibot: 1.2.0(typescript@5.8.3) + zeptomatch: 2.0.2 + transitivePeerDependencies: + - typescript '@prisma/engines-version@5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2': {} - '@prisma/engines-version@6.14.0-25.717184b7b35ea05dfa71a3236b7af656013e1e49': {} + '@prisma/engines-version@7.1.0-6.ab635e6b9d606fa5c8fb8b1a7f909c3c3c1c98ba': {} '@prisma/engines@5.22.0': dependencies: @@ -8375,12 +8586,12 @@ snapshots: '@prisma/fetch-engine': 5.22.0 '@prisma/get-platform': 5.22.0 - '@prisma/engines@6.14.0': + '@prisma/engines@7.1.0': dependencies: - '@prisma/debug': 6.14.0 - '@prisma/engines-version': 6.14.0-25.717184b7b35ea05dfa71a3236b7af656013e1e49 - '@prisma/fetch-engine': 6.14.0 - '@prisma/get-platform': 6.14.0 + '@prisma/debug': 7.1.0 + '@prisma/engines-version': 7.1.0-6.ab635e6b9d606fa5c8fb8b1a7f909c3c3c1c98ba + '@prisma/fetch-engine': 7.1.0 + '@prisma/get-platform': 7.1.0 '@prisma/fetch-engine@5.22.0': dependencies: @@ -8388,23 +8599,35 @@ snapshots: '@prisma/engines-version': 5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2 '@prisma/get-platform': 5.22.0 - '@prisma/fetch-engine@6.14.0': + '@prisma/fetch-engine@7.1.0': dependencies: - '@prisma/debug': 6.14.0 - '@prisma/engines-version': 6.14.0-25.717184b7b35ea05dfa71a3236b7af656013e1e49 - '@prisma/get-platform': 6.14.0 + '@prisma/debug': 7.1.0 + '@prisma/engines-version': 7.1.0-6.ab635e6b9d606fa5c8fb8b1a7f909c3c3c1c98ba + '@prisma/get-platform': 7.1.0 '@prisma/get-platform@5.22.0': dependencies: '@prisma/debug': 5.22.0 - '@prisma/get-platform@6.14.0': + '@prisma/get-platform@6.8.2': + dependencies: + '@prisma/debug': 6.8.2 + + '@prisma/get-platform@7.1.0': dependencies: - '@prisma/debug': 6.14.0 + '@prisma/debug': 7.1.0 + + '@prisma/query-plan-executor@6.18.0': {} - '@prisma/studio-core@0.5.2(@prisma/client@6.14.0(prisma@6.14.0(typescript@5.8.3))(typescript@5.8.3))(@types/react@19.1.10)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)': + '@prisma/studio-core@0.5.2(@prisma/client@6.14.0(prisma@7.1.0(@types/react@19.1.10)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)(typescript@5.8.3))(typescript@5.8.3))(@types/react@19.1.10)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)': + dependencies: + '@prisma/client': 6.14.0(prisma@7.1.0(@types/react@19.1.10)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)(typescript@5.8.3))(typescript@5.8.3) + '@types/react': 19.1.10 + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) + + '@prisma/studio-core@0.8.2(@types/react@19.1.10)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)': dependencies: - '@prisma/client': 6.14.0(prisma@6.14.0(typescript@5.8.3))(typescript@5.8.3) '@types/react': 19.1.10 react: 19.1.2 react-dom: 19.1.2(react@19.1.2) @@ -9782,6 +10005,8 @@ snapshots: asynckit@0.4.0: {} + aws-ssl-profiles@1.1.2: {} + aws4fetch@1.0.20: {} balanced-match@1.0.2: {} @@ -9859,7 +10084,7 @@ snapshots: dotenv: 16.6.1 exsolve: 1.0.7 giget: 2.0.0 - jiti: 2.5.1 + jiti: 2.6.1 ohash: 2.0.11 pathe: 2.0.3 perfect-debounce: 1.0.0 @@ -9900,6 +10125,15 @@ snapshots: check-error@2.1.1: {} + chevrotain@10.5.0: + dependencies: + '@chevrotain/cst-dts-gen': 10.5.0 + '@chevrotain/gast': 10.5.0 + '@chevrotain/types': 10.5.0 + '@chevrotain/utils': 10.5.0 + lodash: 4.17.21 + regexp-to-ast: 0.5.0 + chokidar@4.0.0: dependencies: readdirp: 4.1.2 @@ -10096,6 +10330,8 @@ snapshots: delayed-stream@1.0.0: {} + denque@2.1.0: {} + depd@1.1.2: {} depd@2.0.0: {} @@ -10161,7 +10397,7 @@ snapshots: ee-first@1.1.1: {} - effect@3.16.12: + effect@3.18.4: dependencies: '@standard-schema/spec': 1.0.0 fast-check: 3.23.2 @@ -10622,6 +10858,10 @@ snapshots: function-bind@1.1.2: {} + generate-function@2.3.1: + dependencies: + is-property: 1.0.2 + generic-pool@3.4.2: {} gensync@1.0.0-beta.2: {} @@ -10643,6 +10883,8 @@ snapshots: hasown: 2.0.2 math-intrinsics: 1.1.0 + get-port-please@3.1.2: {} + get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 @@ -10737,6 +10979,8 @@ snapshots: graceful-fs@4.2.11: {} + grammex@3.1.12: {} + gzip-size@6.0.0: dependencies: duplexer: 0.1.2 @@ -10753,6 +10997,8 @@ snapshots: dependencies: function-bind: 1.1.2 + hono@4.10.6: {} + hono@4.9.4: {} hookable@5.5.3: {} @@ -10789,6 +11035,8 @@ snapshots: transitivePeerDependencies: - supports-color + http-status-codes@2.3.0: {} + https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 @@ -10816,6 +11064,10 @@ snapshots: dependencies: safer-buffer: 2.1.2 + iconv-lite@0.7.0: + dependencies: + safer-buffer: 2.1.2 + ignore@5.3.2: {} import-fresh@3.3.1: @@ -10863,6 +11115,8 @@ snapshots: is-promise@4.0.0: {} + is-property@1.0.2: {} + is-stream@2.0.1: {} is-stream@4.0.1: {} @@ -10899,8 +11153,7 @@ snapshots: jiti@2.5.1: {} - jiti@2.6.1: - optional: true + jiti@2.6.1: {} jose@5.9.6: {} @@ -11017,6 +11270,8 @@ snapshots: lightningcss-win32-arm64-msvc: 1.30.1 lightningcss-win32-x64-msvc: 1.30.1 + lilconfig@2.1.0: {} + lines-and-columns@1.2.4: {} locate-path@5.0.0: @@ -11045,6 +11300,10 @@ snapshots: lodash.upperfirst@4.3.1: {} + lodash@4.17.21: {} + + long@5.3.2: {} + loupe@3.1.4: {} lru-cache@10.4.3: {} @@ -11059,6 +11318,8 @@ snapshots: dependencies: yallist: 4.0.0 + lru.min@1.1.3: {} + lucide-react@0.541.0(react@19.1.2): dependencies: react: 19.1.2 @@ -11215,6 +11476,22 @@ snapshots: mustache@4.2.0: {} + mysql2@3.15.3: + dependencies: + aws-ssl-profiles: 1.1.2 + denque: 2.1.0 + generate-function: 2.3.1 + iconv-lite: 0.7.0 + long: 5.3.2 + lru.min: 1.1.3 + named-placeholders: 1.1.4 + seq-queue: 0.0.5 + sqlstring: 2.3.3 + + named-placeholders@1.1.4: + dependencies: + lru.min: 1.1.3 + nanoid@3.3.11: {} negotiator@1.0.0: {} @@ -11488,6 +11765,8 @@ snapshots: dependencies: xtend: 4.0.2 + postgres@3.4.7: {} + posthog-js@1.282.0: dependencies: '@posthog/core': 1.4.0 @@ -11524,17 +11803,30 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - prisma@6.14.0(typescript@5.8.3): + prisma@7.1.0(@types/react@19.1.10)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)(typescript@5.8.3): dependencies: - '@prisma/config': 6.14.0 - '@prisma/engines': 6.14.0 + '@prisma/config': 7.1.0 + '@prisma/dev': 0.15.0(typescript@5.8.3) + '@prisma/engines': 7.1.0 + '@prisma/studio-core': 0.8.2(@types/react@19.1.10)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + mysql2: 3.15.3 + postgres: 3.4.7 optionalDependencies: typescript: 5.8.3 transitivePeerDependencies: + - '@types/react' - magicast + - react + - react-dom promisepipe@3.0.0: {} + proper-lockfile@4.1.2: + dependencies: + graceful-fs: 4.2.11 + retry: 0.12.0 + signal-exit: 3.0.7 + proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 @@ -11613,6 +11905,12 @@ snapshots: indent-string: 4.0.0 strip-indent: 3.0.0 + regexp-to-ast@0.5.0: {} + + remeda@2.21.3: + dependencies: + type-fest: 4.41.0 + require-directory@2.1.1: {} require-from-string@2.0.2: {} @@ -11623,6 +11921,8 @@ snapshots: resolve-pkg-maps@1.0.0: {} + retry@0.12.0: {} + retry@0.13.1: {} reusify@1.1.0: {} @@ -11770,6 +12070,8 @@ snapshots: transitivePeerDependencies: - supports-color + seq-queue@0.0.5: {} + serve-static@2.2.0: dependencies: encodeurl: 2.0.0 @@ -11913,6 +12215,8 @@ snapshots: sprintf-js@1.0.3: {} + sqlstring@2.3.3: {} + stackback@0.0.2: {} stacktracey@2.1.8: @@ -12110,12 +12414,13 @@ snapshots: tree-kill@1.2.2: {} - trpc-cli@0.12.1(@orpc/server@1.12.2(ws@8.18.3))(effect@3.16.12)(zod@4.1.13): + trpc-cli@0.12.1(@orpc/server@1.12.2(ws@8.18.3))(effect@3.18.4)(valibot@1.2.0(typescript@5.9.3))(zod@4.1.13): dependencies: commander: 14.0.2 optionalDependencies: '@orpc/server': 1.12.2(ws@8.18.3) - effect: 3.16.12 + effect: 3.18.4 + valibot: 1.2.0(typescript@5.9.3) zod: 4.1.13 ts-morph@12.0.0: @@ -12185,6 +12490,8 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + type-fest@4.41.0: {} + type-fest@5.3.0: dependencies: tagged-tag: 1.0.0 @@ -12275,6 +12582,15 @@ snapshots: v8-compile-cache-lib@3.0.1: {} + valibot@1.2.0(typescript@5.8.3): + optionalDependencies: + typescript: 5.8.3 + + valibot@1.2.0(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + optional: true + vary@1.1.2: {} vercel@46.0.2(rollup@4.53.3): @@ -12751,6 +13067,10 @@ snapshots: cookie: 1.1.1 youch-core: 0.3.3 + zeptomatch@2.0.2: + dependencies: + grammex: 3.1.12 + zod@3.22.3: {} zod@3.25.76: {} From c0263cda17ab104c51524dc325ed6af928bc77a9 Mon Sep 17 00:00:00 2001 From: Aidan McAlister Date: Tue, 9 Dec 2025 09:47:12 -0500 Subject: [PATCH 17/22] fix: reset account id --- claim-db-worker/wrangler.jsonc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/claim-db-worker/wrangler.jsonc b/claim-db-worker/wrangler.jsonc index 285911f..e723ff7 100644 --- a/claim-db-worker/wrangler.jsonc +++ b/claim-db-worker/wrangler.jsonc @@ -2,7 +2,7 @@ "$schema": "node_modules/wrangler/config-schema.json", "name": "claim-db-worker", "main": ".open-next/worker.js", - "account_id": "0ef7f922ce028e16c1a44d98c86511b0", + "account_id": "16b32bbb36161aca01a6357a37bc453e", "compatibility_date": "2025-08-13", "compatibility_flags": ["nodejs_compat", "global_fetch_strictly_public"], "observability": { From dc2e303d4a018249cc04a87a92b4a16ecf390d9b Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Wed, 10 Dec 2025 17:20:01 +0530 Subject: [PATCH 18/22] fix: entry points for both alias clis --- create-pg/cli.js | 2 ++ create-pg/index.js | 4 +--- create-pg/package.json | 7 +++---- create-postgres/cli.js | 2 ++ create-postgres/index.js | 4 +--- create-postgres/package.json | 9 ++++----- 6 files changed, 13 insertions(+), 15 deletions(-) create mode 100644 create-pg/cli.js create mode 100644 create-postgres/cli.js diff --git a/create-pg/cli.js b/create-pg/cli.js new file mode 100644 index 0000000..979e3ce --- /dev/null +++ b/create-pg/cli.js @@ -0,0 +1,2 @@ +#!/usr/bin/env node +import("create-db/cli"); diff --git a/create-pg/index.js b/create-pg/index.js index 92c25fb..a43dddf 100755 --- a/create-pg/index.js +++ b/create-pg/index.js @@ -1,3 +1 @@ -#!/usr/bin/env node -import { main } from "create-db"; -main(); +export * from "create-db"; diff --git a/create-pg/package.json b/create-pg/package.json index ef1f379..81f2496 100644 --- a/create-pg/package.json +++ b/create-pg/package.json @@ -2,7 +2,6 @@ "name": "create-pg", "version": "1.1.0", "description": "Instantly create a temporary Prisma Postgres database with one command, then claim and persist it in your Prisma Data Platform project when ready.", - "main": "index.js", "author": "prisma", "repository": { "type": "git", @@ -22,7 +21,7 @@ "license": "ISC", "type": "module", "bin": { - "create-pg": "./index.js" + "create-pg": "./cli.js" }, "dependencies": { "create-db": "workspace:*" @@ -31,7 +30,7 @@ "access": "public" }, "files": [ - "index.js", - "README.md" + "cli.js", + "index.js" ] } diff --git a/create-postgres/cli.js b/create-postgres/cli.js new file mode 100644 index 0000000..979e3ce --- /dev/null +++ b/create-postgres/cli.js @@ -0,0 +1,2 @@ +#!/usr/bin/env node +import("create-db/cli"); diff --git a/create-postgres/index.js b/create-postgres/index.js index 92c25fb..a43dddf 100644 --- a/create-postgres/index.js +++ b/create-postgres/index.js @@ -1,3 +1 @@ -#!/usr/bin/env node -import { main } from "create-db"; -main(); +export * from "create-db"; diff --git a/create-postgres/package.json b/create-postgres/package.json index fc015e9..24eb423 100644 --- a/create-postgres/package.json +++ b/create-postgres/package.json @@ -2,7 +2,6 @@ "name": "create-postgres", "version": "1.1.0", "description": "Instantly create a temporary Prisma Postgres database with one command, then claim and persist it in your Prisma Data Platform project when ready.", - "main": "index.js", "author": "", "repository": { "type": "git", @@ -22,7 +21,7 @@ "license": "ISC", "type": "module", "bin": { - "create-postgres": "./index.js" + "create-postgres": "./cli.js" }, "dependencies": { "create-db": "workspace:*" @@ -31,7 +30,7 @@ "access": "public" }, "files": [ - "index.js", - "README.md" + "cli.js", + "index.js" ] -} +} \ No newline at end of file From a1e549e6069cfbf9fce200ef16f65d5703877be4 Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Wed, 10 Dec 2025 17:27:49 +0530 Subject: [PATCH 19/22] chore: test --- create-pg/index.d.ts | 2 ++ create-pg/package.json | 17 +++++++++++++++-- create-postgres/index.d.ts | 2 ++ create-postgres/package.json | 17 +++++++++++++++-- 4 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 create-pg/index.d.ts create mode 100644 create-postgres/index.d.ts diff --git a/create-pg/index.d.ts b/create-pg/index.d.ts new file mode 100644 index 0000000..afcee8f --- /dev/null +++ b/create-pg/index.d.ts @@ -0,0 +1,2 @@ +export * from "create-db"; + diff --git a/create-pg/package.json b/create-pg/package.json index 81f2496..a6a04f9 100644 --- a/create-pg/package.json +++ b/create-pg/package.json @@ -20,6 +20,18 @@ ], "license": "ISC", "type": "module", + "main": "./index.js", + "module": "./index.js", + "types": "./index.d.ts", + "exports": { + ".": { + "types": "./index.d.ts", + "import": "./index.js" + }, + "./cli": { + "import": "./cli.js" + } + }, "bin": { "create-pg": "./cli.js" }, @@ -31,6 +43,7 @@ }, "files": [ "cli.js", - "index.js" + "index.js", + "index.d.ts" ] -} +} \ No newline at end of file diff --git a/create-postgres/index.d.ts b/create-postgres/index.d.ts new file mode 100644 index 0000000..afcee8f --- /dev/null +++ b/create-postgres/index.d.ts @@ -0,0 +1,2 @@ +export * from "create-db"; + diff --git a/create-postgres/package.json b/create-postgres/package.json index 24eb423..5c51c60 100644 --- a/create-postgres/package.json +++ b/create-postgres/package.json @@ -2,7 +2,7 @@ "name": "create-postgres", "version": "1.1.0", "description": "Instantly create a temporary Prisma Postgres database with one command, then claim and persist it in your Prisma Data Platform project when ready.", - "author": "", + "author": "prisma", "repository": { "type": "git", "url": "git+https://github.com/prisma/create-db.git" @@ -20,6 +20,18 @@ ], "license": "ISC", "type": "module", + "main": "./index.js", + "module": "./index.js", + "types": "./index.d.ts", + "exports": { + ".": { + "types": "./index.d.ts", + "import": "./index.js" + }, + "./cli": { + "import": "./cli.js" + } + }, "bin": { "create-postgres": "./cli.js" }, @@ -31,6 +43,7 @@ }, "files": [ "cli.js", - "index.js" + "index.js", + "index.d.ts" ] } \ No newline at end of file From d08487e4eec4daae590f9e07e0c59e8abf293e6e Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Fri, 12 Dec 2025 23:51:02 +0530 Subject: [PATCH 20/22] chore: update readme --- create-db/README.md | 255 +++++++++++++++++++++++++++++--------- create-pg/README.md | 125 ++++++++----------- create-postgres/README.md | 125 ++++++++----------- 3 files changed, 292 insertions(+), 213 deletions(-) diff --git a/create-db/README.md b/create-db/README.md index 51f7e06..b382485 100644 --- a/create-db/README.md +++ b/create-db/README.md @@ -1,106 +1,239 @@ -## **What is `create-db`?** +# create-db -`create-db` is an open-source CLI tool that provisions [**temporary Prisma Postgres databases**](https://www.prisma.io/postgres?utm_source=create_db_npm_docs) with a single command. +`create-db` is an open-source CLI tool and library that provisions [**temporary Prisma Postgres databases**](https://www.prisma.io/postgres?utm_source=create_db_npm_docs) with a single command. -Each database is available for **24 hours** by default. To keep the database permanently, you can **claim it for free** using the URL displayed in the CLI output. +Each database is available for **24 hours** by default. To keep the database permanently, you can **claim it for free** using the URL displayed in the output. This tool is designed for developers who need a fast way to test, prototype, or integrate Prisma Postgres without manual setup or creating an account. -## **Installation and usage** +## Installation and Usage -There is no need to install the tool globally. Simply run: +No installation required. Simply run: ```bash npx create-db@latest ``` -You can also use the following aliases: +You can also use these aliases: ```bash npx create-pg@latest npx create-postgres@latest ``` -## **Examples** +## CLI Usage -```bash -npx create-db # Creates a database in the default region -npx create-db --region eu-west-1 # Creates a database in a specific region -npx create-db --i # Interactive region selection +### Commands +```bash +npx create-db [create] # Create a new database (default command) +npx create-db regions # List available regions ``` -## **Available options** +### Options + +| Flag | Alias | Description | +|------|-------|-------------| +| `--region ` | `-r` | AWS region for the database | +| `--interactive` | `-i` | Interactive mode to select a region | +| `--json` | `-j` | Output machine-readable JSON | +| `--env ` | `-e` | Write DATABASE_URL and CLAIM_URL to specified .env file | +| `--help` | `-h` | Show help message | +| `--version` | | Show version | + +### Available Regions + +- `ap-southeast-1` - Asia Pacific (Singapore) +- `ap-northeast-1` - Asia Pacific (Tokyo) +- `eu-central-1` - Europe (Frankfurt) +- `eu-west-3` - Europe (Paris) +- `us-east-1` - US East (N. Virginia) +- `us-west-1` - US West (N. California) + +### Examples + +```bash +# Create database in auto-detected nearest region +npx create-db + +# Create database in a specific region +npx create-db --region eu-west-3 +npx create-db -r us-east-1 + +# Interactive region selection +npx create-db --interactive +npx create-db -i -You can run `npx create-db --help` or `npx create-db -h` to see all the available CLI options: +# Output as JSON (useful for scripting) +npx create-db --json +npx create-db -j +# Write connection string to .env file +npx create-db --env .env +npx create-db -e .env.local + +# Combine flags +npx create-db -r eu-central-1 -j +npx create-db -i -e .env + +# List available regions +npx create-db regions ``` -npx create-db [options] -Options: - --region , -r Specify a region - Available regions: - ap-southeast-1, ap-northeast-1, - eu-central-1, eu-west-3, - us-east-1, us-west-1 +### JSON Output + +When using `--json`, the output includes: + +```json +{ + "success": true, + "connectionString": "postgresql://user:pass@host:5432/postgres?sslmode=require", + "claimUrl": "https://create-db.prisma.io/claim?projectID=...", + "deletionDate": "2025-12-13T12:00:00.000Z", + "region": "us-east-1", + "name": "2025-12-12T12:00:00.000Z", + "projectId": "proj_..." +} +``` - --interactive, -i Run in interactive mode +### Environment File Output - --help, -h Show this help message +When using `--env`, the following variables are appended to the specified file: +```env +DATABASE_URL="postgresql://user:pass@host:5432/postgres?sslmode=require" +CLAIM_URL="https://create-db.prisma.io/claim?projectID=..." ``` -## **CLI output example** +## Programmatic API +You can also use `create-db` as a library in your Node.js applications: + +```bash +npm install create-db +# or +bun add create-db ``` -ā”Œ šŸš€ Creating a Prisma Postgres database -│ -│ Provisioning a temporary database in us-east-1... -│ -│ It will be automatically deleted in 24 hours, but you can claim it. -│ -ā—‡ Database created successfully! -│ -ā— Connect to your database → -│ -│ Prisma connection string: -│ prisma+postgres://accelerate.prisma-data.net/?api_key=... -│ -│ Standard connection string: -│ postgresql://:@db.prisma.io:5432/postgres -│ -ā—† Claim your database → -│ -│ Want to keep your database? Claim for free: -│ https://create-db.prisma.io?projectID=proj_... -ā”” +### `create(options?)` + +Create a new Prisma Postgres database programmatically. + +```typescript +import { create } from "create-db"; + +const result = await create({ region: "us-east-1" }); + +if (result.success) { + console.log(`Connection string: ${result.connectionString}`); + console.log(`Claim URL: ${result.claimUrl}`); + console.log(`Expires: ${result.deletionDate}`); +} else { + console.error(`Error: ${result.message}`); +} ``` -## **Claiming a database** +#### Options -When you create a database using `create-db`, it is temporary and will be deleted automatically after **24 hours**. +| Option | Type | Description | +|--------|------|-------------| +| `region` | `RegionId` | AWS region for the database (optional, defaults to `us-east-1`) | +| `userAgent` | `string` | Custom user agent string for tracking (optional) | -The CLI output includes a **claim URL** that allows you to keep the database permanently for free. +### `regions()` -**What claiming does:** +List available Prisma Postgres regions. + +```typescript +import { regions } from "create-db"; + +const availableRegions = await regions(); +console.log(availableRegions); +// [{ id: "us-east-1", name: "US East (N. Virginia)", status: "available" }, ...] +``` + +### Type Guards + +```typescript +import { create, isDatabaseSuccess, isDatabaseError } from "create-db"; -- Moves the database into your Prisma Data Platform account. -- Prevents it from being auto-deleted. -- Lets you continue using the database as a long-term instance. +const result = await create(); -Example: +if (isDatabaseSuccess(result)) { + // result is DatabaseResult + console.log(result.connectionString); +} +if (isDatabaseError(result)) { + // result is DatabaseError + console.error(result.message); +} ``` -ā—† Claim your database → -│ -│ Want to keep your database? Claim for free: -| -│ https://create-db.prisma.io?projectID=proj_... -│ -│ Your database will be deleted on 7/24/2025, 2:25:41 AM if not claimed. + +### Types + +```typescript +import type { + Region, + RegionId, + CreateDatabaseResult, + DatabaseResult, + DatabaseError, + ProgrammaticCreateOptions, +} from "create-db"; + +// RegionId is a union type of available regions +type RegionId = "ap-southeast-1" | "ap-northeast-1" | "eu-central-1" | "eu-west-3" | "us-east-1" | "us-west-1"; + +// DatabaseResult (success) +interface DatabaseResult { + success: true; + connectionString: string | null; + claimUrl: string; + deletionDate: string; + region: string; + name: string; + projectId: string; + userAgent?: string; +} + +// DatabaseError (failure) +interface DatabaseError { + success: false; + error: string; + message: string; + raw?: string; + details?: unknown; + status?: number; +} + +// CreateDatabaseResult is DatabaseResult | DatabaseError ``` -## **Next steps** +### Region Validation with Zod + +```typescript +import { RegionSchema } from "create-db"; + +// Validate region input +const result = RegionSchema.safeParse("us-east-1"); +if (result.success) { + console.log("Valid region:", result.data); +} +``` + +## Claiming a Database + +When you create a database, it is temporary and will be deleted after **24 hours**. + +The output includes a **claim URL** that allows you to keep the database permanently for free. + +**What claiming does:** + +- Moves the database into your Prisma Data Platform account +- Prevents it from being auto-deleted +- Lets you continue using the database as a long-term instance + +## Next Steps -- Refer to the section in the official [Prisma Postgres documentation](https://www.prisma.io/docs/postgres/introduction/npx-create-db). +- Refer to the [Prisma Postgres documentation](https://www.prisma.io/docs/postgres/introduction/npx-create-db) for more details. diff --git a/create-pg/README.md b/create-pg/README.md index 1b8e460..602a4bd 100644 --- a/create-pg/README.md +++ b/create-pg/README.md @@ -1,106 +1,79 @@ -## **What is `create-pg`?** +# create-pg -`create-pg` is an open-source CLI tool that provisions [**temporary Prisma Postgres databases**](https://www.prisma.io/postgres?utm_source=create_db_npm_docs) with a single command. +`create-pg` is an alias for [`create-db`](https://www.npmjs.com/package/create-db) - an open-source CLI tool and library that provisions [**temporary Prisma Postgres databases**](https://www.prisma.io/postgres?utm_source=create_db_npm_docs) with a single command. -Each database is available for **24 hours** by default. To keep the database permanently, you can **claim it for free** using the URL displayed in the CLI output. +Each database is available for **24 hours** by default. To keep the database permanently, you can **claim it for free** using the URL displayed in the output. -This tool is designed for developers who need a fast way to test, prototype, or integrate Prisma Postgres without manual setup or creating an account. - -## **Installation and usage** - -There is no need to install the tool globally. Simply run: +## Quick Start ```bash npx create-pg@latest ``` -You can also use the following aliases: +## CLI Usage ```bash -npx create-db@latest -npx create-postgres@latest -``` +# Create database in auto-detected nearest region +npx create-pg -## **Examples** +# Create database in a specific region +npx create-pg --region eu-west-3 +npx create-pg -r us-east-1 -```bash -npx create-pg # Creates a database in the default region -npx create-pg --region eu-west-1 # Creates a database in a specific region -npx create-pg --i # Interactive region selection - -``` +# Interactive region selection +npx create-pg --interactive +npx create-pg -i -## **Available options** +# Output as JSON +npx create-pg --json +npx create-pg -j -You can run `npx create-pg --help` or `npx create-pg -h` to see all the available CLI options: +# Write connection string to .env file +npx create-pg --env .env +npx create-pg -e .env.local +# List available regions +npx create-pg regions ``` -npx create-pg [options] -Options: - --region , -r Specify a region - Available regions: - ap-southeast-1, ap-northeast-1, - eu-central-1, eu-west-3, - us-east-1, us-west-1 +### Options - --interactive, -i Run in interactive mode +| Flag | Alias | Description | +|------|-------|-------------| +| `--region ` | `-r` | AWS region for the database | +| `--interactive` | `-i` | Interactive mode to select a region | +| `--json` | `-j` | Output machine-readable JSON | +| `--env ` | `-e` | Write DATABASE_URL and CLAIM_URL to specified .env file | +| `--help` | `-h` | Show help message | - --help, -h Show this help message +## Programmatic API -``` +```typescript +import { create, regions } from "create-pg"; -## **CLI output example** +// Create a database +const result = await create({ region: "us-east-1" }); -``` -ā”Œ šŸš€ Creating a Prisma Postgres database -│ -│ Provisioning a temporary database in us-east-1... -│ -│ It will be automatically deleted in 24 hours, but you can claim it. -│ -ā—‡ Database created successfully! -│ -ā— Connect to your database → -│ -│ Prisma connection string: -│ prisma+postgres://accelerate.prisma-data.net/?api_key=... -│ -│ Standard connection string: -│ postgresql://:@db.prisma.io:5432/postgres -│ -ā—† Claim your database → -│ -│ Want to keep your database? Claim for free: -│ https://create-db.prisma.io?projectID=proj_... -ā”” +if (result.success) { + console.log(`Connection string: ${result.connectionString}`); + console.log(`Claim URL: ${result.claimUrl}`); +} else { + console.error(`Error: ${result.message}`); +} +// List available regions +const availableRegions = await regions(); ``` -## **Claiming a database** - -When you create a database using `create-pg`, it is temporary and will be deleted automatically after **24 hours**. - -The CLI output includes a **claim URL** that allows you to keep the database permanently for free. - -**What claiming does:** - -- Moves the database into your Prisma Data Platform account. -- Prevents it from being auto-deleted. -- Lets you continue using the database as a long-term instance. +## Aliases -Example: +You can also use: -``` -ā—† Claim your database → -│ -│ Want to keep your database? Claim for free: -| -│ https://create-db.prisma.io?projectID=proj_... -│ -│ Your database will be deleted on 7/24/2025, 2:25:41 AM if not claimed. +```bash +npx create-db@latest +npx create-postgres@latest ``` -## **Next steps** +## Documentation -- Refer to the section in the official [Prisma Postgres documentation](https://www.prisma.io/docs/postgres/introduction/npx-create-db). +For full documentation, see the [create-db README](https://github.com/prisma/create-db/tree/main/create-db#readme). diff --git a/create-postgres/README.md b/create-postgres/README.md index 95ccb51..b4d6726 100644 --- a/create-postgres/README.md +++ b/create-postgres/README.md @@ -1,106 +1,79 @@ -## **What is `create-postgres`?** +# create-postgres -`create-postgres` is an open-source CLI tool that provisions [**temporary Prisma Postgres databases**](https://www.prisma.io/postgres?utm_source=create_db_npm_docs) with a single command. +`create-postgres` is an alias for [`create-db`](https://www.npmjs.com/package/create-db) - an open-source CLI tool and library that provisions [**temporary Prisma Postgres databases**](https://www.prisma.io/postgres?utm_source=create_db_npm_docs) with a single command. -Each database is available for **24 hours** by default. To keep the database permanently, you can **claim it for free** using the URL displayed in the CLI output. +Each database is available for **24 hours** by default. To keep the database permanently, you can **claim it for free** using the URL displayed in the output. -This tool is designed for developers who need a fast way to test, prototype, or integrate Prisma Postgres without manual setup or creating an account. - -## **Installation and usage** - -There is no need to install the tool globally. Simply run: +## Quick Start ```bash npx create-postgres@latest ``` -You can also use the following aliases: +## CLI Usage ```bash -npx create-db@latest -npx create-pg@latest -``` +# Create database in auto-detected nearest region +npx create-postgres -## **Examples** +# Create database in a specific region +npx create-postgres --region eu-west-3 +npx create-postgres -r us-east-1 -```bash -npx create-postgres # Creates a database in the default region -npx create-postgres --region eu-west-1 # Creates a database in a specific region -npx create-postgres --i # Interactive region selection - -``` +# Interactive region selection +npx create-postgres --interactive +npx create-postgres -i -## **Available options** +# Output as JSON +npx create-postgres --json +npx create-postgres -j -You can run `npx create-postgres --help` or `npx create-postgres -h` to see all the available CLI options: +# Write connection string to .env file +npx create-postgres --env .env +npx create-postgres -e .env.local +# List available regions +npx create-postgres regions ``` -npx create-postgres [options] -Options: - --region , -r Specify a region - Available regions: - ap-southeast-1, ap-northeast-1, - eu-central-1, eu-west-3, - us-east-1, us-west-1 +### Options - --interactive, -i Run in interactive mode +| Flag | Alias | Description | +|------|-------|-------------| +| `--region ` | `-r` | AWS region for the database | +| `--interactive` | `-i` | Interactive mode to select a region | +| `--json` | `-j` | Output machine-readable JSON | +| `--env ` | `-e` | Write DATABASE_URL and CLAIM_URL to specified .env file | +| `--help` | `-h` | Show help message | - --help, -h Show this help message +## Programmatic API -``` +```typescript +import { create, regions } from "create-postgres"; -## **CLI output example** +// Create a database +const result = await create({ region: "us-east-1" }); -``` -ā”Œ šŸš€ Creating a Prisma Postgres database -│ -│ Provisioning a temporary database in us-east-1... -│ -│ It will be automatically deleted in 24 hours, but you can claim it. -│ -ā—‡ Database created successfully! -│ -ā— Connect to your database → -│ -│ Prisma connection string: -│ prisma+postgres://accelerate.prisma-data.net/?api_key=... -│ -│ Standard connection string: -│ postgresql://:@db.prisma.io:5432/postgres -│ -ā—† Claim your database → -│ -│ Want to keep your database? Claim for free: -│ https://create-db.prisma.io?projectID=proj_... -ā”” +if (result.success) { + console.log(`Connection string: ${result.connectionString}`); + console.log(`Claim URL: ${result.claimUrl}`); +} else { + console.error(`Error: ${result.message}`); +} +// List available regions +const availableRegions = await regions(); ``` -## **Claiming a database** - -When you create a database using `create-postgres`, it is temporary and will be deleted automatically after **24 hours**. - -The CLI output includes a **claim URL** that allows you to keep the database permanently for free. - -**What claiming does:** - -- Moves the database into your Prisma Data Platform account. -- Prevents it from being auto-deleted. -- Lets you continue using the database as a long-term instance. +## Aliases -Example: +You can also use: -``` -ā—† Claim your database → -│ -│ Want to keep your database? Claim for free: -| -│ https://create-db.prisma.io?projectID=proj_... -│ -│ Your database will be deleted on 7/24/2025, 2:25:41 AM if not claimed. +```bash +npx create-db@latest +npx create-pg@latest ``` -## **Next steps** +## Documentation -- Refer to the section in the official [Prisma Postgres documentation](https://www.prisma.io/docs/postgres/introduction/npx-create-db). +For full documentation, see the [create-db README](https://github.com/prisma/create-db/tree/main/create-db#readme). From d37a5df3ff64bcdd4a054fa44ee20dab1c390bad Mon Sep 17 00:00:00 2001 From: Aidan McAlister Date: Fri, 12 Dec 2025 14:02:45 -0500 Subject: [PATCH 21/22] fix: test updated --- claim-db-worker/__tests__/callback-api.test.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/claim-db-worker/__tests__/callback-api.test.ts b/claim-db-worker/__tests__/callback-api.test.ts index 46f5be4..fe2b48b 100644 --- a/claim-db-worker/__tests__/callback-api.test.ts +++ b/claim-db-worker/__tests__/callback-api.test.ts @@ -55,6 +55,7 @@ describe("auth callback API", () => { vi.mocked(transferProject).mockResolvedValue({ success: true, status: 200, + transferResponse: {}, }); vi.mocked(redirectToSuccess).mockReturnValue( @@ -86,7 +87,9 @@ describe("auth callback API", () => { ); expect(redirectToSuccess).toHaveBeenCalledWith( request, - "test-project-123" + "test-project-123", + "test-workspace-123", // workspaceId + "test-database-123" // databaseId ); expect(mockFetch).toHaveBeenCalledWith( @@ -170,6 +173,7 @@ describe("auth callback API", () => { success: false, status: 403, error: "Insufficient permissions", + transferResponse: {}, }); vi.mocked(redirectToError).mockReturnValue( From 1ab6dc3ca7b15013a8a35a0a67a66ab8a041ed7a Mon Sep 17 00:00:00 2001 From: Aidan McAlister Date: Fri, 12 Dec 2025 14:05:14 -0500 Subject: [PATCH 22/22] fix: test updated again --- .../__tests__/callback-api.test.ts | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/claim-db-worker/__tests__/callback-api.test.ts b/claim-db-worker/__tests__/callback-api.test.ts index fe2b48b..b1b175b 100644 --- a/claim-db-worker/__tests__/callback-api.test.ts +++ b/claim-db-worker/__tests__/callback-api.test.ts @@ -65,10 +65,23 @@ describe("auth callback API", () => { }) ); - mockFetch.mockResolvedValue({ - ok: true, - json: async () => ({}), - }); + mockFetch + .mockResolvedValueOnce({ + ok: true, + json: async () => ({ + data: { workspace: { id: "wksp_test-workspace-123" } }, + }), + }) + .mockResolvedValueOnce({ + ok: true, + json: async () => ({ + data: [{ id: "db_test-database-123" }], + }), + }) + .mockResolvedValue({ + ok: true, + json: async () => ({}), + }); const request = new NextRequest( "http://localhost:3000/api/auth/callback?code=test-code&state=test-state&projectID=test-project-123"