scaffoldry 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +198 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunk-AA2UXYNR.js +1952 -0
- package/dist/chunk-AA2UXYNR.js.map +1 -0
- package/dist/chunk-WOS3F5LR.js +250 -0
- package/dist/chunk-WOS3F5LR.js.map +1 -0
- package/dist/chunk-XIP7YNKZ.js +1216 -0
- package/dist/chunk-XIP7YNKZ.js.map +1 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/platform-Z35MB2P5.js +41 -0
- package/dist/platform-Z35MB2P5.js.map +1 -0
- package/dist/setup-L2PO5OVZ.js +18 -0
- package/dist/setup-L2PO5OVZ.js.map +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/logger.ts","../src/utils/template.ts","../src/utils/license.ts","../src/utils/env.ts","../src/utils/config.ts","../src/utils/diagnostics.ts","../src/wizards/stripe.ts","../src/wizards/base.ts","../src/wizards/database.ts","../src/wizards/email.ts","../src/wizards/storage.ts","../src/commands/setup.ts"],"sourcesContent":["import chalk from \"chalk\";\n\nexport const logger = {\n info: (message: string) => {\n console.log(chalk.blue(\"info\"), message);\n },\n success: (message: string) => {\n console.log(chalk.green(\"success\"), message);\n },\n warn: (message: string) => {\n console.log(chalk.yellow(\"warn\"), message);\n },\n error: (message: string) => {\n console.log(chalk.red(\"error\"), message);\n },\n log: (message: string) => {\n console.log(message);\n },\n newLine: () => {\n console.log();\n },\n};\n","import type { ProjectConfig, TemplateContext } from \"../types.js\";\n\nexport function createTemplateContext(config: ProjectConfig): TemplateContext {\n return {\n projectName: config.name,\n projectDescription: config.description,\n features: config.features,\n databaseProvider: config.database,\n packageManager: config.packageManager,\n hasAuth: config.features.includes(\"auth\"),\n hasBilling: config.features.includes(\"billing\"),\n hasEmail: config.features.includes(\"email\"),\n hasStorage: config.features.includes(\"storage\"),\n hasJobs: config.features.includes(\"jobs\"),\n hasWebhooks: config.features.includes(\"webhooks\"),\n hasAdmin: config.features.includes(\"admin\"),\n };\n}\n\nexport function toKebabCase(str: string): string {\n return str\n .replace(/([a-z])([A-Z])/g, \"$1-$2\")\n .replace(/[\\s_]+/g, \"-\")\n .toLowerCase();\n}\n\nexport function toPascalCase(str: string): string {\n return str\n .split(/[-_\\s]+/)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(\"\");\n}\n\nexport function toCamelCase(str: string): string {\n const pascal = toPascalCase(str);\n return pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n\nexport function replaceTemplateVars(\n content: string,\n context: TemplateContext\n): string {\n return content\n .replace(/\\{\\{projectName\\}\\}/g, context.projectName)\n .replace(/\\{\\{projectDescription\\}\\}/g, context.projectDescription)\n .replace(/\\{\\{pascalName\\}\\}/g, toPascalCase(context.projectName))\n .replace(/\\{\\{camelName\\}\\}/g, toCamelCase(context.projectName))\n .replace(/\\{\\{kebabName\\}\\}/g, toKebabCase(context.projectName));\n}\n","import fs from \"fs-extra\";\nimport path from \"path\";\nimport os from \"os\";\n\nconst CONFIG_DIR = path.join(os.homedir(), \".scaffoldry\");\nconst LICENSE_FILE = path.join(CONFIG_DIR, \"license.json\");\nconst API_BASE_URL = process.env.SCAFFOLDRY_API_URL || \"https://scaffoldry.com\";\n\ninterface LicenseConfig {\n key: string;\n email?: string;\n validatedAt: string;\n}\n\ninterface ValidateLicenseResponse {\n valid: boolean;\n email: string | undefined;\n activationsRemaining?: number;\n error?: string;\n}\n\n/**\n * Get the stored license key\n */\nexport async function getStoredLicense(): Promise<LicenseConfig | null> {\n try {\n if (await fs.pathExists(LICENSE_FILE)) {\n const config = await fs.readJson(LICENSE_FILE);\n return config as LicenseConfig;\n }\n } catch {\n // Ignore read errors\n }\n return null;\n}\n\n/**\n * Store a license key locally\n */\nexport async function storeLicense(config: LicenseConfig): Promise<void> {\n await fs.ensureDir(CONFIG_DIR);\n await fs.writeJson(LICENSE_FILE, config, { spaces: 2 });\n}\n\n/**\n * Clear the stored license\n */\nexport async function clearLicense(): Promise<void> {\n try {\n await fs.remove(LICENSE_FILE);\n } catch {\n // Ignore remove errors\n }\n}\n\n/**\n * Validate a license key format\n * Format: SCAF-XXXX-XXXX-XXXX-XXXX\n */\nexport function isValidLicenseKeyFormat(key: string): boolean {\n const pattern = /^SCAF-[A-HJ-NP-Z2-9]{4}-[A-HJ-NP-Z2-9]{4}-[A-HJ-NP-Z2-9]{4}-[A-HJ-NP-Z2-9]{4}$/;\n return pattern.test(key.toUpperCase());\n}\n\n/**\n * Validate a license key against the API\n */\nexport async function validateLicense(licenseKey: string): Promise<ValidateLicenseResponse> {\n const machineId = await getMachineId();\n\n try {\n const response = await fetch(`${API_BASE_URL}/api/license/validate`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"User-Agent\": \"scaffoldry-cli/1.0.0\",\n },\n body: JSON.stringify({\n licenseKey,\n machineId,\n }),\n });\n\n const data = (await response.json()) as ValidateLicenseResponse;\n return data;\n } catch {\n // Network error - allow offline use if license was previously validated\n const stored = await getStoredLicense();\n if (stored && stored.key === licenseKey) {\n return {\n valid: true,\n email: stored.email,\n };\n }\n\n return {\n valid: false,\n email: undefined,\n error: \"Unable to validate license. Please check your internet connection.\",\n };\n }\n}\n\n/**\n * Get a unique machine identifier\n */\nasync function getMachineId(): Promise<string> {\n // Use hostname + user as a simple machine identifier\n const hostname = os.hostname();\n const username = os.userInfo().username;\n const platform = os.platform();\n\n // Create a simple hash\n const str = `${hostname}:${username}:${platform}`;\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash;\n }\n\n return `machine-${Math.abs(hash).toString(16)}`;\n}\n\n/**\n * Generate the .npmrc content for accessing private packages\n */\nexport function generateNpmrcContent(licenseKey: string): string {\n const registryUrl = `${API_BASE_URL}/api/registry`;\n return `# Scaffoldry Private Registry\n@scaffoldry:registry=${registryUrl}\n//${new URL(registryUrl).host}/api/registry/:_authToken=${licenseKey}\n`;\n}\n","import fs from \"fs-extra\";\nimport path from \"node:path\";\n\n/**\n * Parse .env file content into key-value pairs\n */\nexport function parseEnvFile(content: string): Record<string, string> {\n const env: Record<string, string> = {};\n const lines = content.split(\"\\n\");\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n // Skip empty lines and comments\n if (!trimmed || trimmed.startsWith(\"#\")) {\n continue;\n }\n\n // Match KEY=VALUE or KEY=\"VALUE\" or KEY='VALUE'\n const match = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);\n if (match && match[1] && match[2] !== undefined) {\n const key = match[1];\n let value = match[2];\n\n // Handle quoted values\n if ((value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))) {\n value = value.slice(1, -1);\n }\n\n env[key] = value;\n }\n }\n\n return env;\n}\n\n/**\n * Serialize key-value pairs to .env file format\n * Preserves comments and structure from original content if provided\n */\nexport function serializeEnvFile(\n env: Record<string, string>,\n originalContent?: string\n): string {\n if (!originalContent) {\n // No original content, just serialize all keys\n return Object.entries(env)\n .map(([key, value]) => {\n // Quote values with spaces or special characters\n if (value.includes(\" \") || value.includes(\"#\") || value.includes(\"=\")) {\n return `${key}=\"${value}\"`;\n }\n return `${key}=${value}`;\n })\n .join(\"\\n\") + \"\\n\";\n }\n\n // Parse original to preserve structure\n const lines = originalContent.split(\"\\n\");\n const result: string[] = [];\n const writtenKeys = new Set<string>();\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n // Preserve empty lines and comments\n if (!trimmed || trimmed.startsWith(\"#\")) {\n result.push(line);\n continue;\n }\n\n // Check if this line has a key we need to update\n const match = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*)=/);\n if (match && match[1]) {\n const key = match[1];\n const value = env[key];\n if (value !== undefined) {\n // Update the value\n if (value.includes(\" \") || value.includes(\"#\") || value.includes(\"=\")) {\n result.push(`${key}=\"${value}\"`);\n } else {\n result.push(`${key}=${value}`);\n }\n writtenKeys.add(key);\n } else {\n // Keep original line for keys not in our env object\n result.push(line);\n }\n } else {\n result.push(line);\n }\n }\n\n // Add any new keys not in the original\n for (const [key, value] of Object.entries(env)) {\n if (!writtenKeys.has(key)) {\n if (value.includes(\" \") || value.includes(\"#\") || value.includes(\"=\")) {\n result.push(`${key}=\"${value}\"`);\n } else {\n result.push(`${key}=${value}`);\n }\n }\n }\n\n return result.join(\"\\n\");\n}\n\n/**\n * Read .env.local file from a project directory\n */\nexport async function readEnvLocal(projectDir: string): Promise<Record<string, string>> {\n const envPath = path.join(projectDir, \".env.local\");\n\n if (!(await fs.pathExists(envPath))) {\n return {};\n }\n\n const content = await fs.readFile(envPath, \"utf-8\");\n return parseEnvFile(content);\n}\n\n/**\n * Write or update .env.local file in a project directory\n * Preserves existing comments and structure\n */\nexport async function writeEnvLocal(\n projectDir: string,\n updates: Record<string, string>\n): Promise<void> {\n const envPath = path.join(projectDir, \".env.local\");\n\n let originalContent: string | undefined;\n if (await fs.pathExists(envPath)) {\n originalContent = await fs.readFile(envPath, \"utf-8\");\n // Merge with existing values\n const existing = parseEnvFile(originalContent);\n updates = { ...existing, ...updates };\n }\n\n const newContent = serializeEnvFile(updates, originalContent);\n await fs.writeFile(envPath, newContent);\n}\n\n/**\n * Set a single environment variable in .env.local\n */\nexport async function setEnvVar(\n projectDir: string,\n key: string,\n value: string\n): Promise<void> {\n await writeEnvLocal(projectDir, { [key]: value });\n}\n\n/**\n * Get a single environment variable from .env.local\n */\nexport async function getEnvVar(\n projectDir: string,\n key: string\n): Promise<string | undefined> {\n const env = await readEnvLocal(projectDir);\n return env[key];\n}\n\n/**\n * Check if .env.local exists in a project directory\n */\nexport async function hasEnvLocal(projectDir: string): Promise<boolean> {\n const envPath = path.join(projectDir, \".env.local\");\n return fs.pathExists(envPath);\n}\n\n/**\n * Create .env.local from .env.example if it doesn't exist\n */\nexport async function initEnvLocalFromExample(projectDir: string): Promise<boolean> {\n const envLocalPath = path.join(projectDir, \".env.local\");\n const envExamplePath = path.join(projectDir, \".env.example\");\n\n if (await fs.pathExists(envLocalPath)) {\n return false; // Already exists\n }\n\n if (await fs.pathExists(envExamplePath)) {\n await fs.copy(envExamplePath, envLocalPath);\n return true;\n }\n\n // Create empty .env.local\n await fs.writeFile(envLocalPath, \"# Scaffoldry Environment Variables\\n\");\n return true;\n}\n","import fs from \"fs-extra\";\nimport path from \"node:path\";\nimport os from \"node:os\";\n\nconst CONFIG_DIR = path.join(os.homedir(), \".scaffoldry\");\nconst CONFIG_FILE = path.join(CONFIG_DIR, \"config.json\");\n\n/**\n * User preferences stored globally in ~/.scaffoldry/config.json\n */\nexport interface UserConfig {\n /** Whether to automatically open URLs in browser */\n openBrowserAutomatically?: boolean;\n /** Default package manager preference */\n defaultPackageManager?: \"pnpm\" | \"npm\" | \"yarn\";\n /** Last used project directory */\n lastProjectDir?: string;\n /** Analytics opt-out */\n analyticsOptOut?: boolean;\n /** Default editor command */\n editor?: string;\n}\n\n/**\n * Read user configuration from ~/.scaffoldry/config.json\n */\nexport async function getUserConfig(): Promise<UserConfig> {\n try {\n if (await fs.pathExists(CONFIG_FILE)) {\n const content = await fs.readFile(CONFIG_FILE, \"utf-8\");\n return JSON.parse(content) as UserConfig;\n }\n } catch {\n // Ignore read/parse errors, return defaults\n }\n return {};\n}\n\n/**\n * Write user configuration to ~/.scaffoldry/config.json\n */\nexport async function setUserConfig(config: UserConfig): Promise<void> {\n await fs.ensureDir(CONFIG_DIR);\n await fs.writeJson(CONFIG_FILE, config, { spaces: 2 });\n}\n\n/**\n * Update specific fields in user configuration\n */\nexport async function updateUserConfig(updates: Partial<UserConfig>): Promise<void> {\n const current = await getUserConfig();\n await setUserConfig({ ...current, ...updates });\n}\n\n/**\n * Get a single config value\n */\nexport async function getConfigValue<K extends keyof UserConfig>(\n key: K\n): Promise<UserConfig[K]> {\n const config = await getUserConfig();\n return config[key];\n}\n\n/**\n * Set a single config value\n */\nexport async function setConfigValue<K extends keyof UserConfig>(\n key: K,\n value: UserConfig[K]\n): Promise<void> {\n await updateUserConfig({ [key]: value } as Partial<UserConfig>);\n}\n\n/**\n * Check if this is the first time the user is running Scaffoldry\n */\nexport async function isFirstRun(): Promise<boolean> {\n return !(await fs.pathExists(CONFIG_FILE));\n}\n\n/**\n * Get the path to the Scaffoldry config directory\n */\nexport function getConfigDir(): string {\n return CONFIG_DIR;\n}\n\n/**\n * Ensure the config directory exists\n */\nexport async function ensureConfigDir(): Promise<void> {\n await fs.ensureDir(CONFIG_DIR);\n}\n\n/**\n * Get path to project-specific data in ~/.scaffoldry/projects/{name}/\n */\nexport function getProjectDataDir(projectName: string): string {\n return path.join(CONFIG_DIR, \"projects\", projectName);\n}\n\n/**\n * Save project-specific data\n */\nexport async function saveProjectData(\n projectName: string,\n filename: string,\n data: string\n): Promise<void> {\n const projectDir = getProjectDataDir(projectName);\n await fs.ensureDir(projectDir);\n await fs.writeFile(path.join(projectDir, filename), data);\n}\n\n/**\n * Read project-specific data\n */\nexport async function readProjectData(\n projectName: string,\n filename: string\n): Promise<string | null> {\n const filePath = path.join(getProjectDataDir(projectName), filename);\n try {\n if (await fs.pathExists(filePath)) {\n return await fs.readFile(filePath, \"utf-8\");\n }\n } catch {\n // Ignore errors\n }\n return null;\n}\n","import { exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport fs from \"fs-extra\";\nimport path from \"node:path\";\nimport { readEnvLocal } from \"./env.js\";\nimport { getInstallInstructions } from \"./platform.js\";\n\nconst execAsync = promisify(exec);\n\nexport type CheckStatus = \"pass\" | \"warn\" | \"fail\";\n\nexport interface CheckResult {\n status: CheckStatus;\n label: string;\n message?: string;\n fix?: string;\n}\n\n/**\n * Check if Node.js is installed and get version\n */\nexport async function checkNodeVersion(): Promise<CheckResult> {\n try {\n const { stdout } = await execAsync(\"node --version\");\n const version = stdout.trim().replace(\"v\", \"\");\n const major = parseInt(version.split(\".\")[0] || \"0\", 10);\n\n if (major >= 20) {\n return { status: \"pass\", label: `Node.js ${version}` };\n }\n if (major >= 18) {\n return {\n status: \"warn\",\n label: `Node.js ${version}`,\n message: \"Node.js 20+ recommended\",\n fix: getInstallInstructions(\"node\"),\n };\n }\n return {\n status: \"fail\",\n label: `Node.js ${version}`,\n message: \"Node.js 18+ required\",\n fix: getInstallInstructions(\"node\"),\n };\n } catch {\n return {\n status: \"fail\",\n label: \"Node.js\",\n message: \"Not installed\",\n fix: getInstallInstructions(\"node\"),\n };\n }\n}\n\n/**\n * Check if pnpm is installed and get version\n */\nexport async function checkPnpmVersion(): Promise<CheckResult> {\n try {\n const { stdout } = await execAsync(\"pnpm --version\");\n const version = stdout.trim();\n const major = parseInt(version.split(\".\")[0] || \"0\", 10);\n\n if (major >= 9) {\n return { status: \"pass\", label: `pnpm ${version}` };\n }\n if (major >= 8) {\n return {\n status: \"warn\",\n label: `pnpm ${version}`,\n message: \"pnpm 9+ recommended\",\n fix: getInstallInstructions(\"pnpm\"),\n };\n }\n return {\n status: \"fail\",\n label: `pnpm ${version}`,\n message: \"pnpm 8+ required\",\n fix: getInstallInstructions(\"pnpm\"),\n };\n } catch {\n return {\n status: \"fail\",\n label: \"pnpm\",\n message: \"Not installed\",\n fix: getInstallInstructions(\"pnpm\"),\n };\n }\n}\n\n/**\n * Check if Git is installed\n */\nexport async function checkGitInstalled(): Promise<CheckResult> {\n try {\n const { stdout } = await execAsync(\"git --version\");\n const match = stdout.match(/git version ([\\d.]+)/);\n const version = match ? match[1] : \"unknown\";\n return { status: \"pass\", label: `Git ${version}` };\n } catch {\n return {\n status: \"fail\",\n label: \"Git\",\n message: \"Not installed\",\n fix: getInstallInstructions(\"git\"),\n };\n }\n}\n\n/**\n * Check if Stripe CLI is installed\n */\nexport async function checkStripeCli(): Promise<CheckResult> {\n try {\n const { stdout } = await execAsync(\"stripe --version\");\n const match = stdout.match(/stripe version ([\\d.]+)/i) || stdout.match(/([\\d.]+)/);\n const version = match ? match[1] : \"unknown\";\n return { status: \"pass\", label: `Stripe CLI ${version}` };\n } catch {\n return {\n status: \"warn\",\n label: \"Stripe CLI\",\n message: \"Not installed (required for local webhook testing)\",\n fix: getInstallInstructions(\"stripe-cli\"),\n };\n }\n}\n\n/**\n * Check if a required environment variable is set\n */\nexport async function checkEnvVar(\n projectDir: string,\n key: string,\n options?: { required?: boolean; pattern?: RegExp }\n): Promise<CheckResult> {\n const env = await readEnvLocal(projectDir);\n const value = env[key];\n\n if (!value) {\n if (options?.required === false) {\n return { status: \"pass\", label: key, message: \"Not configured (optional)\" };\n }\n return {\n status: \"fail\",\n label: key,\n message: \"Not configured\",\n fix: `Run: scaffoldry setup ${getSetupCommandForEnvVar(key)}`,\n };\n }\n\n if (options?.pattern && !options.pattern.test(value)) {\n return {\n status: \"warn\",\n label: key,\n message: \"Invalid format\",\n fix: `Run: scaffoldry setup ${getSetupCommandForEnvVar(key)}`,\n };\n }\n\n // Mask the value for display\n const masked = value.length > 8\n ? `${value.slice(0, 4)}${\"•\".repeat(Math.min(12, value.length - 8))}${value.slice(-4)}`\n : \"••••••••\";\n\n return { status: \"pass\", label: key, message: masked };\n}\n\n/**\n * Get the setup command for a given env var\n */\nfunction getSetupCommandForEnvVar(key: string): string {\n if (key.includes(\"STRIPE\")) return \"stripe\";\n if (key.includes(\"DATABASE\") || key.includes(\"DB_\")) return \"database\";\n if (key.includes(\"RESEND\") || key.includes(\"EMAIL\")) return \"email\";\n if (key.includes(\"S3\") || key.includes(\"AWS\") || key.includes(\"STORAGE\")) return \"storage\";\n return \"all\";\n}\n\n/**\n * Verify database connection\n */\nexport async function checkDatabaseConnection(projectDir: string): Promise<CheckResult> {\n const env = await readEnvLocal(projectDir);\n const dbUrl = env.DATABASE_URL;\n\n if (!dbUrl) {\n return {\n status: \"fail\",\n label: \"Database connection\",\n message: \"DATABASE_URL not configured\",\n fix: \"Run: scaffoldry setup database\",\n };\n }\n\n try {\n // Dynamic import to avoid requiring pg in all cases\n const { neon } = await import(\"@neondatabase/serverless\");\n const sql = neon(dbUrl);\n await sql`SELECT 1`;\n return { status: \"pass\", label: \"Database connection\" };\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Connection failed\";\n return {\n status: \"fail\",\n label: \"Database connection\",\n message: message.slice(0, 50),\n fix: \"Check your DATABASE_URL and network connection\",\n };\n }\n}\n\n/**\n * Verify Stripe API key\n */\nexport async function checkStripeApiKey(projectDir: string): Promise<CheckResult> {\n const env = await readEnvLocal(projectDir);\n const apiKey = env.STRIPE_SECRET_KEY;\n\n if (!apiKey) {\n return {\n status: \"fail\",\n label: \"Stripe API\",\n message: \"STRIPE_SECRET_KEY not configured\",\n fix: \"Run: scaffoldry setup stripe\",\n };\n }\n\n try {\n const response = await fetch(\"https://api.stripe.com/v1/balance\", {\n headers: {\n Authorization: `Bearer ${apiKey}`,\n },\n });\n\n if (response.ok) {\n return { status: \"pass\", label: \"Stripe API\" };\n }\n\n const data = await response.json() as { error?: { message?: string } };\n return {\n status: \"fail\",\n label: \"Stripe API\",\n message: data.error?.message || \"Invalid API key\",\n fix: \"Run: scaffoldry setup stripe\",\n };\n } catch {\n return {\n status: \"warn\",\n label: \"Stripe API\",\n message: \"Could not verify (network error)\",\n };\n }\n}\n\n/**\n * Verify Resend API key\n */\nexport async function checkResendApiKey(projectDir: string): Promise<CheckResult> {\n const env = await readEnvLocal(projectDir);\n const apiKey = env.RESEND_API_KEY;\n\n if (!apiKey) {\n return {\n status: \"fail\",\n label: \"Resend API\",\n message: \"RESEND_API_KEY not configured\",\n fix: \"Run: scaffoldry setup email\",\n };\n }\n\n try {\n const response = await fetch(\"https://api.resend.com/domains\", {\n headers: {\n Authorization: `Bearer ${apiKey}`,\n },\n });\n\n if (response.ok) {\n return { status: \"pass\", label: \"Resend API\" };\n }\n\n if (response.status === 401) {\n return {\n status: \"fail\",\n label: \"Resend API\",\n message: \"Invalid API key (401 Unauthorized)\",\n fix: \"Run: scaffoldry setup email\",\n };\n }\n\n return {\n status: \"warn\",\n label: \"Resend API\",\n message: `API returned ${response.status}`,\n };\n } catch {\n return {\n status: \"warn\",\n label: \"Resend API\",\n message: \"Could not verify (network error)\",\n };\n }\n}\n\n/**\n * Check if project has pending migrations\n */\nexport async function checkPendingMigrations(projectDir: string): Promise<CheckResult> {\n const migrationsDir = path.join(projectDir, \"packages\", \"database\", \"drizzle\");\n\n if (!(await fs.pathExists(migrationsDir))) {\n return { status: \"pass\", label: \"Migrations\", message: \"No migrations directory\" };\n }\n\n // This is a simplified check - in reality you'd compare against _drizzle_migrations table\n return {\n status: \"pass\",\n label: \"Migrations\",\n message: \"Run `scaffoldry migrate` to check status\",\n };\n}\n\n/**\n * Check if .env.local exists\n */\nexport async function checkEnvLocalExists(projectDir: string): Promise<CheckResult> {\n const envPath = path.join(projectDir, \".env.local\");\n\n if (await fs.pathExists(envPath)) {\n return { status: \"pass\", label: \".env.local file\" };\n }\n\n return {\n status: \"fail\",\n label: \".env.local file\",\n message: \"Not found\",\n fix: \"Run: scaffoldry setup all\",\n };\n}\n\n/**\n * Run all environment checks\n */\nexport async function runEnvironmentChecks(): Promise<CheckResult[]> {\n const results = await Promise.all([\n checkNodeVersion(),\n checkPnpmVersion(),\n checkGitInstalled(),\n checkStripeCli(),\n ]);\n\n return results;\n}\n\n/**\n * Run all project configuration checks\n */\nexport async function runConfigurationChecks(projectDir: string): Promise<CheckResult[]> {\n const results: CheckResult[] = [];\n\n results.push(await checkEnvLocalExists(projectDir));\n results.push(await checkEnvVar(projectDir, \"DATABASE_URL\"));\n results.push(await checkEnvVar(projectDir, \"STRIPE_SECRET_KEY\"));\n results.push(await checkEnvVar(projectDir, \"STRIPE_WEBHOOK_SECRET\"));\n results.push(await checkEnvVar(projectDir, \"RESEND_API_KEY\"));\n results.push(await checkEnvVar(projectDir, \"AUTH_SECRET\"));\n\n return results;\n}\n\n/**\n * Run all service connectivity checks\n */\nexport async function runServiceChecks(projectDir: string): Promise<CheckResult[]> {\n const results = await Promise.all([\n checkDatabaseConnection(projectDir),\n checkStripeApiKey(projectDir),\n checkResendApiKey(projectDir),\n ]);\n\n return results;\n}\n","import ora from \"ora\";\nimport {\n printBox,\n printStep,\n printSuccess,\n printInfo,\n printError,\n maybeOpenBrowser,\n promptSecret,\n promptConfirm,\n saveWizardResults,\n type WizardResult,\n} from \"./base.js\";\n\nconst STRIPE_DASHBOARD_URL = \"https://dashboard.stripe.com/apikeys\";\nconst STRIPE_WEBHOOK_URL = \"https://dashboard.stripe.com/webhooks\";\n\ninterface StripeAccountInfo {\n id: string;\n businessName?: string;\n email?: string;\n}\n\n/**\n * Verify Stripe API key by calling the API\n */\nasync function verifyStripeKey(apiKey: string): Promise<{ valid: boolean; account?: StripeAccountInfo; error?: string }> {\n try {\n const response = await fetch(\"https://api.stripe.com/v1/account\", {\n headers: {\n Authorization: `Bearer ${apiKey}`,\n },\n });\n\n if (response.ok) {\n const data = await response.json() as {\n id: string;\n business_profile?: { name?: string };\n email?: string;\n };\n const account: StripeAccountInfo = { id: data.id };\n if (data.business_profile?.name) {\n account.businessName = data.business_profile.name;\n }\n if (data.email) {\n account.email = data.email;\n }\n return { valid: true, account };\n }\n\n const errorData = await response.json() as { error?: { message?: string } };\n return {\n valid: false,\n error: errorData.error?.message || `API returned ${response.status}`,\n };\n } catch (error) {\n return {\n valid: false,\n error: error instanceof Error ? error.message : \"Network error\",\n };\n }\n}\n\n/**\n * Validate Stripe secret key format\n */\nfunction isValidStripeSecretKey(key: string): boolean {\n return /^sk_(test|live)_[A-Za-z0-9]+$/.test(key);\n}\n\n/**\n * Validate Stripe webhook secret format\n */\nfunction isValidWebhookSecret(secret: string): boolean {\n return /^whsec_[A-Za-z0-9]+$/.test(secret);\n}\n\n/**\n * Run the Stripe setup wizard\n */\nexport async function stripeWizard(projectDir: string): Promise<WizardResult> {\n console.log();\n printBox(\"Stripe Setup\", [\n \"Let's configure Stripe for your billing system.\",\n \"\",\n \"You'll need:\",\n \"• Stripe Secret Key (sk_test_... or sk_live_...)\",\n \"• Webhook Signing Secret (whsec_...)\",\n ]);\n\n // Step 1: Get API key\n printStep(1, \"Get your API keys\");\n console.log(\" 1. Go to: https://dashboard.stripe.com/apikeys\");\n console.log(' 2. Copy your \"Secret key\" (starts with sk_test_ or sk_)');\n console.log();\n\n await maybeOpenBrowser(STRIPE_DASHBOARD_URL, \"Stripe dashboard\");\n console.log();\n\n const secretKey = await promptSecret(\"Paste your Stripe Secret Key:\");\n\n if (!secretKey) {\n printError(\"Stripe secret key is required\");\n return { success: false, message: \"Cancelled\" };\n }\n\n if (!isValidStripeSecretKey(secretKey)) {\n printError(\"Invalid key format. Expected: sk_test_... or sk_live_...\");\n return { success: false, message: \"Invalid key format\" };\n }\n\n // Verify the key\n const spinner = ora(\"Verifying with Stripe API...\").start();\n printInfo(\"Calling stripe.account.retrieve()...\");\n\n const verification = await verifyStripeKey(secretKey);\n\n if (!verification.valid) {\n spinner.fail(\"Key verification failed\");\n printError(verification.error || \"Unknown error\");\n return { success: false, message: verification.error || \"Verification failed\" };\n }\n\n const accountName = verification.account?.businessName || verification.account?.email || verification.account?.id;\n spinner.succeed(`Key verified! Account: ${accountName}`);\n\n // Step 2: Webhook secret\n printStep(2, \"Webhook endpoint\");\n console.log(\" For local development, we'll use Stripe CLI.\");\n console.log(\" For production, add: https://your-domain.com/api/webhooks/stripe\");\n console.log();\n console.log(\" To get your webhook signing secret:\");\n console.log(\" • Local dev: Run `stripe listen` to get whsec_...\");\n console.log(\" • Production: Copy from Stripe Dashboard > Webhooks\");\n console.log();\n\n const { setupWebhook } = { setupWebhook: await promptConfirm(\"Do you have a webhook signing secret?\", false) };\n\n let webhookSecret: string | undefined;\n\n if (setupWebhook) {\n await maybeOpenBrowser(STRIPE_WEBHOOK_URL, \"Stripe webhooks\");\n console.log();\n\n webhookSecret = await promptSecret(\"Paste your Webhook Signing Secret:\");\n\n if (webhookSecret && !isValidWebhookSecret(webhookSecret)) {\n printError(\"Invalid webhook secret format. Expected: whsec_...\");\n webhookSecret = undefined;\n }\n }\n\n if (!webhookSecret) {\n printInfo(\"Skipping webhook secret for now.\");\n printInfo(\"For local dev, run: stripe listen --forward-to localhost:3000/api/webhooks/stripe\");\n webhookSecret = \"whsec_PLACEHOLDER_RUN_STRIPE_LISTEN\";\n }\n\n // Save results\n const envVars: Record<string, string> = {\n STRIPE_SECRET_KEY: secretKey,\n STRIPE_WEBHOOK_SECRET: webhookSecret,\n };\n\n // Also set publishable key if we can infer it\n const isTestMode = secretKey.startsWith(\"sk_test_\");\n if (isTestMode) {\n console.log();\n printInfo(\"Tip: Also add your publishable key for client-side Stripe.js\");\n const publishableKey = await promptSecret(\"Paste your Stripe Publishable Key (optional):\");\n if (publishableKey && publishableKey.startsWith(\"pk_test_\")) {\n envVars.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY = publishableKey;\n }\n }\n\n await saveWizardResults(projectDir, envVars);\n\n console.log();\n printSuccess(\"Stripe setup complete!\");\n\n if (webhookSecret === \"whsec_PLACEHOLDER_RUN_STRIPE_LISTEN\") {\n console.log();\n printInfo(\"Next: Run `scaffoldry dev` to start with Stripe webhook forwarding\");\n }\n\n return {\n success: true,\n envVars,\n };\n}\n","import prompts from \"prompts\";\nimport chalk from \"chalk\";\nimport { getConfigValue, setConfigValue } from \"../utils/config.js\";\nimport { writeEnvLocal } from \"../utils/env.js\";\nimport { logger } from \"../utils/logger.js\";\nimport { openBrowser } from \"../utils/platform.js\";\n\nexport interface WizardResult {\n success: boolean;\n envVars?: Record<string, string>;\n message?: string;\n}\n\n/**\n * Print a styled box with a title and content\n */\nexport function printBox(title: string, content: string[]): void {\n const width = 61;\n const topBorder = \"┌\" + \"─\".repeat(width) + \"┐\";\n const bottomBorder = \"└\" + \"─\".repeat(width) + \"┘\";\n\n console.log(chalk.cyan(topBorder));\n console.log(chalk.cyan(\"│\") + \" \".repeat(width) + chalk.cyan(\"│\"));\n console.log(chalk.cyan(\"│\") + \" \" + chalk.bold(title).padEnd(width - 3) + chalk.cyan(\"│\"));\n console.log(chalk.cyan(\"│\") + \" \".repeat(width) + chalk.cyan(\"│\"));\n\n for (const line of content) {\n const paddedLine = \" \" + line;\n console.log(chalk.cyan(\"│\") + paddedLine.padEnd(width) + chalk.cyan(\"│\"));\n }\n\n console.log(chalk.cyan(\"│\") + \" \".repeat(width) + chalk.cyan(\"│\"));\n console.log(chalk.cyan(bottomBorder));\n}\n\n/**\n * Print a step header\n */\nexport function printStep(stepNumber: number, title: string): void {\n console.log();\n console.log(chalk.bold(` STEP ${stepNumber}: ${title}`));\n console.log(\" \" + \"─\".repeat(53));\n}\n\n/**\n * Print a success message with checkmark\n */\nexport function printSuccess(message: string): void {\n console.log(chalk.green(\" ✓ \" + message));\n}\n\n/**\n * Print an info message with arrow\n */\nexport function printInfo(message: string): void {\n console.log(chalk.blue(\" → \" + message));\n}\n\n/**\n * Print a warning message\n */\nexport function printWarning(message: string): void {\n console.log(chalk.yellow(\" ⚠ \" + message));\n}\n\n/**\n * Print an error message\n */\nexport function printError(message: string): void {\n console.log(chalk.red(\" ✗ \" + message));\n}\n\n/**\n * Ask to open URL in browser (with preference caching)\n */\nexport async function maybeOpenBrowser(url: string, description: string): Promise<void> {\n const autoOpen = await getConfigValue(\"openBrowserAutomatically\");\n\n if (autoOpen === true) {\n await openBrowser(url);\n printInfo(`Opened: ${url}`);\n return;\n }\n\n if (autoOpen === false) {\n printInfo(`URL: ${url}`);\n return;\n }\n\n // First time - ask preference\n const { openIt, remember } = await prompts([\n {\n type: \"confirm\",\n name: \"openIt\",\n message: `Open ${description} in browser?`,\n initial: true,\n },\n {\n type: \"confirm\",\n name: \"remember\",\n message: \"Remember this preference?\",\n initial: true,\n },\n ]);\n\n if (remember) {\n await setConfigValue(\"openBrowserAutomatically\", openIt);\n }\n\n if (openIt) {\n await openBrowser(url);\n printInfo(`Opened: ${url}`);\n } else {\n printInfo(`URL: ${url}`);\n }\n}\n\n\n/**\n * Prompt for a secret value (masked input)\n */\nexport async function promptSecret(message: string): Promise<string | undefined> {\n const { value } = await prompts({\n type: \"password\",\n name: \"value\",\n message,\n });\n\n return value;\n}\n\n/**\n * Prompt for a text value\n */\nexport async function promptText(\n message: string,\n options?: { initial?: string; validate?: (value: string) => boolean | string }\n): Promise<string | undefined> {\n const { value } = await prompts({\n type: \"text\",\n name: \"value\",\n message,\n initial: options?.initial,\n validate: options?.validate,\n });\n\n return value;\n}\n\n/**\n * Prompt for confirmation\n */\nexport async function promptConfirm(message: string, initial = true): Promise<boolean> {\n const { value } = await prompts({\n type: \"confirm\",\n name: \"value\",\n message,\n initial,\n });\n\n return value ?? false;\n}\n\n/**\n * Save wizard results to .env.local\n */\nexport async function saveWizardResults(\n projectDir: string,\n envVars: Record<string, string>\n): Promise<void> {\n await writeEnvLocal(projectDir, envVars);\n logger.newLine();\n printSuccess(\"Environment variables saved to .env.local\");\n}\n\n/**\n * Format a masked secret for display\n */\nexport function maskSecret(value: string): string {\n if (value.length <= 8) {\n return \"••••••••\";\n }\n return `${value.slice(0, 4)}${\"•\".repeat(Math.min(12, value.length - 8))}${value.slice(-4)}`;\n}\n","import ora from \"ora\";\nimport {\n printBox,\n printStep,\n printSuccess,\n printInfo,\n printError,\n maybeOpenBrowser,\n promptSecret,\n saveWizardResults,\n type WizardResult,\n} from \"./base.js\";\n\nconst NEON_DASHBOARD_URL = \"https://console.neon.tech\";\n\n/**\n * Verify database connection by executing a simple query\n */\nasync function verifyDatabaseConnection(connectionString: string): Promise<{ valid: boolean; error?: string }> {\n try {\n const { neon } = await import(\"@neondatabase/serverless\");\n const sql = neon(connectionString);\n await sql`SELECT 1 as test`;\n return { valid: true };\n } catch (error) {\n return {\n valid: false,\n error: error instanceof Error ? error.message : \"Connection failed\",\n };\n }\n}\n\n/**\n * Basic validation for PostgreSQL connection string format\n */\nfunction isValidConnectionString(url: string): boolean {\n try {\n const parsed = new URL(url);\n return (\n (parsed.protocol === \"postgres:\" || parsed.protocol === \"postgresql:\") &&\n parsed.hostname.length > 0\n );\n } catch {\n return false;\n }\n}\n\n/**\n * Extract database host for display\n */\nfunction extractHost(url: string): string {\n try {\n const parsed = new URL(url);\n return parsed.hostname;\n } catch {\n return \"unknown\";\n }\n}\n\n/**\n * Run the Database setup wizard\n */\nexport async function databaseWizard(projectDir: string): Promise<WizardResult> {\n console.log();\n printBox(\"Database Setup\", [\n \"Let's configure your Neon PostgreSQL database.\",\n \"\",\n \"Scaffoldry uses Neon for serverless PostgreSQL.\",\n \"It's free to start and scales automatically.\",\n ]);\n\n // Step 1: Get or create Neon account\n printStep(1, \"Get your Neon connection string\");\n console.log(\" 1. Sign in at: https://console.neon.tech\");\n console.log(\" 2. Create a project (or select existing)\");\n console.log(\" 3. Copy the connection string from the Dashboard\");\n console.log(\" (Format: postgresql://user:pass@host/dbname)\");\n console.log();\n\n await maybeOpenBrowser(NEON_DASHBOARD_URL, \"Neon console\");\n console.log();\n\n const connectionString = await promptSecret(\"Paste your DATABASE_URL:\");\n\n if (!connectionString) {\n printError(\"Database connection string is required\");\n return { success: false, message: \"Cancelled\" };\n }\n\n if (!isValidConnectionString(connectionString)) {\n printError(\"Invalid connection string format\");\n printInfo(\"Expected: postgresql://user:password@host/database\");\n return { success: false, message: \"Invalid format\" };\n }\n\n // Verify the connection\n const spinner = ora(\"Verifying database connection...\").start();\n printInfo(\"Executing: SELECT 1\");\n\n const verification = await verifyDatabaseConnection(connectionString);\n\n if (!verification.valid) {\n spinner.fail(\"Connection failed\");\n printError(verification.error || \"Unknown error\");\n\n // Provide helpful troubleshooting tips\n console.log();\n printInfo(\"Troubleshooting tips:\");\n console.log(\" • Check that your connection string is correct\");\n console.log(\" • Ensure your IP is allowed (Neon > Settings > IP Allow)\");\n console.log(\" • For Neon, make sure SSL is enabled (add ?sslmode=require)\");\n\n return { success: false, message: verification.error || \"Connection failed\" };\n }\n\n const host = extractHost(connectionString);\n spinner.succeed(`Connected to ${host}!`);\n\n // Save results\n const envVars: Record<string, string> = {\n DATABASE_URL: connectionString,\n };\n\n await saveWizardResults(projectDir, envVars);\n\n console.log();\n printSuccess(\"Database setup complete!\");\n console.log();\n printInfo(\"Next: Run `scaffoldry migrate` to apply database migrations\");\n\n return {\n success: true,\n envVars,\n };\n}\n","import ora from \"ora\";\nimport {\n printBox,\n printStep,\n printSuccess,\n printInfo,\n printError,\n maybeOpenBrowser,\n promptSecret,\n promptText,\n saveWizardResults,\n type WizardResult,\n} from \"./base.js\";\n\nconst RESEND_DASHBOARD_URL = \"https://resend.com/api-keys\";\n\ninterface ResendDomain {\n id: string;\n name: string;\n status: string;\n}\n\n/**\n * Verify Resend API key by listing domains\n */\nasync function verifyResendKey(apiKey: string): Promise<{ valid: boolean; domains?: ResendDomain[]; error?: string }> {\n try {\n const response = await fetch(\"https://api.resend.com/domains\", {\n headers: {\n Authorization: `Bearer ${apiKey}`,\n },\n });\n\n if (response.ok) {\n const data = await response.json() as { data?: ResendDomain[] };\n return {\n valid: true,\n domains: data.data || [],\n };\n }\n\n if (response.status === 401) {\n return {\n valid: false,\n error: \"Invalid API key (401 Unauthorized)\",\n };\n }\n\n return {\n valid: false,\n error: `API returned ${response.status}`,\n };\n } catch (error) {\n return {\n valid: false,\n error: error instanceof Error ? error.message : \"Network error\",\n };\n }\n}\n\n/**\n * Validate Resend API key format\n */\nfunction isValidResendKey(key: string): boolean {\n return /^re_[A-Za-z0-9]+$/.test(key);\n}\n\n/**\n * Run the Email setup wizard\n */\nexport async function emailWizard(projectDir: string): Promise<WizardResult> {\n console.log();\n printBox(\"Email Setup\", [\n \"Let's configure Resend for transactional emails.\",\n \"\",\n \"Resend handles:\",\n \"• Password reset emails\",\n \"• Magic link authentication\",\n \"• Welcome emails and notifications\",\n ]);\n\n // Step 1: Get API key\n printStep(1, \"Get your Resend API key\");\n console.log(\" 1. Sign in at: https://resend.com\");\n console.log(\" 2. Go to API Keys in the dashboard\");\n console.log(\" 3. Create a new API key (or use existing)\");\n console.log(\" (Format: re_...)\");\n console.log();\n\n await maybeOpenBrowser(RESEND_DASHBOARD_URL, \"Resend dashboard\");\n console.log();\n\n const apiKey = await promptSecret(\"Paste your Resend API Key:\");\n\n if (!apiKey) {\n printError(\"Resend API key is required\");\n return { success: false, message: \"Cancelled\" };\n }\n\n if (!isValidResendKey(apiKey)) {\n printError(\"Invalid API key format. Expected: re_...\");\n return { success: false, message: \"Invalid format\" };\n }\n\n // Verify the key\n const spinner = ora(\"Verifying with Resend API...\").start();\n printInfo(\"Calling resend.domains.list()...\");\n\n const verification = await verifyResendKey(apiKey);\n\n if (!verification.valid) {\n spinner.fail(\"API key verification failed\");\n printError(verification.error || \"Unknown error\");\n return { success: false, message: verification.error || \"Verification failed\" };\n }\n\n spinner.succeed(\"API key verified!\");\n\n // Show verified domains\n if (verification.domains && verification.domains.length > 0) {\n console.log();\n printInfo(\"Verified domains:\");\n for (const domain of verification.domains) {\n const status = domain.status === \"verified\" ? \"✓\" : \"⚠\";\n console.log(` ${status} ${domain.name} (${domain.status})`);\n }\n } else {\n console.log();\n printInfo(\"No domains configured yet.\");\n printInfo(\"For production, add your domain at https://resend.com/domains\");\n }\n\n // Step 2: Configure from address\n printStep(2, \"Configure sender address\");\n console.log(\" For testing, you can use: onboarding@resend.dev\");\n console.log(\" For production, use: noreply@your-verified-domain.com\");\n console.log();\n\n let defaultFrom = \"onboarding@resend.dev\";\n if (verification.domains && verification.domains.length > 0) {\n const verifiedDomain = verification.domains.find(d => d.status === \"verified\");\n if (verifiedDomain) {\n defaultFrom = `noreply@${verifiedDomain.name}`;\n }\n }\n\n const emailFrom = await promptText(\"Default sender email:\", { initial: defaultFrom });\n\n // Save results\n const envVars: Record<string, string> = {\n RESEND_API_KEY: apiKey,\n };\n\n if (emailFrom) {\n envVars.EMAIL_FROM = emailFrom;\n }\n\n await saveWizardResults(projectDir, envVars);\n\n console.log();\n printSuccess(\"Email setup complete!\");\n\n if (!verification.domains || verification.domains.length === 0) {\n console.log();\n printInfo(\"Important: Add a verified domain for production use\");\n printInfo(\"Visit: https://resend.com/domains\");\n }\n\n return {\n success: true,\n envVars,\n };\n}\n","import ora from \"ora\";\nimport {\n printBox,\n printStep,\n printSuccess,\n printInfo,\n printError,\n printWarning,\n maybeOpenBrowser,\n promptSecret,\n promptText,\n promptConfirm,\n saveWizardResults,\n type WizardResult,\n} from \"./base.js\";\n\nconst AWS_CONSOLE_URL = \"https://console.aws.amazon.com/s3\";\nconst AWS_IAM_URL = \"https://console.aws.amazon.com/iam\";\n\n/**\n * Verify S3 bucket access using native fetch (HEAD request to check bucket exists)\n * Note: This is a simplified check - in production you'd use AWS SDK\n */\nasync function verifyS3Access(\n bucket: string,\n region: string,\n accessKeyId: string,\n secretAccessKey: string\n): Promise<{ valid: boolean; error?: string }> {\n // For a proper verification, we'd need AWS SDK v3\n // For now, we'll do a basic format check and trust the user\n // In the actual implementation, you'd use @aws-sdk/client-s3\n\n try {\n // Simple validation - real verification would need AWS SDK\n if (!bucket || !region || !accessKeyId || !secretAccessKey) {\n return { valid: false, error: \"Missing required configuration\" };\n }\n\n // Basic format validation\n if (!/^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/.test(bucket)) {\n return { valid: false, error: \"Invalid bucket name format\" };\n }\n\n // Note: Real verification would use:\n // const { S3Client, HeadBucketCommand } = await import(\"@aws-sdk/client-s3\");\n // const client = new S3Client({ region, credentials: { accessKeyId, secretAccessKey } });\n // await client.send(new HeadBucketCommand({ Bucket: bucket }));\n\n return { valid: true };\n } catch (error) {\n return {\n valid: false,\n error: error instanceof Error ? error.message : \"Verification failed\",\n };\n }\n}\n\n/**\n * Run the Storage setup wizard\n */\nexport async function storageWizard(projectDir: string): Promise<WizardResult> {\n console.log();\n printBox(\"Storage Setup (AWS S3)\", [\n \"Let's configure AWS S3 for file storage.\",\n \"\",\n \"S3 is used for:\",\n \"• User file uploads\",\n \"• Profile images and avatars\",\n \"• Document storage\",\n \"\",\n \"Note: This feature is optional.\",\n ]);\n\n const proceed = await promptConfirm(\"Do you want to configure S3 storage?\", true);\n\n if (!proceed) {\n printInfo(\"Skipping S3 storage setup.\");\n return { success: true, message: \"Skipped\" };\n }\n\n // Step 1: Create or select S3 bucket\n printStep(1, \"Create or select an S3 bucket\");\n console.log(\" 1. Sign in to AWS Console\");\n console.log(\" 2. Go to S3 and create a bucket (or use existing)\");\n console.log(\" 3. Note the bucket name and region\");\n console.log();\n console.log(\" Recommended settings:\");\n console.log(\" • Block all public access: ON\");\n console.log(\" • Versioning: Optional\");\n console.log(\" • Default encryption: Enabled\");\n console.log();\n\n await maybeOpenBrowser(AWS_CONSOLE_URL, \"AWS S3 console\");\n console.log();\n\n const bucketName = await promptText(\"S3 Bucket name:\", {\n validate: (value) => {\n if (!value) return \"Bucket name is required\";\n if (!/^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/.test(value)) {\n return \"Invalid bucket name format\";\n }\n return true;\n },\n });\n\n if (!bucketName) {\n printError(\"Bucket name is required\");\n return { success: false, message: \"Cancelled\" };\n }\n\n const region = await promptText(\"AWS Region:\", {\n initial: \"us-east-1\",\n });\n\n if (!region) {\n printError(\"Region is required\");\n return { success: false, message: \"Cancelled\" };\n }\n\n // Step 2: Get IAM credentials\n printStep(2, \"Create IAM credentials\");\n console.log(\" 1. Go to IAM in AWS Console\");\n console.log(\" 2. Create a new user or use existing\");\n console.log(\" 3. Attach S3 access policy (or use AmazonS3FullAccess)\");\n console.log(\" 4. Create Access Key and copy credentials\");\n console.log();\n\n await maybeOpenBrowser(AWS_IAM_URL, \"AWS IAM console\");\n console.log();\n\n const accessKeyId = await promptText(\"AWS Access Key ID:\");\n\n if (!accessKeyId) {\n printError(\"Access Key ID is required\");\n return { success: false, message: \"Cancelled\" };\n }\n\n const secretAccessKey = await promptSecret(\"AWS Secret Access Key:\");\n\n if (!secretAccessKey) {\n printError(\"Secret Access Key is required\");\n return { success: false, message: \"Cancelled\" };\n }\n\n // Verify configuration\n const spinner = ora(\"Validating S3 configuration...\").start();\n\n const verification = await verifyS3Access(bucketName, region, accessKeyId, secretAccessKey);\n\n if (!verification.valid) {\n spinner.fail(\"Configuration validation failed\");\n printError(verification.error || \"Unknown error\");\n\n const continueAnyway = await promptConfirm(\"Save configuration anyway?\", false);\n if (!continueAnyway) {\n return { success: false, message: verification.error || \"Verification failed\" };\n }\n printWarning(\"Saving unverified configuration\");\n } else {\n spinner.succeed(\"Configuration validated!\");\n }\n\n // Save results\n const envVars: Record<string, string> = {\n S3_BUCKET_NAME: bucketName,\n S3_REGION: region,\n AWS_ACCESS_KEY_ID: accessKeyId,\n AWS_SECRET_ACCESS_KEY: secretAccessKey,\n };\n\n await saveWizardResults(projectDir, envVars);\n\n console.log();\n printSuccess(\"Storage setup complete!\");\n console.log();\n printInfo(\"Your files will be stored in:\");\n console.log(` s3://${bucketName} (${region})`);\n\n return {\n success: true,\n envVars,\n };\n}\n","import { logger } from \"../utils/index.js\";\nimport {\n stripeWizard,\n databaseWizard,\n emailWizard,\n storageWizard,\n printBox,\n printSuccess,\n printError,\n printInfo,\n type WizardResult,\n} from \"../wizards/index.js\";\n\nexport type SetupService = \"stripe\" | \"database\" | \"email\" | \"storage\" | \"all\";\n\nexport interface SetupOptions {\n service: SetupService;\n}\n\n/**\n * Get the current working directory as the project directory\n * In a real implementation, you might want to detect the project root\n */\nfunction getProjectDir(): string {\n return process.cwd();\n}\n\n/**\n * Run a single setup wizard\n */\nasync function runWizard(\n service: Exclude<SetupService, \"all\">,\n projectDir: string\n): Promise<WizardResult> {\n switch (service) {\n case \"stripe\":\n return stripeWizard(projectDir);\n case \"database\":\n return databaseWizard(projectDir);\n case \"email\":\n return emailWizard(projectDir);\n case \"storage\":\n return storageWizard(projectDir);\n }\n}\n\n/**\n * Setup command - configure services for your Scaffoldry project\n */\nexport async function setupCommand(options: SetupOptions): Promise<void> {\n const projectDir = getProjectDir();\n\n if (options.service === \"all\") {\n logger.newLine();\n printBox(\"Scaffoldry Setup\", [\n \"Let's configure all your services.\",\n \"\",\n \"We'll set up:\",\n \"• Database (Neon PostgreSQL)\",\n \"• Stripe (Payments)\",\n \"• Email (Resend)\",\n \"• Storage (AWS S3) - optional\",\n ]);\n\n const services: Exclude<SetupService, \"all\">[] = [\"database\", \"stripe\", \"email\", \"storage\"];\n const results: { service: string; success: boolean }[] = [];\n\n for (const service of services) {\n const result = await runWizard(service, projectDir);\n results.push({ service, success: result.success });\n\n if (!result.success && result.message !== \"Skipped\") {\n logger.newLine();\n printError(`${service} setup failed: ${result.message}`);\n\n // Ask if user wants to continue with other services\n const { continueSetup } = await import(\"prompts\").then((m) =>\n m.default({\n type: \"confirm\",\n name: \"continueSetup\",\n message: \"Continue with other services?\",\n initial: true,\n })\n );\n\n if (!continueSetup) {\n break;\n }\n }\n }\n\n // Summary\n logger.newLine();\n printBox(\"Setup Complete\", [\n \"Here's a summary of your configuration:\",\n \"\",\n ...results.map(({ service, success }) =>\n `${success ? \"✓\" : \"✗\"} ${service.charAt(0).toUpperCase() + service.slice(1)}`\n ),\n ]);\n\n const successCount = results.filter((r) => r.success).length;\n if (successCount === results.length) {\n logger.newLine();\n printSuccess(\"All services configured!\");\n printInfo(\"Run `scaffoldry doctor` to verify your configuration\");\n printInfo(\"Run `scaffoldry dev` to start development\");\n } else {\n logger.newLine();\n printInfo(`${successCount}/${results.length} services configured`);\n printInfo(\"Run `scaffoldry setup <service>` to configure remaining services\");\n }\n } else {\n const result = await runWizard(options.service, projectDir);\n\n if (result.success) {\n logger.newLine();\n printInfo(\"Run `scaffoldry doctor` to verify your full configuration\");\n }\n }\n}\n\n/**\n * Stripe setup subcommand\n */\nexport async function setupStripeCommand(): Promise<void> {\n await setupCommand({ service: \"stripe\" });\n}\n\n/**\n * Database setup subcommand\n */\nexport async function setupDatabaseCommand(): Promise<void> {\n await setupCommand({ service: \"database\" });\n}\n\n/**\n * Email setup subcommand\n */\nexport async function setupEmailCommand(): Promise<void> {\n await setupCommand({ service: \"email\" });\n}\n\n/**\n * Storage setup subcommand\n */\nexport async function setupStorageCommand(): Promise<void> {\n await setupCommand({ service: \"storage\" });\n}\n\n/**\n * All setup subcommand\n */\nexport async function setupAllCommand(): Promise<void> {\n await setupCommand({ service: \"all\" });\n}\n"],"mappings":"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","names":["fs","path","fs","path","os","CONFIG_DIR","path","CONFIG_DIR","fs","fs","path","path","fs","chalk","chalk","ora","ora","ora","ora","ora","ora"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
interface InitOptions {
|
|
2
|
+
configFile?: string | undefined;
|
|
3
|
+
skipPrompts?: boolean | undefined;
|
|
4
|
+
}
|
|
5
|
+
declare function initCommand(targetDir?: string, options?: InitOptions): Promise<void>;
|
|
6
|
+
|
|
7
|
+
declare const logger: {
|
|
8
|
+
info: (message: string) => void;
|
|
9
|
+
success: (message: string) => void;
|
|
10
|
+
warn: (message: string) => void;
|
|
11
|
+
error: (message: string) => void;
|
|
12
|
+
log: (message: string) => void;
|
|
13
|
+
newLine: () => void;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
interface ProjectConfig {
|
|
17
|
+
name: string;
|
|
18
|
+
description: string;
|
|
19
|
+
features: ProjectFeature[];
|
|
20
|
+
database: DatabaseProvider;
|
|
21
|
+
packageManager: PackageManager;
|
|
22
|
+
}
|
|
23
|
+
type ProjectFeature = "auth" | "billing" | "email" | "storage" | "jobs" | "webhooks" | "admin";
|
|
24
|
+
type DatabaseProvider = "neon" | "supabase" | "local";
|
|
25
|
+
type PackageManager = "pnpm" | "npm" | "yarn";
|
|
26
|
+
interface TemplateFile {
|
|
27
|
+
path: string;
|
|
28
|
+
content: string;
|
|
29
|
+
}
|
|
30
|
+
interface TemplateContext {
|
|
31
|
+
projectName: string;
|
|
32
|
+
projectDescription: string;
|
|
33
|
+
features: ProjectFeature[];
|
|
34
|
+
databaseProvider: DatabaseProvider;
|
|
35
|
+
packageManager: PackageManager;
|
|
36
|
+
hasAuth: boolean;
|
|
37
|
+
hasBilling: boolean;
|
|
38
|
+
hasEmail: boolean;
|
|
39
|
+
hasStorage: boolean;
|
|
40
|
+
hasJobs: boolean;
|
|
41
|
+
hasWebhooks: boolean;
|
|
42
|
+
hasAdmin: boolean;
|
|
43
|
+
}
|
|
44
|
+
interface CommandContext {
|
|
45
|
+
cwd: string;
|
|
46
|
+
verbose: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
declare function createTemplateContext(config: ProjectConfig): TemplateContext;
|
|
50
|
+
declare function toKebabCase(str: string): string;
|
|
51
|
+
declare function toPascalCase(str: string): string;
|
|
52
|
+
declare function toCamelCase(str: string): string;
|
|
53
|
+
declare function replaceTemplateVars(content: string, context: TemplateContext): string;
|
|
54
|
+
|
|
55
|
+
declare function generateProjectFiles(context: TemplateContext): TemplateFile[];
|
|
56
|
+
|
|
57
|
+
export { type CommandContext, type DatabaseProvider, type PackageManager, type ProjectConfig, type ProjectFeature, type TemplateContext, type TemplateFile, createTemplateContext, generateProjectFiles, initCommand, logger, replaceTemplateVars, toCamelCase, toKebabCase, toPascalCase };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import {
|
|
2
|
+
generateProjectFiles,
|
|
3
|
+
initCommand
|
|
4
|
+
} from "./chunk-AA2UXYNR.js";
|
|
5
|
+
import {
|
|
6
|
+
createTemplateContext,
|
|
7
|
+
logger,
|
|
8
|
+
replaceTemplateVars,
|
|
9
|
+
toCamelCase,
|
|
10
|
+
toKebabCase,
|
|
11
|
+
toPascalCase
|
|
12
|
+
} from "./chunk-XIP7YNKZ.js";
|
|
13
|
+
import "./chunk-WOS3F5LR.js";
|
|
14
|
+
export {
|
|
15
|
+
createTemplateContext,
|
|
16
|
+
generateProjectFiles,
|
|
17
|
+
initCommand,
|
|
18
|
+
logger,
|
|
19
|
+
replaceTemplateVars,
|
|
20
|
+
toCamelCase,
|
|
21
|
+
toKebabCase,
|
|
22
|
+
toPascalCase
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {
|
|
2
|
+
commandExists,
|
|
3
|
+
copyToClipboard,
|
|
4
|
+
detectPlatform,
|
|
5
|
+
getHomeDir,
|
|
6
|
+
getInstallInstructions,
|
|
7
|
+
getKillSignal,
|
|
8
|
+
getLineEnding,
|
|
9
|
+
getPlatformDisplayName,
|
|
10
|
+
getSpawnOptions,
|
|
11
|
+
getTermSignal,
|
|
12
|
+
isLinux,
|
|
13
|
+
isMacOS,
|
|
14
|
+
isWSL,
|
|
15
|
+
isWindows,
|
|
16
|
+
normalizePath,
|
|
17
|
+
openBrowser,
|
|
18
|
+
setupShutdownHandlers,
|
|
19
|
+
spawnCrossplatform
|
|
20
|
+
} from "./chunk-WOS3F5LR.js";
|
|
21
|
+
export {
|
|
22
|
+
commandExists,
|
|
23
|
+
copyToClipboard,
|
|
24
|
+
detectPlatform,
|
|
25
|
+
getHomeDir,
|
|
26
|
+
getInstallInstructions,
|
|
27
|
+
getKillSignal,
|
|
28
|
+
getLineEnding,
|
|
29
|
+
getPlatformDisplayName,
|
|
30
|
+
getSpawnOptions,
|
|
31
|
+
getTermSignal,
|
|
32
|
+
isLinux,
|
|
33
|
+
isMacOS,
|
|
34
|
+
isWSL,
|
|
35
|
+
isWindows,
|
|
36
|
+
normalizePath,
|
|
37
|
+
openBrowser,
|
|
38
|
+
setupShutdownHandlers,
|
|
39
|
+
spawnCrossplatform
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=platform-Z35MB2P5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {
|
|
2
|
+
setupAllCommand,
|
|
3
|
+
setupCommand,
|
|
4
|
+
setupDatabaseCommand,
|
|
5
|
+
setupEmailCommand,
|
|
6
|
+
setupStorageCommand,
|
|
7
|
+
setupStripeCommand
|
|
8
|
+
} from "./chunk-XIP7YNKZ.js";
|
|
9
|
+
import "./chunk-WOS3F5LR.js";
|
|
10
|
+
export {
|
|
11
|
+
setupAllCommand,
|
|
12
|
+
setupCommand,
|
|
13
|
+
setupDatabaseCommand,
|
|
14
|
+
setupEmailCommand,
|
|
15
|
+
setupStorageCommand,
|
|
16
|
+
setupStripeCommand
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=setup-L2PO5OVZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "scaffoldry",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "CLI for scaffolding Scaffoldry projects",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"registry": "https://registry.npmjs.org",
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"scaffoldry",
|
|
12
|
+
"saas",
|
|
13
|
+
"starter",
|
|
14
|
+
"boilerplate",
|
|
15
|
+
"nextjs",
|
|
16
|
+
"typescript",
|
|
17
|
+
"drizzle",
|
|
18
|
+
"stripe",
|
|
19
|
+
"auth"
|
|
20
|
+
],
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/scaffoldry/scaffoldry.git",
|
|
24
|
+
"directory": "packages/scaffoldry-cli"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://scaffoldry.com",
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/scaffoldry/scaffoldry/issues"
|
|
29
|
+
},
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"bin": {
|
|
32
|
+
"scaffoldry": "./dist/bin.js"
|
|
33
|
+
},
|
|
34
|
+
"main": "./dist/index.js",
|
|
35
|
+
"module": "./dist/index.js",
|
|
36
|
+
"types": "./dist/index.d.ts",
|
|
37
|
+
"exports": {
|
|
38
|
+
".": {
|
|
39
|
+
"types": "./dist/index.d.ts",
|
|
40
|
+
"import": "./dist/index.js"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"files": [
|
|
44
|
+
"dist"
|
|
45
|
+
],
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsup",
|
|
48
|
+
"test": "vitest run",
|
|
49
|
+
"test:watch": "vitest",
|
|
50
|
+
"typecheck": "tsc --noEmit"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"commander": "^12.1.0",
|
|
54
|
+
"prompts": "^2.4.2",
|
|
55
|
+
"chalk": "^5.4.1",
|
|
56
|
+
"ora": "^8.2.0",
|
|
57
|
+
"fs-extra": "^11.3.0",
|
|
58
|
+
"glob": "^10.4.0",
|
|
59
|
+
"@neondatabase/serverless": "^0.10.0",
|
|
60
|
+
"drizzle-orm": "^0.45.1",
|
|
61
|
+
"postgres": "^3.4.5"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@types/fs-extra": "^11.0.4",
|
|
65
|
+
"@types/prompts": "^2.4.9",
|
|
66
|
+
"tsup": "^8.5.0",
|
|
67
|
+
"typescript": "^5.8.3",
|
|
68
|
+
"vitest": "^1.6.1"
|
|
69
|
+
}
|
|
70
|
+
}
|