UNPKG

@reliverse/rse-sdk

Version:

@reliverse/rse-sdk allows you to create new plugins for @reliverse/rse CLI, interact with reliverse.org, and even extend your own CLI functionality (you may also try @reliverse/dler-sdk for this case).

263 lines (262 loc) 8.25 kB
import { re } from "@reliverse/relico"; import { relinka } from "@reliverse/relinka"; import { cancel, confirm, isCancel, select, spinner, text } from "@reliverse/rempts"; import { $ } from "execa"; import os from "node:os"; import path from "node:path"; import { addEnvVariablesToFile } from "../project-generation/env-setup.js"; import { commandExists } from "../../utils/command-exists.js"; async function isTursoInstalled() { return commandExists("turso"); } async function isTursoLoggedIn() { try { const output = await $`turso auth whoami`; return !output.stdout.includes("You are not logged in"); } catch { return false; } } async function loginToTurso() { const s = spinner(); try { s.start("Logging in to Turso..."); await $`turso auth login`; s.stop("Logged into Turso"); return true; } catch (_error) { s.stop(re.red("Failed to log in to Turso")); } } async function installTursoCLI(isMac) { const s = spinner(); try { s.start("Installing Turso CLI..."); if (isMac) { await $`brew install tursodatabase/tap/turso`; } else { const { stdout: installScript } = await $`curl -sSfL https://get.tur.so/install.sh`; await $`bash -c '${installScript}'`; } s.stop("Turso CLI installed"); return true; } catch (error) { if (error instanceof Error && error.message.includes("User force closed")) { s.stop("Turso CLI installation cancelled"); relinka("warn", re.yellow("Turso CLI installation cancelled by user")); throw new Error("Installation cancelled"); } s.stop(re.red("Failed to install Turso CLI")); } } async function getTursoGroups() { const s = spinner(); try { s.start("Fetching Turso groups..."); const { stdout } = await $`turso group list`; const lines = stdout.trim().split("\n"); if (lines.length <= 1) { s.stop("No Turso groups found"); return []; } const groups = lines.slice(1).map((line) => { const [name, locations, version, status] = line.trim().split(/\s{2,}/); if (!name || !locations || !version || !status) return null; return { name, locations, version, status }; }).filter((group) => group !== null); s.stop(`Found ${groups.length} Turso groups`); return groups; } catch (error) { s.stop(re.red("Error fetching Turso groups")); console.error("Error fetching Turso groups:", error); return []; } } async function selectTursoGroup() { const groups = await getTursoGroups(); if (groups.length === 0) { return null; } if (groups.length === 1) { relinka( "info", `Using the only available group: ${re.blue(groups[0]?.name ?? "")}` ); return groups[0]?.name ?? null; } const groupOptions = groups.map((group) => ({ value: group.name, label: `${group.name} (${group.locations})` })); const selectedGroup = await select({ message: "Select a Turso database group:", options: groupOptions }); if (isCancel(selectedGroup)) { cancel(re.red("Operation cancelled")); process.exit(0); } return selectedGroup; } async function createTursoDatabase(dbName, groupName) { const s = spinner(); try { s.start( `Creating Turso database "${dbName}"${groupName ? ` in group "${groupName}"` : ""}...` ); if (groupName) { await $`turso db create ${dbName} --group ${groupName}`; } else { await $`turso db create ${dbName}`; } s.stop(`Turso database "${dbName}" created`); } catch (error) { s.stop(re.red(`Failed to create database "${dbName}"`)); if (error instanceof Error && error.message.includes("already exists")) { throw new Error("DATABASE_EXISTS"); } } s.start("Retrieving database connection details..."); try { const { stdout: dbUrl } = await $`turso db show ${dbName} --url`; const { stdout: authToken } = await $`turso db tokens create ${dbName}`; s.stop("Database connection details retrieved"); return { dbUrl: dbUrl.trim(), authToken: authToken.trim() }; } catch (_error) { s.stop(re.red("Failed to retrieve database connection details")); } } async function writeEnvFile(projectDir, config) { const envPath = path.join(projectDir, "apps/server", ".env"); const variables = [ { key: "DATABASE_URL", value: config?.dbUrl ?? "", condition: true }, { key: "DATABASE_AUTH_TOKEN", value: config?.authToken ?? "", condition: true } ]; await addEnvVariablesToFile(envPath, variables); } function displayManualSetupInstructions() { relinka( "info", `Manual Turso Setup Instructions: 1. Visit https://turso.tech and create an account 2. Create a new database from the dashboard 3. Get your database URL and authentication token 4. Add these credentials to the .env file in apps/server/.env DATABASE_URL=your_database_url DATABASE_AUTH_TOKEN=your_auth_token` ); } export async function setupTurso(config) { const { orm, projectDir } = config; const _isDrizzle = orm === "drizzle"; const setupSpinner = spinner(); setupSpinner.start("Checking Turso CLI availability..."); try { const platform = os.platform(); const isMac = platform === "darwin"; const _isLinux = platform === "linux"; const isWindows = platform === "win32"; if (isWindows) { setupSpinner.stop(re.yellow("Turso setup not supported on Windows")); relinka( "warn", re.yellow("Automatic Turso setup is not supported on Windows.") ); await writeEnvFile(projectDir); displayManualSetupInstructions(); return; } setupSpinner.stop("Turso CLI availability checked"); const isCliInstalled = await isTursoInstalled(); if (!isCliInstalled) { const shouldInstall = await confirm({ /* Argument of type '{ message: string; initialValue: true; }' is not assignable to parameter of type 'ConfirmPromptOptions'. Property 'title' is missing in type '{ message: string; initialValue: true; }' but required in type 'ConfirmPromptOptions'.ts(2345) types.d.ts(118, 5): 'title' is declared here. */ title: "Turso CLI Installation", message: "Would you like to install Turso CLI?", initialValue: true }); if (isCancel(shouldInstall)) { cancel(re.red("Operation cancelled")); process.exit(0); } if (!shouldInstall) { await writeEnvFile(projectDir); displayManualSetupInstructions(); return; } await installTursoCLI(isMac); } const isLoggedIn = await isTursoLoggedIn(); if (!isLoggedIn) { await loginToTurso(); } const selectedGroup = await selectTursoGroup(); let success = false; let dbName = ""; let suggestedName = path.basename(projectDir); while (!success) { const dbNameResponse = await text({ message: "Enter a name for your database:", defaultValue: suggestedName, initialValue: suggestedName, placeholder: suggestedName }); if (isCancel(dbNameResponse)) { cancel(re.red("Operation cancelled")); process.exit(0); } dbName = dbNameResponse; try { const config2 = await createTursoDatabase(dbName, selectedGroup); await writeEnvFile(projectDir, config2); success = true; } catch (error) { if (error instanceof Error && error.message === "DATABASE_EXISTS") { relinka( "warn", re.yellow(`Database "${re.red(dbName)}" already exists`) ); suggestedName = `${dbName}-${Math.floor(Math.random() * 1e3)}`; } else { throw error; } } } relinka("success", "Turso database setup completed successfully!"); } catch (error) { setupSpinner.stop(re.red("Turso CLI availability check failed")); relinka( "error", re.red( `Error during Turso setup: ${error instanceof Error ? error.message : String(error)}` ) ); await writeEnvFile(projectDir); displayManualSetupInstructions(); relinka("success", "Setup completed with manual configuration required."); } }