@cellajs/create-cella 0.1.7 → 0.2.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/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/cli.ts","../package.json","../src/constants.ts","../src/utils/validate-project-name.ts","../src/utils/extract-package-json-version-from-uri.ts","../src/utils/is-empty-directory.ts","../src/create.ts","../src/utils/run-package-manager-command.ts","../src/utils/clean-template.ts","../src/utils/run-git-command.ts","../src/add-remote.ts"],"sourcesContent":["#!/usr/bin/env tsx\n\nimport { basename, resolve } from 'node:path';\nimport { existsSync } from 'node:fs';\nimport colors from 'picocolors';\n\nimport { input, confirm, select } from '@inquirer/prompts';\n\nimport { cli } from './cli';\nimport { extractPackageJsonVersionFromUri } from './utils/extract-package-json-version-from-uri';\nimport { validateProjectName } from './utils/validate-project-name.ts';\nimport { isEmptyDirectory } from './utils/is-empty-directory.ts';\nimport { create } from './create.ts';\nimport { LOGO, VERSION, WEBSITE, AUTHOR, GITHUB, DESCRIPTION, TEMPLATE_URL } from './constants.ts';\n\ninterface CreateOptions {\n projectName: string;\n targetFolder: string;\n newBranchName?: string | null;\n skipInstall: boolean;\n skipGit: boolean;\n skipClean: boolean;\n skipGenerate: boolean;\n packageManager: string;\n}\n\nasync function main(): Promise<void> {\n console.info(LOGO);\n\n // Get the latest version of the template\n const templateVersion = await extractPackageJsonVersionFromUri(TEMPLATE_URL);\n\n // Display CLI version and created by information\n console.info();\n console.info(DESCRIPTION);\n console.info();\n console.info(`Cella version: ${colors.green(templateVersion)}`);\n console.info(`Cli version ${colors.green(VERSION)}`);\n console.info(`Created by ${AUTHOR}`);\n console.info(`${GITHUB} | ${WEBSITE}`);\n console.info();\n\n // Skip creating a new branch if --skipNewBranch flag is provided or git is skipped\n if (cli.options.skipNewBranch || cli.options.skipGit) {\n cli.createNewBranch = false;\n cli.newBranchName = null;\n }\n\n // Skip generating sql files if --skipGenerate flag is provided\n if (cli.options.skipGenerate === true) {\n cli.options.skipGenerate = true;\n }\n\n // Skip installing packages if --skipInstall flag is provided\n if (cli.options.skipInstall === true) {\n cli.options.skipInstall = true;\n }\n\n // Skip cleaning the template if --skipClean flag is provided\n if (cli.options.skipClean === true) {\n cli.options.skipClean = true;\n }\n\n // Skip initializing git if --skipGit flag is provided\n if (cli.options.skipGit === true) {\n cli.options.skipGit = true;\n }\n\n // Prompt for project name if not provided\n if (!cli.directory) {\n cli.directory = await input({\n message: 'Enter your project name',\n default: 'my-cella-app',\n validate: (name) => {\n const validation = validateProjectName(basename(resolve(name)));\n return validation.valid ? true : `Invalid project name: ${validation.problems[0]}`;\n },\n });\n }\n\n // Prompt to create a new branch besides the main branch (if not skipped)\n if (cli.createNewBranch === null) {\n cli.createNewBranch = await confirm({\n message: 'Would you like to create a new branch (besides \"main\")?',\n default: true,\n });\n }\n\n // Prompt for new branch name, only if user opted to create a new branch\n if (!cli.newBranchName && cli.createNewBranch) {\n cli.newBranchName = await input({\n message: 'Enter the new branch name',\n default: 'development',\n validate: (name) => {\n const validation = validateProjectName(basename(resolve(name)));\n return validation.valid ? true : `Invalid branch name: ${validation.problems[0]}`;\n },\n });\n }\n\n const targetFolder = resolve(cli.directory);\n const projectName = basename(targetFolder);\n\n // Check if the target folder exists and is not empty\n if (existsSync(targetFolder) && !(await isEmptyDirectory(targetFolder))) {\n const dirName = cli.directory === '.' ? 'Current directory' : `Target directory \"${targetFolder}\"`;\n const message = `${dirName} is not empty. Please choose how you would like to proceed:`;\n\n const action = await select({\n message,\n choices: [\n { name: 'Cancel and exit', value: 'cancel' },\n { name: 'Ignore existing files and continue', value: 'ignore' },\n ],\n });\n\n if (action === 'cancel') {\n process.exit(1);\n }\n }\n\n // Proceed with the project creation\n const createOptions: CreateOptions = {\n projectName,\n targetFolder,\n newBranchName: cli.newBranchName,\n skipInstall: cli.options.skipInstall,\n skipGit: cli.options.skipGit,\n skipClean: cli.options.skipClean,\n skipGenerate: cli.options.skipGenerate,\n packageManager: cli.packageManager,\n };\n\n await create(createOptions);\n}\n\nmain().catch(console.error);\n","import { basename, resolve } from 'node:path';\nimport { Command, InvalidArgumentError } from 'commander';\n\nimport { NAME, VERSION } from './constants.ts';\nimport { validateProjectName } from './utils/validate-project-name.ts';\n\n// Define types for CLI options\ninterface CLIOptions {\n skipNewBranch: boolean;\n skipClean: boolean;\n skipGit: boolean;\n skipInstall: boolean;\n skipGenerate: boolean;\n newBranchName?: string;\n}\n\n// Define CLI configuration\ninterface CLIConfig {\n options: CLIOptions;\n args: string[];\n directory: string | null;\n newBranchName: string | null;\n createNewBranch: boolean | null;\n packageManager: string;\n}\n\n// Initialize CLI variables\nlet directory: string | null = null;\nlet newBranchName: string | null = null;\nlet createNewBranch: boolean | null = null;\nconst packageManager = 'pnpm';\n\n// Set up the CLI command using Commander\nexport const command = new Command(NAME)\n .version(\n VERSION,\n '-v, --version',\n `Output the current version of ${NAME}.`\n )\n .argument('[directory]', 'The directory name for the new project.')\n .usage('[directory] [options]')\n .helpOption('-h, --help', 'Display this help message.')\n .option('--skip-new-branch', 'Skip creating a new branch during initialization.', false)\n .option('--skip-install', 'Skip the installation of packages.', false)\n .option('--skip-generate', 'Skip generating SQL files.', false)\n .option('--skip-clean', 'Skip cleaning the `cella` template.', false)\n .option('--skip-git', 'Skip initializing a git repository.', false)\n .option(\n '--new-branch-name <name>',\n 'Specify a new branch name to create and use.',\n (name: string) => {\n if (typeof name === 'string') {\n name = name.trim();\n }\n\n if (name) {\n const validation = validateProjectName(basename(resolve(name)));\n\n if (!validation.valid) {\n throw new InvalidArgumentError(\n `Invalid branch name: ${validation.problems[0]}`\n );\n }\n \n createNewBranch = true;\n newBranchName = name;\n }\n }\n )\n .action((name: string) => {\n if (typeof name === 'string') {\n name = name.trim();\n }\n\n if (name) {\n const validation = validateProjectName(basename(resolve(name)));\n\n if (!validation.valid) {\n throw new InvalidArgumentError(\n `Invalid project name: ${validation.problems[0]}`\n );\n }\n\n directory = name;\n }\n })\n .parse();\n\n// Gather the CLI options and arguments\nconst options: CLIOptions = command.opts<CLIOptions>({\n skipNewBranch: false,\n skipClean: false,\n skipGit: false,\n skipInstall: false,\n skipGenerate: false,\n});\n\n// Export the CLI configuration for use in other modules\nexport const cli: CLIConfig = {\n options,\n args: command.args,\n directory,\n newBranchName,\n createNewBranch,\n packageManager,\n};\n","{\n \"name\": \"@cellajs/create-cella\",\n \"version\": \"0.1.7\",\n \"private\": false,\n \"license\": \"MIT\",\n \"description\": \"Cella is a TypeScript template to create web apps with sync and offline capabilities.\",\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/cellajs/cella\",\n \"directory\": \"cli/create-cella\"\n },\n \"homepage\": \"https://cellajs.com\",\n \"author\": \"CellaJS <info@cellajs.com>\",\n \"engines\": {\n \"node\": \">=20.14.0\"\n },\n \"type\": \"module\",\n \"main\": \"./src/index.ts\",\n \"bin\": {\n \"create-cella\": \"index.js\"\n },\n \"scripts\": {\n \"start\": \"tsx ./src/index.ts\",\n \"clean\": \"rimraf ./dist\",\n \"build\": \"tsup\",\n \"test-build\": \"pnpm run build && node index.js\",\n \"prepublishOnly\": \"pnpm run build\"\n },\n \"packageManager\": \"pnpm@9.11.0\",\n \"dependencies\": {\n \"@inquirer/prompts\": \"^6.0.1\",\n \"axios\": \"^1.8.3\",\n \"commander\": \"^12.1.0\",\n \"cross-spawn\": \"^7.0.3\",\n \"giget\": \"^1.2.3\",\n \"picocolors\": \"^1.1.0\",\n \"validate-npm-package-name\": \"^5.0.1\",\n \"yocto-spinner\": \"^0.1.0\"\n },\n \"devDependencies\": {\n \"tsup\": \"^8.3.5\",\n \"tsx\": \"^4.19.2\"\n }\n}\n","export const NAME = 'create-cella';\n\n// URL of the template repository\nexport const TEMPLATE_URL = 'github:cellajs/cella';\n\n// URL to the repository\nexport const CELLA_REMOTE_URL: string = 'git@github.com:cellajs/cella.git';\n\n// Import package.json dynamically for version and website information\nimport packageJson from '../package.json' assert { type: 'json' };\n\n// Export details from package.json\nexport const DESCRIPTION: string = packageJson.description;\nexport const VERSION: string = packageJson.version;\nexport const AUTHOR: string = packageJson.author;\nexport const WEBSITE: string = packageJson.homepage;\nexport const GITHUB: string = packageJson.repository.url;\n\n// Files or folders to be removed from the template after downloading\nexport const TO_REMOVE: string[] = [\n 'info',\n './cli/create-cella',\n];\n\n// Specific folder contents to be cleaned out from the template\nexport const TO_CLEAN: string[] = [\n './backend/drizzle',\n];\n\n// Files to copy/paste after downloading\nexport const TO_COPY: Record<string, string> = {\n './backend/.env.example': './backend/.env',\n './frontend/.env.example': './frontend/.env',\n './info/QUICKSTART.md': 'README.md',\n};\n\n// Files to be editted after downloading\nexport const TO_EDIT: Record<string, { regexMatch: RegExp; replaceWith: string }[]> = {\n './config/default.ts': [\n {\n regexMatch: /enabledAuthenticationStrategies:\\s*\\[[^\\]]+\\]\\s*as\\s*const,/g,\n replaceWith: \"enabledAuthenticationStrategies: ['password', 'passkey'] as const,\",\n },\n {\n regexMatch: /uploadEnabled\\:\\s*(true|false),/g,\n replaceWith: \"uploadEnabled: false,\",\n },\n {\n regexMatch: /enabledOauthProviders:\\s*\\[[^\\]]+\\]\\s*as\\s*const,/g,\n replaceWith: \"enabledOauthProviders: [] as const,\",\n },\n ],\n};\n// ASCII logo for the CLI output\nexport const LOGO = `\n _ _ \n ▒▓█████▓▒ ___ ___| | | __ _\n ▒▓█ █▓▒ / __/ _ \\\\ | |/ _\\` |\n ▒▓█ █▓▒ | (_| __/ | | (_| |\n ▒▓█████▓▒ \\\\___\\\\___|_|_|\\\\__,_|\n`;\n","import validate from 'validate-npm-package-name';\n\ninterface ValidationResult {\n valid: boolean;\n problems?: string[];\n}\n\nexport function validateProjectName(name: string): ValidationResult {\n const nameValidation = validate(name);\n\n if (nameValidation.validForNewPackages) {\n return { valid: true };\n }\n\n return {\n valid: false,\n problems: [\n ...(nameValidation.errors || []),\n ...(nameValidation.warnings || []),\n ],\n };\n}\n","import axios from 'axios';\n\n/**\n * Retrieves the version from the package.json file of a GitHub repository.\n * If the package.json file is not found or an error occurs, it returns 'unknown'.\n * \n * @param repositoryUrl {string} - The GitHub repository URL in the format 'github:user/repo'.\n * @param branch {string} - The branch name (defaults to 'main').\n * @returns {Promise<string>} - The version from the package.json file.\n */\nexport async function extractPackageJsonVersionFromUri(repositoryUrl: string, branch: string = 'main'): Promise<string> {\n // Extract owner and repo from the URL\n const [owner, repo] = repositoryUrl.replace('github:', '').split('/');\n \n // Construct the URL for the package.json file in the provided branch\n const packageJsonUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/package.json`;\n\n try {\n // Fetch the package.json file\n const response = await axios.get(packageJsonUrl);\n const packageJson = response.data;\n \n // Return the version from the package.json, or 'unknown' if not found\n return packageJson.version || 'unknown';\n } catch (error) {\n // If there's an error (file not found, etc.), return 'unknown'\n return 'unknown';\n }\n}","import { readdir } from 'node:fs/promises';\n\n/**\n * Checks if a directory is empty or only contains a .git directory.\n * \n * @param path - The path of the directory to check.\n * @returns Resolves to true if the directory is empty or contains only a .git folder, false otherwise.\n * @throws Throws an error if the path is not a directory or if there's an issue reading the directory.\n */\nexport async function isEmptyDirectory(path: string): Promise<boolean> {\n const files = await readdir(path);\n\n // Check if directory is empty or contains only the .git directory\n return files.length === 0 || (files.length === 1 && files[0] === '.git');\n}\n","import { mkdir } from 'node:fs/promises';\nimport { existsSync } from 'node:fs';\nimport { join, relative } from 'node:path';\nimport colors from 'picocolors';\nimport { downloadTemplate } from 'giget';\nimport yoctoSpinner from 'yocto-spinner';\n\nimport { TEMPLATE_URL } from './constants.ts';\n\nimport { install, generate } from './utils/run-package-manager-command.ts';\nimport { cleanTemplate } from './utils/clean-template.ts';\nimport { runGitCommand } from './utils/run-git-command.ts';\nimport { addRemote } from './add-remote.ts';\n\ninterface CreateOptions {\n projectName: string;\n targetFolder: string;\n newBranchName?: string | null;\n skipInstall: boolean;\n skipGit: boolean;\n skipClean: boolean;\n skipGenerate: boolean;\n packageManager: string;\n}\n\nexport async function create({\n projectName,\n targetFolder,\n newBranchName,\n skipInstall,\n skipGit,\n skipClean,\n skipGenerate,\n packageManager,\n}: CreateOptions): Promise<void> {\n // Save the original working directory\n const originalCwd = process.cwd();\n\n console.info();\n\n // Create the target folder if it doesn't exist\n const createFolderSpinner = yoctoSpinner({\n text: 'Creating project folder',\n }).start();\n\n await mkdir(targetFolder, { recursive: true });\n process.chdir(targetFolder);\n\n createFolderSpinner.success('Project folder created');\n\n // Download the template from the specified URL\n const downloadSpinner = yoctoSpinner({\n text: 'Downloading `cella` template',\n }).start();\n\n await downloadTemplate(TEMPLATE_URL, {\n cwd: process.cwd(),\n dir: targetFolder,\n force: true,\n provider: 'github',\n });\n\n downloadSpinner.success('`cella` template downloaded');\n\n // Clean the template if the skipClean flag is not set\n if (!skipClean) {\n const cleanSpinner = yoctoSpinner({\n text: 'cleaning `cella` template',\n }).start();\n\n try {\n await cleanTemplate({\n targetFolder,\n projectName,\n });\n cleanSpinner.success('`cella` template cleaned');\n } catch (e) {\n console.error(e);\n cleanSpinner.error('Failed to clean `cella` template');\n process.exit(1);\n }\n } else {\n console.info(`${colors.yellow('⚠')} --skip-clean > Skip cleaning \\`cella\\` template`);\n }\n\n // Install dependencies if the skipInstall flag is not set\n if (!skipInstall) {\n const installSpinner = yoctoSpinner({\n text: 'installing dependencies',\n }).start();\n\n try {\n await install(packageManager);\n installSpinner.success('Dependencies installed');\n } catch (e) {\n console.error(e);\n installSpinner.error('Failed to install dependencies');\n process.exit(1);\n }\n } else {\n console.info(`${colors.yellow('⚠')} --skip-install > Skip installing dependencies`);\n }\n\n // Generate SQL files if the skipGenerate flag is not set\n if (!skipGenerate) {\n const generateSpinner = yoctoSpinner({\n text: 'generating SQL files',\n }).start();\n\n try {\n await generate(packageManager);\n generateSpinner.success('SQL files generated');\n } catch (e) {\n console.error(e);\n generateSpinner.error('Failed to generate SQL files');\n process.exit(1);\n }\n } else {\n console.info(`${colors.yellow('⚠')} --skip-generate > Skip generating SQL files`);\n }\n\n // Initialize Git repository if skipGit flag is not set\n if (!skipGit) {\n const gitSpinner = yoctoSpinner({\n text: 'initializing git repository',\n }).start();\n\n const gitFolderPath = join(targetFolder, '.git');\n\n if (!existsSync(gitFolderPath)) {\n try {\n // Run Git commands to initialize the repository and make the first commit\n await runGitCommand({ targetFolder, command: 'init' });\n await runGitCommand({ targetFolder, command: 'add .' });\n await runGitCommand({ targetFolder, command: 'commit -m \"Initial commit\"' });\n\n // If a new branch name is specified, create and checkout the branch\n if (newBranchName) {\n await runGitCommand({ targetFolder, command: `branch ${newBranchName}` });\n await runGitCommand({ targetFolder, command: `checkout ${newBranchName}` });\n gitSpinner.success(`Git repository initialized, initial commit created, and new branch ${newBranchName} created`);\n } else {\n gitSpinner.success('Git repository initialized and initial commit created');\n }\n } catch (e) {\n console.error(e);\n gitSpinner.error('Failed to initialize Git repository or create branch');\n process.exit(1);\n }\n } else {\n gitSpinner.warning('Git repository already initialized > Skip git init');\n }\n } else {\n console.info(`${colors.yellow('⚠')} --skip-git > Skip git init`);\n }\n\n // Add Cella as upstream remote\n await addRemote({ targetFolder });\n\n // Final success message indicating project creation\n console.info();\n console.info(`${colors.green('Success')} Created ${projectName} at ${targetFolder}`);\n console.info();\n\n // Check if the working directory needs to be changed\n const needsCd = originalCwd !== targetFolder;\n const relativePath = relative(originalCwd, targetFolder);\n\n if (needsCd) {\n // Calculate the relative path between the original working directory and the target folder\n console.info('now go to your project using:');\n console.info(colors.cyan(` cd ${relativePath}`)); // Adding './' to make it clear it's a relative path\n console.info();\n }\n\n console.info(`${needsCd ? 'then ' : ''}quick start using pglite with:`);\n console.info(colors.cyan(` ${packageManager} quick`));\n console.info();\n\n console.info('Already have docker installed? Then you can run a full setup:');\n console.info(colors.cyan(` ${packageManager} docker`));\n console.info(colors.cyan(` ${packageManager} dev`));\n console.info(colors.cyan(` ${packageManager} seed`));\n console.info();\n\n console.info(`Once running, you can sign in using:`);\n console.info(`email: ${colors.greenBright('admin-test@cellajs.com')}`);\n console.info(`password: ${colors.greenBright('12345678')}`);\n console.info();\n console.info(`For more info, check out: ${relativePath}/README.md`);\n console.info(`Enjoy building ${projectName} using cella! 🎉`);\n console.info();\n}\n","import spawn from 'cross-spawn';\n\n/**\n * Executes a command using the specified package manager (e.g., pnpm).\n * \n * @param packageManager - The package manager to use (e.g., 'pnpm').\n * @param args - The arguments to pass to the package manager command.\n * @param env - Additional environment variables to set during command execution.\n * @returns A promise that resolves if the command executes successfully; otherwise, it rejects with an error message.\n */\nexport async function runPackageManagerCommand(\n packageManager: string,\n args: string[],\n env: Record<string, string> = {}\n): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn(packageManager, args, {\n env: {\n ...process.env,\n ...env,\n },\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n\n // Buffer for capturing stderr and stdout output\n let stderrBuffer = '';\n let stdoutBuffer = '';\n\n // Capture stderr output\n child.stderr?.on('data', (data: Buffer) => {\n stderrBuffer += data.toString();\n });\n\n // Capture stdout output\n child.stdout?.on('data', (data: Buffer) => {\n stdoutBuffer += data.toString();\n });\n\n // Handle process exit\n child.on('close', (code: number | null) => {\n if (code !== 0) {\n reject(\n `\"${packageManager} ${args.join(' ')}\" failed ${stdoutBuffer} ${stderrBuffer}`\n );\n return;\n }\n resolve();\n });\n });\n}\n\n/**\n * Installs dependencies using the specified package manager.\n * \n * @param packageManager - The package manager to use for installation (e.g., 'pnpm').\n * @returns A promise that resolves if the installation completes successfully; otherwise, it rejects with an error.\n */\nexport async function install(packageManager: string): Promise<void> {\n return runPackageManagerCommand(packageManager, ['install'], {\n NODE_ENV: 'development',\n });\n}\n\n/**\n * Generates SQL files using the specified package manager.\n * \n * @param packageManager - The package manager to use for generation (e.g., 'pnpm').\n * @returns A promise that resolves if the generation completes successfully; otherwise, it rejects with an error.\n */\nexport async function generate(packageManager: string): Promise<void> {\n return runPackageManagerCommand(packageManager, ['generate'], {\n NODE_ENV: 'development',\n });\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport colors from 'picocolors';\n\nimport { TO_CLEAN, TO_REMOVE, TO_COPY, TO_EDIT } from '../constants.ts';\n\n/**\n * Cleans the specified template by removing designated folders and files.\n * @param params - Parameters containing the target folder and project name.\n */\nexport async function cleanTemplate({\n targetFolder,\n projectName,\n}: {\n targetFolder: string;\n projectName: string;\n}): Promise<void> {\n // Change the current working directory to targetFolder if not already set\n if (process.cwd() !== targetFolder) {\n process.chdir(targetFolder);\n }\n\n return new Promise<void>(async (resolve, reject) => {\n try {\n // Copy specified files\n for (const [src, dest] of Object.entries(TO_COPY)) {\n const srcAbsolutePath = path.resolve(targetFolder, src);\n const destAbsolutePath = path.resolve(targetFolder, dest);\n await copyFile(srcAbsolutePath, destAbsolutePath);\n }\n\n // Clean specified folder contents\n await Promise.all(\n TO_CLEAN.map((folderPath) => {\n const absolutePath = path.resolve(targetFolder, folderPath);\n return removeFolderContents(absolutePath);\n })\n );\n\n // Remove specified files and folders\n await Promise.all(\n TO_REMOVE.map((filePath) => {\n const absolutePath = path.resolve(targetFolder, filePath);\n return removeFileOrFolder(absolutePath);\n })\n );\n\n // Edit specific files\n await Promise.all(Object.entries(TO_EDIT).map(async ([filePath, edits]) => {\n const absolutePath = path.resolve(targetFolder, filePath);\n await editFile(absolutePath, edits);\n })\n );\n\n resolve();\n } catch (err) {\n reject(`Error during the cleaning process: ${err}`);\n }\n });\n}\n\n/**\n * Removes all contents within a specified folder.\n * @param folderPath - The path of the folder to clean.\n */\nexport async function removeFolderContents(folderPath: string): Promise<void> {\n // List all files in the folder\n const files = await fs.readdir(folderPath);\n\n await Promise.all(\n files.map(async (file) => {\n const filePath = path.join(folderPath, file);\n\n // Get the file or folder statistics\n const stat = await fs.lstat(filePath);\n\n // If it's a directory, remove it and all its contents\n if (stat.isDirectory()) {\n await fs.rm(filePath, { recursive: true, force: true });\n } else {\n // If it's a file, remove it\n await fs.rm(filePath);\n }\n })\n );\n}\n\n/**\n * Removes a specified file or folder.\n * @param pathToRemove - The path to the file or folder to remove.\n */\nexport async function removeFileOrFolder(pathToRemove: string): Promise<void> {\n await fs.rm(pathToRemove, { recursive: true, force: true });\n}\n\n/**\n * Helper function to copy files if the source exists.\n * @param src - The source file path.\n * @param dest - The destination file path.\n */\nexport async function copyFile(src: string, dest: string): Promise<void> {\n try {\n // Check if the source file exists\n await fs.access(src);\n\n // Ensure the destination directory exists\n await fs.mkdir(path.dirname(dest), { recursive: true });\n\n // Copy the file\n await fs.copyFile(src, dest);\n } catch (err: any) {\n if (err.code === 'ENOENT') {\n console.info(`\\n${colors.yellow('⚠')} Source file \"${src}\" does not exist > Skip copy`);\n } else {\n throw err;\n }\n }\n}\n\n/**\n * Helper function edit a file by applying regex replacements.\n * @param filePath - The path of the file to edit.\n * @param edits - The list of edits to apply.\n */\nexport async function editFile(filePath: string, edits: Array<{regexMatch: RegExp; replaceWith: string }>): Promise<void> {\n try {\n await fs.access(filePath);\n\n // Read the existing file content\n const fileContent = await fs.readFile(filePath, 'utf8');\n let updatedContent = fileContent;\n\n // Apply each edit to the content\n edits.forEach(({ regexMatch, replaceWith }) => {\n updatedContent = updatedContent.replace(regexMatch, replaceWith);\n });\n\n // Write the updated content back to the file\n if (fileContent !== updatedContent) {\n await fs.writeFile(filePath, updatedContent, 'utf8');\n }\n\n } catch (err: any) {\n if (err.code === 'ENOENT') {\n console.info(`\\n${colors.yellow('⚠')} Source file \"${filePath}\" does not exist > Skip edit`);\n } else {\n throw err;\n }\n }\n}","import { spawn } from 'node:child_process';\n\ntype GitCommandType = 'merge' | 'diff' | 'other';\n\ninterface RunGitCommandOptions {\n targetFolder: string;\n command: string;\n}\n\n// Helper to determine the type of Git command for custom handling\nfunction getGitCommandType(command: string): GitCommandType {\n if (command.startsWith('merge')) return 'merge';\n if (command.startsWith('diff')) return 'diff';\n return 'other';\n}\n\n// Helper to check if a Git command is successful\nfunction isGitCommandSuccess(gitCommand: GitCommandType, code: number | null, errOutput: string): boolean {\n if (gitCommand === 'merge') return code === 0 || (code === 1 && !errOutput);\n if (gitCommand === 'diff') return code === 0 || (code === 2 && !errOutput);\n return code === 0;\n}\n\n// Function to run a Git command and handle its output\nexport async function runGitCommand({ targetFolder, command }: RunGitCommandOptions): Promise<string> {\n return new Promise((resolve, reject) => {\n const gitCommand = getGitCommandType(command);\n const child = spawn(`git ${command}`, [], { cwd: targetFolder, shell: true, timeout: 60000 });\n\n let output = '';\n let errOutput = '';\n\n child.on('error', (error: Error) => {\n reject(error);\n });\n\n child.on('exit', (code: number | null) => {\n if (isGitCommandSuccess(gitCommand, code, errOutput)) {\n resolve(output.trim());\n } else {\n reject(\n `Git ${gitCommand} command failed with exit code ${code}, stderr: \"${errOutput.trim()}\", stdout: \"${output.trim()}\"`\n );\n }\n });\n\n // Collect stdout data\n child.stdout.on('data', (data: Buffer) => {\n output += data.toString();\n });\n\n // Collect stderr data\n child.stderr.on('data', (data: Buffer) => {\n errOutput += data.toString();\n });\n });\n}\n","import yoctoSpinner from 'yocto-spinner';\nimport { runGitCommand } from './utils/run-git-command.ts';\nimport { CELLA_REMOTE_URL } from './constants.ts';\n\ninterface AddRemoteOptions {\n targetFolder: string;\n remoteUrl?: string; // Optional, defaults to CELLA_REMOTE_URL\n remoteName?: string; // Optional, defaults to 'upstream'\n}\n\nexport async function addRemote({\n targetFolder,\n remoteUrl = CELLA_REMOTE_URL,\n remoteName = 'upstream',\n}: AddRemoteOptions): Promise<void> {\n\n // Spinner for adding remote\n const remoteSpinner = yoctoSpinner({\n text: 'Adding remote',\n }).start();\n\n try {\n // Check if the remote exists\n let remote: string | null = null;\n\n try {\n remote = await runGitCommand({ targetFolder, command: `remote get-url ${remoteName}` });\n } catch (error: any) {\n // If the remote doesn't exist, it throws a fatal error\n remote = null;\n }\n\n // Add or update the remote if it doesn't exist or differs from `remoteUrl`\n if (!remote) {\n await runGitCommand({ targetFolder, command: `remote add ${remoteName} ${remoteUrl}` });\n remoteSpinner.success('Remote added successfully.');\n } else if (remote !== remoteUrl) {\n // Remove existing remote and set the new URL\n await runGitCommand({ targetFolder, command: `remote remove ${remoteName}` });\n await runGitCommand({ targetFolder, command: `remote add ${remoteName} ${remoteUrl}` });\n remoteSpinner.success('Remote updated successfully.');\n } else {\n remoteSpinner.success('Remote is already configured correctly.');\n }\n } catch (error) {\n console.error(error);\n remoteSpinner.error('Failed to add remote.');\n process.exit(1);\n }\n}\n"],"mappings":"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","names":["basename","resolve","existsSync","colors","path","colors","yoctoSpinner","packageManager","resolve","resolve","spawn","command","resolve","newBranchName","packageManager","yoctoSpinner","colors","colors","basename","resolve","existsSync"]}
1
+ {"version":3,"sources":["../src/create-cella-cli.ts","../src/constants.ts","../package.json","../src/create.ts","../src/utils/git/command.ts","../src/add-remote.ts","../src/modules/cli/commands.ts","../src/utils/validate-project-name.ts","../src/modules/cli/display.ts","../src/utils/clean-template.ts","../src/utils/progress.ts","../src/utils/run-package-manager-command.ts","../src/utils/detect-used-ports.ts","../src/utils/extract-package-json-version-from-uri.ts","../src/utils/is-empty-directory.ts"],"sourcesContent":["#!/usr/bin/env tsx\n\nimport { existsSync } from 'node:fs';\nimport { basename, resolve } from 'node:path';\nimport { input, select } from '@inquirer/prompts';\nimport pc from 'picocolors';\nimport { TEMPLATE_URL } from '#/constants';\nimport { create } from '#/create';\nimport { type CreateOptions, cli, showWelcome } from '#/modules/cli';\nimport { detectUsedPorts, findNextOffset } from '#/utils/detect-used-ports';\nimport { extractPackageJsonVersionFromUri } from '#/utils/extract-package-json-version-from-uri';\nimport { isEmptyDirectory } from '#/utils/is-empty-directory';\nimport { validateProjectName } from '#/utils/validate-project-name';\n\nasync function main(): Promise<void> {\n // Get the latest version of the template\n const templateVersion = await extractPackageJsonVersionFromUri(TEMPLATE_URL);\n\n // Display CLI welcome banner\n showWelcome(templateVersion);\n\n // Shared theme to clear prompts after answering\n const promptTheme = { prefix: '', style: { answer: (text: string) => text } };\n const promptContext = { clearPromptOnDone: true };\n\n // Prompt for project name if not provided\n if (!cli.directory) {\n cli.directory = await input(\n {\n message: 'Enter your project name',\n default: 'my-cella-app',\n theme: promptTheme,\n validate: (name) => {\n const validation = validateProjectName(basename(resolve(name)));\n return validation.valid ? true : `Invalid project name: ${validation.problems?.[0] ?? 'unknown error'}`;\n },\n },\n promptContext,\n );\n }\n\n // Default to creating a 'development' working branch\n if (!cli.newBranchName) {\n cli.newBranchName = 'development';\n }\n\n const targetFolder = resolve(cli.directory);\n const projectName = basename(targetFolder)?.toLowerCase() || 'project';\n\n // Check if the target folder exists and is not empty\n if (existsSync(targetFolder) && !(await isEmptyDirectory(targetFolder))) {\n const dirName = cli.directory === '.' ? 'Current directory' : `Target directory \"${targetFolder}\"`;\n const message = `${dirName} is not empty. Please choose how you would like to proceed:`;\n\n const action = await select(\n {\n message,\n theme: promptTheme,\n choices: [\n { name: 'Cancel and exit', value: 'cancel' },\n { name: 'Ignore existing files and continue', value: 'ignore' },\n ],\n },\n promptContext,\n );\n\n if (action === 'cancel') {\n process.exit(1);\n }\n }\n\n // Scan sibling directories and prompt for port offset\n const portOffset = await promptPortOffset(targetFolder, promptTheme, promptContext);\n\n // Prompt for admin email\n const adminEmail = await input(\n {\n message: 'Admin email for initial seed user',\n default: `admin@${projectName}.com`,\n theme: promptTheme,\n },\n promptContext,\n );\n\n // Proceed with the project creation\n const createOptions: CreateOptions = {\n projectName,\n targetFolder,\n newBranchName: cli.newBranchName,\n packageManager: cli.packageManager,\n templateUrl: cli.options.template,\n portOffset,\n adminEmail,\n };\n\n await create(createOptions);\n}\n\nmain().catch(console.error);\n\n/** Format an offset as a port overview string, e.g. \"10 → :3010 / :4010 / :5442\" */\nfunction formatOffset(o: number, suffix = ''): string {\n return `${o} → :${3000 + o} / :${4000 + o} / :${5432 + o}${suffix}`;\n}\n\n/** Scan sibling forks and prompt the user to pick a port offset */\nasync function promptPortOffset(targetFolder: string, theme: object, context: object): Promise<number> {\n const usedPorts = await detectUsedPorts(targetFolder);\n const suggested = findNextOffset(usedPorts);\n\n if (usedPorts.length > 0) {\n console.info(pc.dim('\\nDetected cella forks in sibling directories:'));\n for (const p of usedPorts) {\n console.info(pc.dim(` ${p.project}: frontend :${p.frontend}, backend :${p.backend} (offset ${p.offset})`));\n }\n console.info();\n }\n\n const presets = [0, 10, 20, 30].filter((o) => o !== suggested);\n const choice = await select(\n {\n message: 'Port offset (avoids conflicts with sibling forks)',\n theme,\n choices: [\n ...(suggested > 0 ? [{ name: formatOffset(suggested, ' (suggested)'), value: suggested }] : []),\n { name: formatOffset(0, ' (default)'), value: 0 },\n ...presets.map((o) => ({ name: formatOffset(o), value: o })),\n { name: 'Custom offset', value: -1 },\n ],\n },\n context,\n );\n\n if (choice !== -1) return choice;\n\n const custom = await input(\n {\n message: 'Enter custom offset (0-490, multiples of 10)',\n default: String(suggested),\n theme,\n validate: (val) => {\n const n = Number(val);\n if (Number.isNaN(n) || n < 0 || n > 490) return 'Must be between 0 and 490';\n if (n % 10 !== 0) return 'Must be a multiple of 10';\n return true;\n },\n },\n context,\n );\n return Number(custom);\n}\n","import pc from 'picocolors';\nimport packageJson from '../package.json' with { type: 'json' };\n\n/** Name of this CLI tool */\nexport const NAME = 'create cella';\n\n/** Thin line divider for console output (60 chars wide) */\nexport const DIVIDER = '─'.repeat(60);\n\n/** URL of the template repository */\nexport const TEMPLATE_URL = 'github:cellajs/cella';\n\n/** URL to the repository */\nexport const CELLA_REMOTE_URL = 'git@github.com:cellajs/cella.git';\n\n/** Export details from package.json */\nexport const DESCRIPTION: string = packageJson.description;\nexport const VERSION: string = packageJson.version;\nexport const AUTHOR: string = packageJson.author;\nexport const WEBSITE: string = packageJson.homepage;\nexport const GITHUB: string = packageJson.repository.url;\n\nexport function getHeaderLine(templateVersion?: string): string {\n const leftText = `⧈ ${NAME} · v${VERSION} · cella v${templateVersion}`;\n const rightText = packageJson.homepage.replace('https://', '');\n const left = `${pc.cyan(`⧈ ${NAME}`)} ${pc.dim(`· v${VERSION} · cella v${templateVersion}`)}`;\n const right = pc.cyan(rightText);\n const padding = Math.max(1, 60 - leftText.length - rightText.length);\n return `${left}${' '.repeat(padding)}${right}`;\n}\n\n// Files or folders to be removed from the template after downloading\nexport const TO_REMOVE: string[] = ['./cli/create', './info/QUICKSTART.md'];\n\n// Specific folder contents to be cleaned out from the template\nexport const TO_CLEAN: string[] = ['./backend/drizzle'];\n\n// Files to copy/paste after downloading\nexport const TO_COPY: Record<string, string> = {\n './frontend/.env.example': './frontend/.env',\n './info/QUICKSTART.md': 'README.md',\n};\n\n/**\n * Placeholder config template that replaces `shared/config/config.default.ts` in new forks.\n * Contains `__project_name__` and `__project_slug__` tokens interpolated at create time.\n */\nexport const PLACEHOLDER_CONFIG = './cli/create-cella/configs/default-config.ts.template';\n\n/**\n * Read a `.env.example` file and apply key=value replacements.\n * Comments and unmatched keys are preserved as-is.\n * Returns null if the file doesn't exist (caller should generate from scratch).\n */\nexport async function generateEnvFromExample(\n examplePath: string,\n replacements: Record<string, string>,\n): Promise<string | null> {\n const { readFile } = await import('node:fs/promises');\n let content: string;\n try {\n content = await readFile(examplePath, 'utf8');\n } catch {\n return null;\n }\n\n return content.replace(/^([A-Z_][A-Z0-9_]*)=(.*)$/gm, (match, key, _value) => {\n if (key in replacements) return `${key}=${replacements[key]}`;\n return match;\n });\n}\n\n/** Replacement map for root `.env` */\nexport function getRootEnvReplacements(slug: string, portOffset: number): Record<string, string> {\n return {\n PROJECT_SLUG: slug,\n DB_PORT: String(5432 + portOffset),\n DB_TEST_PORT: String(5434 + portOffset),\n };\n}\n\n/** Replacement map for `backend/.env` */\nexport function getBackendEnvReplacements(adminEmail: string, portOffset: number): Record<string, string> {\n const db = 5432 + portOffset;\n return {\n DATABASE_URL: `postgres://runtime_role:dev_password@0.0.0.0:${db}/postgres`,\n DATABASE_ADMIN_URL: `postgres://postgres:postgres@0.0.0.0:${db}/postgres`,\n DATABASE_CDC_URL: `postgres://admin_role:dev_password@0.0.0.0:${db}/postgres`,\n ADMIN_EMAIL: adminEmail,\n PORT: String(4000 + portOffset),\n };\n}\n\n/**\n * Generate env config files with project-specific values.\n * All configs go through the same data-driven loop.\n * Values prefixed with '=' are emitted as raw TS expressions (not quoted).\n */\nexport function generateEnvConfigs(slug: string, name: string, portOffset: number): Record<string, string> {\n const fe = 3000 + portOffset;\n const be = 4000 + portOffset;\n\n const header =\n \"import type { DeepPartial } from '../src/builder/types';\\nimport type _default from './config.default';\\n\";\n\n // Per-environment specs: optional imports + object props (= prefix → raw TS expression)\n const envs: Record<string, { imports?: string; props: Record<string, string | boolean> }> = {\n development: {\n props: {\n slug: `${slug}-development`,\n debug: false,\n domain: '',\n frontendUrl: `http://localhost:${fe}`,\n backendUrl: `http://localhost:${be}`,\n backendAuthUrl: `http://localhost:${be}/auth`,\n },\n },\n staging: {\n props: {\n slug: `${slug}-staging`,\n debug: false,\n domain: `${slug}.example.com`,\n frontendUrl: `https://staging.${slug}.example.com`,\n backendUrl: `https://api-staging.${slug}.example.com`,\n backendAuthUrl: `https://api-staging.${slug}.example.com/auth`,\n },\n },\n tunnel: {\n props: {\n frontendUrl: `https://localhost:${fe}`,\n backendUrl: `https://${slug}.ngrok.dev`,\n backendAuthUrl: `https://${slug}.ngrok.dev/auth`,\n },\n },\n test: {\n imports: \"import development from './config.development';\\n\",\n props: {\n debug: false,\n domain: '',\n frontendUrl: '=development.frontendUrl',\n backendUrl: '=development.backendUrl',\n backendAuthUrl: '=development.backendAuthUrl',\n },\n },\n production: { props: { maintenance: false } },\n };\n\n // Serialize value: '=' prefix → raw TS expression, boolean → literal, string → quoted\n const lit = (v: string | boolean) => {\n if (typeof v === 'boolean') return String(v);\n if (v.startsWith('=')) return v.slice(1);\n return `'${v}'`;\n };\n\n const result: Record<string, string> = {};\n\n for (const [mode, { imports = '', props }] of Object.entries(envs)) {\n const nameEntry = mode !== 'production' ? ` name: '${name} ${mode.toUpperCase()}',\\n` : '';\n const body = Object.entries(props)\n .map(([k, v]) => ` ${k}: ${lit(v)},`)\n .join('\\n');\n result[`./shared/config/config.${mode}.ts`] =\n `${imports}${header}\\nexport default {\\n mode: '${mode}',\\n${nameEntry}${body}\\n} satisfies DeepPartial<typeof _default>;\\n`;\n }\n\n return result;\n}\n","{\n \"name\": \"@cellajs/create-cella\",\n \"version\": \"0.2.1\",\n \"private\": false,\n \"license\": \"MIT\",\n \"description\": \"Cella is a TypeScript template to create web apps with sync and offline capabilities.\",\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/cellajs/cella\",\n \"directory\": \"cli/create-cella\"\n },\n \"homepage\": \"https://cellajs.com\",\n \"author\": \"CellaJS <info@cellajs.com>\",\n \"engines\": {\n \"node\": \"24.x\"\n },\n \"type\": \"module\",\n \"main\": \"./dist/index.js\",\n \"files\": [\n \"configs\",\n \"dist\",\n \"index.js\",\n \"README.md\"\n ],\n \"imports\": {\n \"#/*\": \"./src/*\"\n },\n \"bin\": {\n \"create-cella\": \"index.js\"\n },\n \"scripts\": {\n \"start\": \"tsx ./src/create-cella-cli.ts\",\n \"clean\": \"rimraf ./dist\",\n \"build\": \"tsup\",\n \"test-build\": \"pnpm run build && node index.js\",\n \"prepublishOnly\": \"pnpm run test:release\",\n \"check\": \"pnpm ts && pnpm biome check --write .\",\n \"ts\": \"tsgo --pretty\",\n \"lint\": \"biome check .\",\n \"lint:fix\": \"biome check --write .\",\n \"test\": \"vitest run\",\n \"test:release\": \"pnpm run build && vitest run tests/release-artifact.test.ts\",\n \"test:watch\": \"vitest\"\n },\n \"dependencies\": {\n \"@inquirer/prompts\": \"^8.5.2\",\n \"axios\": \"^1.17.0\",\n \"commander\": \"^15.0.0\",\n \"giget\": \"^3.1.2\",\n \"nano-spawn\": \"^2.0.0\",\n \"ora\": \"^9.3.0\",\n \"picocolors\": \"^1.1.1\",\n \"validate-npm-package-name\": \"^8.0.0\"\n },\n \"devDependencies\": {\n \"@types/node\": \"catalog:\",\n \"@typescript/native-preview\": \"catalog:\",\n \"@types/validate-npm-package-name\": \"^4.0.2\",\n \"tsup\": \"catalog:\",\n \"tsx\": \"catalog:\",\n \"vitest\": \"catalog:\"\n }\n}\n","import { existsSync } from 'node:fs';\nimport { cp, mkdir } from 'node:fs/promises';\nimport { join, relative, resolve } from 'node:path';\nimport { downloadTemplate } from 'giget';\nimport { addRemote } from '#/add-remote';\nimport { TEMPLATE_URL } from '#/constants';\nimport { type CreateOptions, showSuccess } from '#/modules/cli';\nimport { cleanTemplate } from '#/utils/clean-template';\nimport { gitAddAll, gitBranch, gitCheckout, gitCommit, gitInit } from '#/utils/git';\nimport { createProgress } from '#/utils/progress';\nimport { generate, install } from '#/utils/run-package-manager-command';\n\n/** Check if a path is a local directory */\nfunction isLocalPath(path: string): boolean {\n return path.startsWith('/') || path.startsWith('./') || path.startsWith('../');\n}\n\nexport async function create({\n projectName,\n targetFolder,\n newBranchName,\n packageManager,\n templateUrl,\n portOffset,\n adminEmail,\n silent = false,\n}: CreateOptions): Promise<void> {\n // Save the original working directory\n const originalCwd = process.cwd();\n\n // Use custom template or default\n const template = templateUrl || TEMPLATE_URL;\n const isLocalTemplate = templateUrl && isLocalPath(templateUrl);\n\n const progress = createProgress('creating project', silent);\n\n try {\n // Create the target folder\n progress.step('creating project folder');\n await mkdir(targetFolder, { recursive: true });\n process.chdir(targetFolder);\n\n // Download or copy the template\n if (isLocalTemplate) {\n progress.step('copying local template');\n const sourcePath = resolve(originalCwd, templateUrl);\n\n // Check if target is inside source (would cause EINVAL)\n if (targetFolder.startsWith(sourcePath)) {\n throw new Error(\n 'Cannot create project inside the template directory.\\n' +\n ` Run from outside: cd ~ && pnpm create @cellajs/cella ${projectName} --template ${templateUrl}`,\n );\n }\n\n await cp(sourcePath, targetFolder, {\n recursive: true,\n filter: (src) => !src.includes('node_modules') && !src.includes('.git'),\n });\n } else {\n progress.step('downloading cella template');\n await downloadTemplate(template, {\n cwd: process.cwd(),\n dir: targetFolder,\n force: true,\n provider: 'github',\n });\n }\n\n // Clean the template, generate configs\n progress.step('cleaning template');\n const displayName = projectName.replace(/[-_]/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase());\n await cleanTemplate({ targetFolder, projectName, displayName, portOffset, adminEmail });\n\n // Install dependencies\n progress.step('installing dependencies');\n await install(packageManager);\n\n // Generate SQL files\n progress.step('generating migrations');\n await generate(packageManager);\n\n // Initialize git repository\n const gitFolderPath = join(targetFolder, '.git');\n if (!existsSync(gitFolderPath)) {\n progress.step('initializing git');\n await gitInit(targetFolder);\n await gitAddAll(targetFolder);\n await gitCommit(targetFolder, 'Initial commit');\n\n if (newBranchName) {\n progress.step(`creating branch '${newBranchName}'`);\n await gitBranch(targetFolder, newBranchName);\n await gitCheckout(targetFolder, newBranchName);\n }\n }\n\n // Add upstream remote\n progress.step('adding upstream remote');\n await addRemote({ targetFolder, silent: true });\n\n // Done\n progress.done(`created ${projectName}`);\n } catch (error) {\n progress.fail(error instanceof Error ? error.message : String(error));\n process.exit(1);\n }\n\n // Check if the working directory needs to be changed\n const needsCd = originalCwd !== targetFolder;\n const relativePath = relative(originalCwd, targetFolder);\n\n // Display final success message\n showSuccess(projectName, targetFolder, relativePath, needsCd, packageManager);\n}\n","import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\n\nconst execFileAsync = promisify(execFile);\n\n/**\n * Executes a Git command in a specific repository path and returns the trimmed stdout.\n * Uses execFile for safety (no shell injection) aligned with sync package pattern.\n *\n * @param args - The Git command arguments to execute (e.g., ['status'], ['checkout', 'branch-name']).\n * @param repoPath - The path to the Git repository.\n * @param options - Optional settings for command execution.\n * @returns The stdout of the Git command, trimmed of leading and trailing whitespace.\n */\nexport async function runGitCommand(\n args: string[],\n repoPath: string,\n options: { skipEditor?: boolean; maxBuffer?: number } = {},\n): Promise<string> {\n const gitArgs = repoPath ? ['-C', repoPath, ...args] : args;\n\n const env = {\n ...process.env,\n ...(options.skipEditor ? { GIT_EDITOR: 'true' } : {}),\n };\n\n // Default to 10MB buffer for typical outputs\n const maxBuffer = options.maxBuffer ?? 10 * 1024 * 1024;\n\n const { stdout } = await execFileAsync('git', gitArgs, { env, maxBuffer });\n\n return stdout.trim();\n}\n\n/**\n * Initializes a new Git repository in the specified directory.\n */\nexport async function gitInit(repoPath: string): Promise<string> {\n return runGitCommand(['init'], repoPath);\n}\n\n/**\n * Stages all files in the repository.\n */\nexport async function gitAddAll(repoPath: string): Promise<string> {\n return runGitCommand(['add', '.'], repoPath);\n}\n\n/**\n * Creates a commit with the specified message.\n */\nexport async function gitCommit(repoPath: string, message: string): Promise<string> {\n return runGitCommand(['commit', '-m', message], repoPath);\n}\n\n/**\n * Creates a new branch with the specified name.\n */\nexport async function gitBranch(repoPath: string, branchName: string): Promise<string> {\n return runGitCommand(['branch', branchName], repoPath);\n}\n\n/**\n * Checks out the specified branch.\n */\nexport async function gitCheckout(repoPath: string, branchName: string): Promise<string> {\n return runGitCommand(['checkout', branchName], repoPath);\n}\n\n/**\n * Gets the URL of a remote.\n */\nexport async function gitRemoteGetUrl(repoPath: string, remoteName: string): Promise<string> {\n return runGitCommand(['remote', 'get-url', remoteName], repoPath);\n}\n\n/**\n * Adds a new remote.\n */\nexport async function gitRemoteAdd(repoPath: string, remoteName: string, remoteUrl: string): Promise<string> {\n return runGitCommand(['remote', 'add', remoteName, remoteUrl], repoPath);\n}\n\n/**\n * Removes a remote.\n */\nexport async function gitRemoteRemove(repoPath: string, remoteName: string): Promise<string> {\n return runGitCommand(['remote', 'remove', remoteName], repoPath);\n}\n","import { CELLA_REMOTE_URL } from '#/constants';\nimport type { AddRemoteOptions } from '#/modules/cli';\nimport { gitRemoteAdd, gitRemoteGetUrl, gitRemoteRemove } from '#/utils/git';\n\n/**\n * Adds or updates the upstream remote for the Cella template.\n * @param options - Configuration options\n * @param options.silent - If true, don't throw on failure (used when called from progress tracker)\n */\nexport async function addRemote({\n targetFolder,\n remoteUrl = CELLA_REMOTE_URL,\n remoteName = 'upstream',\n silent = false,\n}: AddRemoteOptions): Promise<void> {\n try {\n // Check if the remote exists\n let remote: string | null = null;\n\n try {\n remote = await gitRemoteGetUrl(targetFolder, remoteName);\n } catch {\n // If the remote doesn't exist, it throws a fatal error\n remote = null;\n }\n\n // Add or update the remote if it doesn't exist or differs from `remoteUrl`\n if (!remote) {\n await gitRemoteAdd(targetFolder, remoteName, remoteUrl);\n } else if (remote !== remoteUrl) {\n // Remove existing remote and set the new URL\n await gitRemoteRemove(targetFolder, remoteName);\n await gitRemoteAdd(targetFolder, remoteName, remoteUrl);\n }\n } catch (error) {\n if (!silent) {\n throw error;\n }\n }\n}\n","import { basename, resolve } from 'node:path';\nimport { Command, InvalidArgumentError } from 'commander';\nimport { NAME, VERSION } from '#/constants';\nimport { validateProjectName } from '#/utils/validate-project-name';\nimport type { CLIConfig, CLIOptions } from './types';\n\n// Initialize CLI variables\nlet directory: string | null = null;\nconst newBranchName: string | null = null;\nconst packageManager = 'pnpm';\n\n/**\n * Defines the root CLI command using Commander.\n * This command accepts CLI options and validates user input.\n */\nexport const command = new Command(NAME)\n .version(VERSION, '-v, --version', `output the current version of ${NAME}`)\n .argument('[directory]', 'the directory name for the new project')\n .usage('[directory] [options]')\n .helpOption('-h, --help', 'display this help message')\n .option('--template <path>', 'use a custom template (local path or github:user/repo)')\n .action((name: string) => {\n const trimmedName = typeof name === 'string' ? name.trim() : name;\n\n if (trimmedName) {\n const validation = validateProjectName(basename(resolve(trimmedName)));\n\n if (!validation.valid) {\n throw new InvalidArgumentError(`Invalid project name: ${validation.problems?.[0] ?? 'unknown error'}`);\n }\n\n directory = trimmedName;\n }\n })\n .parse();\n\n// Gather the CLI options and arguments\nconst options: CLIOptions = command.opts<CLIOptions>();\n\n/**\n * Runs the CLI and returns the parsed configuration.\n * This function parses command line arguments and returns the CLI config.\n */\nexport function runCli(): CLIConfig {\n return {\n options,\n args: command.args,\n directory,\n newBranchName,\n packageManager,\n };\n}\n\n// Export CLI configuration for direct import\nexport const cli: CLIConfig = runCli();\n","import validate from 'validate-npm-package-name';\n\ninterface ValidationResult {\n valid: boolean;\n problems?: string[];\n}\n\nexport function validateProjectName(name: string): ValidationResult {\n const nameValidation = validate(name);\n\n if (nameValidation.validForNewPackages) {\n return { valid: true };\n }\n\n return {\n valid: false,\n problems: [...(nameValidation.errors || []), ...(nameValidation.warnings || [])],\n };\n}\n","import pc from 'picocolors';\nimport { DESCRIPTION, DIVIDER, getHeaderLine } from '#/constants';\n\n/** ASCII art logo for the CLI welcome screen. */\nfunction showAscii(): void {\n console.info(pc.cyan(' _ _ '));\n console.info(pc.cyan('▒▓█████▓▒ ___ ___| | | __ _ '));\n console.info(pc.cyan('▒▓█ █▓▒ / __/ _ \\\\ | |/ _` | '));\n console.info(pc.cyan('▒▓█ █▓▒ | (_| __/ | | (_| | '));\n console.info(pc.cyan('▒▓█████▓▒ \\\\___\\\\___|_|_|\\\\__,_| '));\n}\n\n/**\n * Displays the compact CLI welcome header.\n * @param templateVersion - The version of the cella template being used\n */\nexport function showWelcome(templateVersion: string): void {\n console.info();\n showAscii();\n console.info();\n console.info(pc.dim(DESCRIPTION));\n console.info();\n console.info(getHeaderLine(templateVersion));\n console.info(DIVIDER);\n}\n\n/**\n * Displays the final success message after project creation.\n */\nexport function showSuccess(\n projectName: string,\n _targetFolder: string,\n relativePath: string,\n needsCd: boolean,\n packageManager: string,\n): void {\n console.info(DIVIDER);\n console.info();\n\n // Navigation instruction\n if (needsCd) {\n console.info(`${pc.green('→')} cd ${pc.cyan(relativePath)}`);\n console.info();\n }\n\n // Quick start options\n console.info(`${pc.green('→')} ${pc.cyan(`${packageManager} quick`)} ${pc.gray('(pglite, no docker)')}`);\n console.info();\n console.info(pc.gray('or, for full setup:'));\n console.info();\n console.info(\n `${pc.green('→')} ${pc.cyan(`${packageManager} docker`)} ${pc.dim('&&')} ${pc.cyan(`${packageManager} seed`)} ${pc.dim('&&')} ${pc.cyan(`${packageManager} dev`)}`,\n );\n console.info();\n\n // Credentials\n console.info(`sign in: ${pc.gray('admin-test@cellajs.com / 12345678')}`);\n console.info();\n console.info(`enjoy building ${pc.green(projectName)} with cella!`);\n console.info();\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport pc from 'picocolors';\nimport {\n generateEnvConfigs,\n generateEnvFromExample,\n getBackendEnvReplacements,\n getRootEnvReplacements,\n PLACEHOLDER_CONFIG,\n TO_CLEAN,\n TO_COPY,\n TO_REMOVE,\n} from '#/constants';\n\nconst warningMark = pc.yellow('⚠');\n\n/**\n * Cleans the specified template by removing designated folders and files.\n * @param params - Parameters containing the target folder, project name, and optional extra edits.\n */\nexport async function cleanTemplate({\n targetFolder,\n projectName,\n displayName,\n portOffset = 0,\n adminEmail = `admin@${projectName}.example.com`,\n}: {\n targetFolder: string;\n projectName: string;\n displayName: string;\n portOffset?: number;\n adminEmail?: string;\n}): Promise<void> {\n // Change the current working directory to targetFolder if not already set\n if (process.cwd() !== targetFolder) {\n process.chdir(targetFolder);\n }\n\n return new Promise<void>((resolve, reject) => {\n (async () => {\n try {\n // Copy specified files\n for (const [src, dest] of Object.entries(TO_COPY)) {\n const srcAbsolutePath = path.resolve(targetFolder, src);\n const destAbsolutePath = path.resolve(targetFolder, dest);\n await copyFile(srcAbsolutePath, destAbsolutePath);\n }\n\n // Replace config.default.ts with interpolated placeholder config\n await applyPlaceholderConfig(targetFolder, projectName);\n\n // Generate root .env from .env.example (single source of truth for ports)\n const rootReplacements = getRootEnvReplacements(projectName, portOffset);\n const rootEnv = await generateEnvFromExample(path.resolve(targetFolder, '.env.example'), rootReplacements);\n await fs.writeFile(\n path.resolve(targetFolder, '.env'),\n rootEnv ??\n Object.entries(rootReplacements)\n .map(([k, v]) => `${k}=${v}`)\n .join('\\n'),\n 'utf8',\n );\n\n // Generate backend .env from backend/.env.example\n const backendReplacements = getBackendEnvReplacements(adminEmail, portOffset);\n const backendEnv = await generateEnvFromExample(\n path.resolve(targetFolder, 'backend/.env.example'),\n backendReplacements,\n );\n if (backendEnv) {\n await fs.writeFile(path.resolve(targetFolder, 'backend/.env'), backendEnv, 'utf8');\n }\n\n // Generate minimal env config files with project values and ports baked in\n const envConfigs = generateEnvConfigs(projectName, displayName, portOffset);\n await Promise.all(\n Object.entries(envConfigs).map(([filePath, content]) =>\n fs.writeFile(path.resolve(targetFolder, filePath), content, 'utf8'),\n ),\n );\n\n // Clean specified folder contents\n await Promise.all(\n TO_CLEAN.map((folderPath) => {\n const absolutePath = path.resolve(targetFolder, folderPath);\n return removeFolderContents(absolutePath);\n }),\n );\n\n // Remove specified files and folders\n await Promise.all(\n TO_REMOVE.map((filePath) => {\n const absolutePath = path.resolve(targetFolder, filePath);\n return removeFileOrFolder(absolutePath);\n }),\n );\n\n resolve();\n } catch (err) {\n reject(`Error during the cleaning process: ${err}`);\n }\n })();\n });\n}\n\n/**\n * Removes all contents within a specified folder.\n * @param folderPath - The path of the folder to clean.\n */\nexport async function removeFolderContents(folderPath: string): Promise<void> {\n // List all files in the folder\n const files = await fs.readdir(folderPath);\n\n await Promise.all(\n files.map(async (file) => {\n const filePath = path.join(folderPath, file);\n\n // Get the file or folder statistics\n const stat = await fs.lstat(filePath);\n\n // If it's a directory, remove it and all its contents\n if (stat.isDirectory()) {\n await fs.rm(filePath, { recursive: true, force: true });\n } else {\n // If it's a file, remove it\n await fs.rm(filePath);\n }\n }),\n );\n}\n\n/**\n * Removes a specified file or folder.\n * @param pathToRemove - The path to the file or folder to remove.\n */\nexport async function removeFileOrFolder(pathToRemove: string): Promise<void> {\n await fs.rm(pathToRemove, { recursive: true, force: true });\n}\n\n/**\n * Helper function to copy files if the source exists.\n * @param src - The source file path.\n * @param dest - The destination file path.\n */\nexport async function copyFile(src: string, dest: string): Promise<void> {\n try {\n // Check if the source file exists\n await fs.access(src);\n\n // Ensure the destination directory exists\n await fs.mkdir(path.dirname(dest), { recursive: true });\n\n // Copy the file\n await fs.copyFile(src, dest);\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n console.info(`\\n${warningMark} Source file \"${src}\" does not exist > Skip copy`);\n } else {\n throw err;\n }\n }\n}\n\n/**\n * Read the placeholder config template, interpolate project tokens, and\n * write it as `shared/config/config.default.ts` — replacing the original.\n */\nasync function applyPlaceholderConfig(targetFolder: string, projectName: string): Promise<void> {\n const displayName = projectName.replace(/[-_]/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase());\n\n const src = path.resolve(targetFolder, PLACEHOLDER_CONFIG);\n const dest = path.resolve(targetFolder, './shared/config/config.default.ts');\n\n try {\n let content = await fs.readFile(src, 'utf8');\n content = content.replaceAll('__project_name__', displayName);\n content = content.replaceAll('__project_slug__', projectName);\n await fs.writeFile(dest, content, 'utf8');\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n console.info(`\\n${warningMark} Placeholder config \"${src}\" not found > Skip`);\n } else {\n throw err;\n }\n }\n}\n","/**\n * Progress Tracker Utility\n *\n * Provides compact progress tracking for CLI operations.\n * Uses a single spinner that updates in place.\n */\n\nimport ora, { type Ora } from 'ora';\nimport pc from 'picocolors';\n\n/** Global reference to the currently active spinner */\nlet activeSpinner: Ora | null = null;\n\n/** Pause the active spinner (for interactive prompts) */\nexport function pauseSpinner(): void {\n if (activeSpinner) {\n activeSpinner.stop();\n }\n}\n\n/** Resume the active spinner after a prompt */\nexport function resumeSpinner(): void {\n if (activeSpinner) {\n activeSpinner.start();\n }\n}\n\nexport interface ProgressTracker {\n /** Update the current step (shown as spinner text) */\n step: (message: string) => void;\n /** Mark the tracker as complete with final message */\n done: (message: string) => void;\n /** Mark the tracker as failed with error message */\n fail: (message: string) => void;\n /** Wrap an async operation to auto-fail on error */\n wrap: <T>(fn: () => Promise<T>) => Promise<T>;\n}\n\n/**\n * Creates a progress tracker for a multi-step operation.\n * Shows a single spinner that updates in place.\n *\n * @param title - The initial title for the spinner\n * @returns A ProgressTracker instance\n *\n * @example\n * const progress = createProgress('creating project');\n * progress.step('downloading template');\n * progress.step('installing dependencies');\n * progress.done('project created');\n */\nexport function createProgress(title: string, silent = false): ProgressTracker {\n const completedSteps: string[] = [];\n\n const spinner = ora({\n text: title,\n isSilent: silent,\n });\n\n activeSpinner = spinner;\n spinner.start();\n\n // Helper to log (respects silent mode)\n const log = (msg: string) => {\n if (!silent) console.info(msg);\n };\n\n return {\n step: (message: string) => {\n // Complete previous step with green check if there was one\n if (completedSteps.length > 0) {\n spinner.stop();\n log(`${pc.green('✓')} ${completedSteps[completedSteps.length - 1]}`);\n }\n completedSteps.push(message);\n spinner.text = message;\n spinner.start();\n },\n\n done: (message: string) => {\n // Complete the last step with green check\n if (completedSteps.length > 0) {\n spinner.stop();\n log(`${pc.green('✓')} ${completedSteps[completedSteps.length - 1]}`);\n } else {\n spinner.stop();\n }\n activeSpinner = null;\n if (message) {\n log('');\n log(`${pc.green('✓')} ${message}`);\n }\n },\n\n fail: (message: string) => {\n spinner.stop();\n activeSpinner = null;\n log(`${pc.red('✗')} ${pc.red(message)}`);\n },\n\n wrap: async <T>(fn: () => Promise<T>): Promise<T> => {\n try {\n return await fn();\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n // Stop spinner and show tree-style breakdown on failure\n spinner.stop();\n activeSpinner = null;\n log(pc.cyan(`\\n${title}`));\n for (const step of completedSteps) {\n log(pc.gray(` ├─ ${step}`));\n }\n log(pc.red(` └─ ✗ ${errorMessage}`));\n throw error;\n }\n },\n };\n}\n","import spawn from 'nano-spawn';\n\n/**\n * Executes a command using the specified package manager (e.g., pnpm).\n *\n * @param packageManager - The package manager to use (e.g., 'pnpm').\n * @param args - The arguments to pass to the package manager command.\n * @param env - Additional environment variables to set during command execution.\n * @returns A promise that resolves if the command executes successfully; otherwise, it rejects with an error message.\n */\nexport async function runPackageManagerCommand(\n packageManager: string,\n args: string[],\n env: Record<string, string> = {},\n): Promise<void> {\n try {\n await spawn(packageManager, args, {\n env: { ...process.env, ...env },\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n } catch (error) {\n const err = error as { stdout?: string; stderr?: string };\n throw new Error(`\"${packageManager} ${args.join(' ')}\" failed ${err.stdout || ''} ${err.stderr || ''}`);\n }\n}\n\n/**\n * Installs dependencies using the specified package manager.\n *\n * @param packageManager - The package manager to use for installation (e.g., 'pnpm').\n * @returns A promise that resolves if the installation completes successfully; otherwise, it rejects with an error.\n */\nexport async function install(packageManager: string): Promise<void> {\n return runPackageManagerCommand(packageManager, ['install'], {\n NODE_ENV: 'development',\n });\n}\n\n/**\n * Generates SQL files using the specified package manager.\n *\n * @param packageManager - The package manager to use for generation (e.g., 'pnpm').\n * @returns A promise that resolves if the generation completes successfully; otherwise, it rejects with an error.\n */\nexport async function generate(packageManager: string): Promise<void> {\n return runPackageManagerCommand(packageManager, ['generate'], {\n NODE_ENV: 'development',\n });\n}\n","import { readdir, readFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\n\ninterface UsedPorts {\n project: string;\n frontend: number;\n backend: number;\n offset: number;\n}\n\n/**\n * Scan sibling directories for existing cella forks and extract their dev ports.\n * Looks for `shared/config/config.development.ts` to identify cella-based projects.\n */\nexport async function detectUsedPorts(targetFolder: string): Promise<UsedPorts[]> {\n const parentDir = dirname(targetFolder);\n const used: UsedPorts[] = [];\n\n let siblings: string[];\n try {\n siblings = await readdir(parentDir);\n } catch {\n return used;\n }\n\n for (const name of siblings) {\n const configPath = join(parentDir, name, 'shared/config/config.development.ts');\n try {\n const content = await readFile(configPath, 'utf8');\n const feMatch = content.match(/frontendUrl:\\s*'http:\\/\\/localhost:(\\d+)'/);\n const beMatch = content.match(/backendUrl:\\s*'http:\\/\\/localhost:(\\d+)'/);\n if (feMatch && beMatch) {\n const frontend = Number(feMatch[1]);\n const backend = Number(beMatch[1]);\n used.push({\n project: name,\n frontend,\n backend,\n offset: frontend - 3000,\n });\n }\n } catch {\n // Not a cella fork, skip\n }\n }\n\n return used;\n}\n\n/** Find the next available offset (in steps of 10) that doesn't collide with existing forks. */\nexport function findNextOffset(usedPorts: UsedPorts[]): number {\n const usedOffsets = new Set(usedPorts.map((p) => p.offset));\n for (let offset = 0; offset <= 490; offset += 10) {\n if (!usedOffsets.has(offset)) return offset;\n }\n return 0;\n}\n","import axios from 'axios';\n\n/**\n * Retrieves the version from the package.json file of a GitHub repository.\n * If the package.json file is not found or an error occurs, it returns 'unknown'.\n *\n * @param repositoryUrl {string} - The GitHub repository URL in the format 'github:user/repo'.\n * @param branch {string} - The branch name (defaults to 'main').\n * @returns {Promise<string>} - The version from the package.json file.\n */\nexport async function extractPackageJsonVersionFromUri(repositoryUrl: string, branch = 'main'): Promise<string> {\n // Extract owner and repo from the URL\n const [owner, repo] = repositoryUrl.replace('github:', '').split('/');\n\n // Construct the URL for the package.json file in the provided branch\n const packageJsonUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/package.json`;\n\n try {\n // Fetch the package.json file\n const response = await axios.get(packageJsonUrl);\n const packageJson = response.data;\n\n // Return the version from the package.json, or 'unknown' if not found\n return packageJson.version || 'unknown';\n } catch (error) {\n // If there's an error (file not found, etc.), return 'unknown'\n return 'unknown';\n }\n}\n","import { readdir } from 'node:fs/promises';\n\n/**\n * Checks if a directory is empty or only contains a .git directory.\n *\n * @param path - The path of the directory to check.\n * @returns Resolves to true if the directory is empty or contains only a .git folder, false otherwise.\n * @throws Throws an error if the path is not a directory or if there's an issue reading the directory.\n */\nexport async function isEmptyDirectory(path: string): Promise<boolean> {\n const files = await readdir(path);\n\n // Check if directory is empty or contains only the .git directory\n return files.length === 0 || (files.length === 1 && files[0] === '.git');\n}\n"],"mappings":";;;AAEA,SAAS,cAAAA,mBAAkB;AAC3B,SAAS,YAAAC,WAAU,WAAAC,gBAAe;AAClC,SAAS,OAAO,cAAc;AAC9B,OAAOC,SAAQ;;;ACLf,OAAO,QAAQ;;;ACAf;AAAA,EACE,MAAQ;AAAA,EACR,SAAW;AAAA,EACX,SAAW;AAAA,EACX,SAAW;AAAA,EACX,aAAe;AAAA,EACf,eAAiB;AAAA,IACf,QAAU;AAAA,EACZ;AAAA,EACA,YAAc;AAAA,IACZ,MAAQ;AAAA,IACR,KAAO;AAAA,IACP,WAAa;AAAA,EACf;AAAA,EACA,UAAY;AAAA,EACZ,QAAU;AAAA,EACV,SAAW;AAAA,IACT,MAAQ;AAAA,EACV;AAAA,EACA,MAAQ;AAAA,EACR,MAAQ;AAAA,EACR,OAAS;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,SAAW;AAAA,IACT,OAAO;AAAA,EACT;AAAA,EACA,KAAO;AAAA,IACL,gBAAgB;AAAA,EAClB;AAAA,EACA,SAAW;AAAA,IACT,OAAS;AAAA,IACT,OAAS;AAAA,IACT,OAAS;AAAA,IACT,cAAc;AAAA,IACd,gBAAkB;AAAA,IAClB,OAAS;AAAA,IACT,IAAM;AAAA,IACN,MAAQ;AAAA,IACR,YAAY;AAAA,IACZ,MAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,cAAc;AAAA,EAChB;AAAA,EACA,cAAgB;AAAA,IACd,qBAAqB;AAAA,IACrB,OAAS;AAAA,IACT,WAAa;AAAA,IACb,OAAS;AAAA,IACT,cAAc;AAAA,IACd,KAAO;AAAA,IACP,YAAc;AAAA,IACd,6BAA6B;AAAA,EAC/B;AAAA,EACA,iBAAmB;AAAA,IACjB,eAAe;AAAA,IACf,8BAA8B;AAAA,IAC9B,oCAAoC;AAAA,IACpC,MAAQ;AAAA,IACR,KAAO;AAAA,IACP,QAAU;AAAA,EACZ;AACF;;;AD7DO,IAAM,OAAO;AAGb,IAAM,UAAU,SAAI,OAAO,EAAE;AAG7B,IAAM,eAAe;AAGrB,IAAM,mBAAmB;AAGzB,IAAM,cAAsB,gBAAY;AACxC,IAAM,UAAkB,gBAAY;AACpC,IAAM,SAAiB,gBAAY;AACnC,IAAM,UAAkB,gBAAY;AACpC,IAAM,SAAiB,gBAAY,WAAW;AAE9C,SAAS,cAAc,iBAAkC;AAC9D,QAAM,WAAW,UAAK,IAAI,UAAO,OAAO,gBAAa,eAAe;AACpE,QAAM,YAAY,gBAAY,SAAS,QAAQ,YAAY,EAAE;AAC7D,QAAM,OAAO,GAAG,GAAG,KAAK,UAAK,IAAI,EAAE,CAAC,IAAI,GAAG,IAAI,SAAM,OAAO,gBAAa,eAAe,EAAE,CAAC;AAC3F,QAAM,QAAQ,GAAG,KAAK,SAAS;AAC/B,QAAM,UAAU,KAAK,IAAI,GAAG,KAAK,SAAS,SAAS,UAAU,MAAM;AACnE,SAAO,GAAG,IAAI,GAAG,IAAI,OAAO,OAAO,CAAC,GAAG,KAAK;AAC9C;AAGO,IAAM,YAAsB,CAAC,gBAAgB,sBAAsB;AAGnE,IAAM,WAAqB,CAAC,mBAAmB;AAG/C,IAAM,UAAkC;AAAA,EAC7C,2BAA2B;AAAA,EAC3B,wBAAwB;AAC1B;AAMO,IAAM,qBAAqB;AAOlC,eAAsB,uBACpB,aACA,cACwB;AACxB,QAAM,EAAE,UAAAC,UAAS,IAAI,MAAM,OAAO,aAAkB;AACpD,MAAI;AACJ,MAAI;AACF,cAAU,MAAMA,UAAS,aAAa,MAAM;AAAA,EAC9C,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,SAAO,QAAQ,QAAQ,+BAA+B,CAAC,OAAO,KAAK,WAAW;AAC5E,QAAI,OAAO,aAAc,QAAO,GAAG,GAAG,IAAI,aAAa,GAAG,CAAC;AAC3D,WAAO;AAAA,EACT,CAAC;AACH;AAGO,SAAS,uBAAuB,MAAc,YAA4C;AAC/F,SAAO;AAAA,IACL,cAAc;AAAA,IACd,SAAS,OAAO,OAAO,UAAU;AAAA,IACjC,cAAc,OAAO,OAAO,UAAU;AAAA,EACxC;AACF;AAGO,SAAS,0BAA0B,YAAoB,YAA4C;AACxG,QAAM,KAAK,OAAO;AAClB,SAAO;AAAA,IACL,cAAc,gDAAgD,EAAE;AAAA,IAChE,oBAAoB,wCAAwC,EAAE;AAAA,IAC9D,kBAAkB,8CAA8C,EAAE;AAAA,IAClE,aAAa;AAAA,IACb,MAAM,OAAO,MAAO,UAAU;AAAA,EAChC;AACF;AAOO,SAAS,mBAAmB,MAAc,MAAc,YAA4C;AACzG,QAAM,KAAK,MAAO;AAClB,QAAM,KAAK,MAAO;AAElB,QAAM,SACJ;AAGF,QAAM,OAAsF;AAAA,IAC1F,aAAa;AAAA,MACX,OAAO;AAAA,QACL,MAAM,GAAG,IAAI;AAAA,QACb,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,aAAa,oBAAoB,EAAE;AAAA,QACnC,YAAY,oBAAoB,EAAE;AAAA,QAClC,gBAAgB,oBAAoB,EAAE;AAAA,MACxC;AAAA,IACF;AAAA,IACA,SAAS;AAAA,MACP,OAAO;AAAA,QACL,MAAM,GAAG,IAAI;AAAA,QACb,OAAO;AAAA,QACP,QAAQ,GAAG,IAAI;AAAA,QACf,aAAa,mBAAmB,IAAI;AAAA,QACpC,YAAY,uBAAuB,IAAI;AAAA,QACvC,gBAAgB,uBAAuB,IAAI;AAAA,MAC7C;AAAA,IACF;AAAA,IACA,QAAQ;AAAA,MACN,OAAO;AAAA,QACL,aAAa,qBAAqB,EAAE;AAAA,QACpC,YAAY,WAAW,IAAI;AAAA,QAC3B,gBAAgB,WAAW,IAAI;AAAA,MACjC;AAAA,IACF;AAAA,IACA,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,OAAO;AAAA,QACL,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,aAAa;AAAA,QACb,YAAY;AAAA,QACZ,gBAAgB;AAAA,MAClB;AAAA,IACF;AAAA,IACA,YAAY,EAAE,OAAO,EAAE,aAAa,MAAM,EAAE;AAAA,EAC9C;AAGA,QAAM,MAAM,CAAC,MAAwB;AACnC,QAAI,OAAO,MAAM,UAAW,QAAO,OAAO,CAAC;AAC3C,QAAI,EAAE,WAAW,GAAG,EAAG,QAAO,EAAE,MAAM,CAAC;AACvC,WAAO,IAAI,CAAC;AAAA,EACd;AAEA,QAAM,SAAiC,CAAC;AAExC,aAAW,CAAC,MAAM,EAAE,UAAU,IAAI,MAAM,CAAC,KAAK,OAAO,QAAQ,IAAI,GAAG;AAClE,UAAM,YAAY,SAAS,eAAe,YAAY,IAAI,IAAI,KAAK,YAAY,CAAC;AAAA,IAAS;AACzF,UAAM,OAAO,OAAO,QAAQ,KAAK,EAC9B,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,EACpC,KAAK,IAAI;AACZ,WAAO,0BAA0B,IAAI,KAAK,IACxC,GAAG,OAAO,GAAG,MAAM;AAAA;AAAA,WAAgC,IAAI;AAAA,EAAO,SAAS,GAAG,IAAI;AAAA;AAAA;AAAA,EAClF;AAEA,SAAO;AACT;;;AEtKA,SAAS,kBAAkB;AAC3B,SAAS,IAAI,aAAa;AAC1B,SAAS,MAAM,UAAU,WAAAC,gBAAe;AACxC,SAAS,wBAAwB;;;ACHjC,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AAE1B,IAAM,gBAAgB,UAAU,QAAQ;AAWxC,eAAsB,cACpB,MACA,UACAC,WAAwD,CAAC,GACxC;AACjB,QAAM,UAAU,WAAW,CAAC,MAAM,UAAU,GAAG,IAAI,IAAI;AAEvD,QAAM,MAAM;AAAA,IACV,GAAG,QAAQ;AAAA,IACX,GAAIA,SAAQ,aAAa,EAAE,YAAY,OAAO,IAAI,CAAC;AAAA,EACrD;AAGA,QAAM,YAAYA,SAAQ,aAAa,KAAK,OAAO;AAEnD,QAAM,EAAE,OAAO,IAAI,MAAM,cAAc,OAAO,SAAS,EAAE,KAAK,UAAU,CAAC;AAEzE,SAAO,OAAO,KAAK;AACrB;AAKA,eAAsB,QAAQ,UAAmC;AAC/D,SAAO,cAAc,CAAC,MAAM,GAAG,QAAQ;AACzC;AAKA,eAAsB,UAAU,UAAmC;AACjE,SAAO,cAAc,CAAC,OAAO,GAAG,GAAG,QAAQ;AAC7C;AAKA,eAAsB,UAAU,UAAkB,SAAkC;AAClF,SAAO,cAAc,CAAC,UAAU,MAAM,OAAO,GAAG,QAAQ;AAC1D;AAKA,eAAsB,UAAU,UAAkB,YAAqC;AACrF,SAAO,cAAc,CAAC,UAAU,UAAU,GAAG,QAAQ;AACvD;AAKA,eAAsB,YAAY,UAAkB,YAAqC;AACvF,SAAO,cAAc,CAAC,YAAY,UAAU,GAAG,QAAQ;AACzD;AAKA,eAAsB,gBAAgB,UAAkB,YAAqC;AAC3F,SAAO,cAAc,CAAC,UAAU,WAAW,UAAU,GAAG,QAAQ;AAClE;AAKA,eAAsB,aAAa,UAAkB,YAAoB,WAAoC;AAC3G,SAAO,cAAc,CAAC,UAAU,OAAO,YAAY,SAAS,GAAG,QAAQ;AACzE;AAKA,eAAsB,gBAAgB,UAAkB,YAAqC;AAC3F,SAAO,cAAc,CAAC,UAAU,UAAU,UAAU,GAAG,QAAQ;AACjE;;;AC/EA,eAAsB,UAAU;AAAA,EAC9B;AAAA,EACA,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,SAAS;AACX,GAAoC;AAClC,MAAI;AAEF,QAAI,SAAwB;AAE5B,QAAI;AACF,eAAS,MAAM,gBAAgB,cAAc,UAAU;AAAA,IACzD,QAAQ;AAEN,eAAS;AAAA,IACX;AAGA,QAAI,CAAC,QAAQ;AACX,YAAM,aAAa,cAAc,YAAY,SAAS;AAAA,IACxD,WAAW,WAAW,WAAW;AAE/B,YAAM,gBAAgB,cAAc,UAAU;AAC9C,YAAM,aAAa,cAAc,YAAY,SAAS;AAAA,IACxD;AAAA,EACF,SAAS,OAAO;AACd,QAAI,CAAC,QAAQ;AACX,YAAM;AAAA,IACR;AAAA,EACF;AACF;;;ACvCA,SAAS,UAAU,eAAe;AAClC,SAAS,SAAS,4BAA4B;;;ACD9C,OAAO,cAAc;AAOd,SAAS,oBAAoB,MAAgC;AAClE,QAAM,iBAAiB,SAAS,IAAI;AAEpC,MAAI,eAAe,qBAAqB;AACtC,WAAO,EAAE,OAAO,KAAK;AAAA,EACvB;AAEA,SAAO;AAAA,IACL,OAAO;AAAA,IACP,UAAU,CAAC,GAAI,eAAe,UAAU,CAAC,GAAI,GAAI,eAAe,YAAY,CAAC,CAAE;AAAA,EACjF;AACF;;;ADXA,IAAI,YAA2B;AAC/B,IAAM,gBAA+B;AACrC,IAAM,iBAAiB;AAMhB,IAAM,UAAU,IAAI,QAAQ,IAAI,EACpC,QAAQ,SAAS,iBAAiB,iCAAiC,IAAI,EAAE,EACzE,SAAS,eAAe,wCAAwC,EAChE,MAAM,uBAAuB,EAC7B,WAAW,cAAc,2BAA2B,EACpD,OAAO,qBAAqB,wDAAwD,EACpF,OAAO,CAAC,SAAiB;AACxB,QAAM,cAAc,OAAO,SAAS,WAAW,KAAK,KAAK,IAAI;AAE7D,MAAI,aAAa;AACf,UAAM,aAAa,oBAAoB,SAAS,QAAQ,WAAW,CAAC,CAAC;AAErE,QAAI,CAAC,WAAW,OAAO;AACrB,YAAM,IAAI,qBAAqB,yBAAyB,WAAW,WAAW,CAAC,KAAK,eAAe,EAAE;AAAA,IACvG;AAEA,gBAAY;AAAA,EACd;AACF,CAAC,EACA,MAAM;AAGT,IAAM,UAAsB,QAAQ,KAAiB;AAM9C,SAAS,SAAoB;AAClC,SAAO;AAAA,IACL;AAAA,IACA,MAAM,QAAQ;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAGO,IAAM,MAAiB,OAAO;;;AEtDrC,OAAOC,SAAQ;AAIf,SAAS,YAAkB;AACzB,UAAQ,KAAKC,IAAG,KAAK,uCAAuC,CAAC;AAC7D,UAAQ,KAAKA,IAAG,KAAK,oFAAuC,CAAC;AAC7D,UAAQ,KAAKA,IAAG,KAAK,qEAAuC,CAAC;AAC7D,UAAQ,KAAKA,IAAG,KAAK,qEAAuC,CAAC;AAC7D,UAAQ,KAAKA,IAAG,KAAK,oFAAuC,CAAC;AAC/D;AAMO,SAAS,YAAY,iBAA+B;AACzD,UAAQ,KAAK;AACb,YAAU;AACV,UAAQ,KAAK;AACb,UAAQ,KAAKA,IAAG,IAAI,WAAW,CAAC;AAChC,UAAQ,KAAK;AACb,UAAQ,KAAK,cAAc,eAAe,CAAC;AAC3C,UAAQ,KAAK,OAAO;AACtB;AAKO,SAAS,YACd,aACA,eACA,cACA,SACAC,iBACM;AACN,UAAQ,KAAK,OAAO;AACpB,UAAQ,KAAK;AAGb,MAAI,SAAS;AACX,YAAQ,KAAK,GAAGD,IAAG,MAAM,QAAG,CAAC,OAAOA,IAAG,KAAK,YAAY,CAAC,EAAE;AAC3D,YAAQ,KAAK;AAAA,EACf;AAGA,UAAQ,KAAK,GAAGA,IAAG,MAAM,QAAG,CAAC,IAAIA,IAAG,KAAK,GAAGC,eAAc,QAAQ,CAAC,UAAUD,IAAG,KAAK,qBAAqB,CAAC,EAAE;AAC7G,UAAQ,KAAK;AACb,UAAQ,KAAKA,IAAG,KAAK,qBAAqB,CAAC;AAC3C,UAAQ,KAAK;AACb,UAAQ;AAAA,IACN,GAAGA,IAAG,MAAM,QAAG,CAAC,IAAIA,IAAG,KAAK,GAAGC,eAAc,SAAS,CAAC,IAAID,IAAG,IAAI,IAAI,CAAC,IAAIA,IAAG,KAAK,GAAGC,eAAc,OAAO,CAAC,IAAID,IAAG,IAAI,IAAI,CAAC,IAAIA,IAAG,KAAK,GAAGC,eAAc,MAAM,CAAC;AAAA,EAClK;AACA,UAAQ,KAAK;AAGb,UAAQ,KAAK,YAAYD,IAAG,KAAK,mCAAmC,CAAC,EAAE;AACvE,UAAQ,KAAK;AACb,UAAQ,KAAK,kBAAkBA,IAAG,MAAM,WAAW,CAAC,cAAc;AAClE,UAAQ,KAAK;AACf;;;AC5DA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAOE,SAAQ;AAYf,IAAM,cAAcC,IAAG,OAAO,QAAG;AAMjC,eAAsB,cAAc;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb,aAAa,SAAS,WAAW;AACnC,GAMkB;AAEhB,MAAI,QAAQ,IAAI,MAAM,cAAc;AAClC,YAAQ,MAAM,YAAY;AAAA,EAC5B;AAEA,SAAO,IAAI,QAAc,CAACC,UAAS,WAAW;AAC5C,KAAC,YAAY;AACX,UAAI;AAEF,mBAAW,CAAC,KAAK,IAAI,KAAK,OAAO,QAAQ,OAAO,GAAG;AACjD,gBAAM,kBAAkB,KAAK,QAAQ,cAAc,GAAG;AACtD,gBAAM,mBAAmB,KAAK,QAAQ,cAAc,IAAI;AACxD,gBAAM,SAAS,iBAAiB,gBAAgB;AAAA,QAClD;AAGA,cAAM,uBAAuB,cAAc,WAAW;AAGtD,cAAM,mBAAmB,uBAAuB,aAAa,UAAU;AACvE,cAAM,UAAU,MAAM,uBAAuB,KAAK,QAAQ,cAAc,cAAc,GAAG,gBAAgB;AACzG,cAAM,GAAG;AAAA,UACP,KAAK,QAAQ,cAAc,MAAM;AAAA,UACjC,WACE,OAAO,QAAQ,gBAAgB,EAC5B,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,EAAE,EAC3B,KAAK,IAAI;AAAA,UACd;AAAA,QACF;AAGA,cAAM,sBAAsB,0BAA0B,YAAY,UAAU;AAC5E,cAAM,aAAa,MAAM;AAAA,UACvB,KAAK,QAAQ,cAAc,sBAAsB;AAAA,UACjD;AAAA,QACF;AACA,YAAI,YAAY;AACd,gBAAM,GAAG,UAAU,KAAK,QAAQ,cAAc,cAAc,GAAG,YAAY,MAAM;AAAA,QACnF;AAGA,cAAM,aAAa,mBAAmB,aAAa,aAAa,UAAU;AAC1E,cAAM,QAAQ;AAAA,UACZ,OAAO,QAAQ,UAAU,EAAE;AAAA,YAAI,CAAC,CAAC,UAAU,OAAO,MAChD,GAAG,UAAU,KAAK,QAAQ,cAAc,QAAQ,GAAG,SAAS,MAAM;AAAA,UACpE;AAAA,QACF;AAGA,cAAM,QAAQ;AAAA,UACZ,SAAS,IAAI,CAAC,eAAe;AAC3B,kBAAM,eAAe,KAAK,QAAQ,cAAc,UAAU;AAC1D,mBAAO,qBAAqB,YAAY;AAAA,UAC1C,CAAC;AAAA,QACH;AAGA,cAAM,QAAQ;AAAA,UACZ,UAAU,IAAI,CAAC,aAAa;AAC1B,kBAAM,eAAe,KAAK,QAAQ,cAAc,QAAQ;AACxD,mBAAO,mBAAmB,YAAY;AAAA,UACxC,CAAC;AAAA,QACH;AAEA,QAAAA,SAAQ;AAAA,MACV,SAAS,KAAK;AACZ,eAAO,sCAAsC,GAAG,EAAE;AAAA,MACpD;AAAA,IACF,GAAG;AAAA,EACL,CAAC;AACH;AAMA,eAAsB,qBAAqB,YAAmC;AAE5E,QAAM,QAAQ,MAAM,GAAG,QAAQ,UAAU;AAEzC,QAAM,QAAQ;AAAA,IACZ,MAAM,IAAI,OAAO,SAAS;AACxB,YAAM,WAAW,KAAK,KAAK,YAAY,IAAI;AAG3C,YAAM,OAAO,MAAM,GAAG,MAAM,QAAQ;AAGpC,UAAI,KAAK,YAAY,GAAG;AACtB,cAAM,GAAG,GAAG,UAAU,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,MACxD,OAAO;AAEL,cAAM,GAAG,GAAG,QAAQ;AAAA,MACtB;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAMA,eAAsB,mBAAmB,cAAqC;AAC5E,QAAM,GAAG,GAAG,cAAc,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAC5D;AAOA,eAAsB,SAAS,KAAa,MAA6B;AACvE,MAAI;AAEF,UAAM,GAAG,OAAO,GAAG;AAGnB,UAAM,GAAG,MAAM,KAAK,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAGtD,UAAM,GAAG,SAAS,KAAK,IAAI;AAAA,EAC7B,SAAS,KAAc;AACrB,QAAK,IAA8B,SAAS,UAAU;AACpD,cAAQ,KAAK;AAAA,EAAK,WAAW,iBAAiB,GAAG,8BAA8B;AAAA,IACjF,OAAO;AACL,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAMA,eAAe,uBAAuB,cAAsB,aAAoC;AAC9F,QAAM,cAAc,YAAY,QAAQ,SAAS,GAAG,EAAE,QAAQ,SAAS,CAAC,MAAM,EAAE,YAAY,CAAC;AAE7F,QAAM,MAAM,KAAK,QAAQ,cAAc,kBAAkB;AACzD,QAAM,OAAO,KAAK,QAAQ,cAAc,mCAAmC;AAE3E,MAAI;AACF,QAAI,UAAU,MAAM,GAAG,SAAS,KAAK,MAAM;AAC3C,cAAU,QAAQ,WAAW,oBAAoB,WAAW;AAC5D,cAAU,QAAQ,WAAW,oBAAoB,WAAW;AAC5D,UAAM,GAAG,UAAU,MAAM,SAAS,MAAM;AAAA,EAC1C,SAAS,KAAc;AACrB,QAAK,IAA8B,SAAS,UAAU;AACpD,cAAQ,KAAK;AAAA,EAAK,WAAW,wBAAwB,GAAG,oBAAoB;AAAA,IAC9E,OAAO;AACL,YAAM;AAAA,IACR;AAAA,EACF;AACF;;;AClLA,OAAO,SAAuB;AAC9B,OAAOC,SAAQ;AAGf,IAAI,gBAA4B;AAwCzB,SAAS,eAAe,OAAe,SAAS,OAAwB;AAC7E,QAAM,iBAA2B,CAAC;AAElC,QAAM,UAAU,IAAI;AAAA,IAClB,MAAM;AAAA,IACN,UAAU;AAAA,EACZ,CAAC;AAED,kBAAgB;AAChB,UAAQ,MAAM;AAGd,QAAM,MAAM,CAAC,QAAgB;AAC3B,QAAI,CAAC,OAAQ,SAAQ,KAAK,GAAG;AAAA,EAC/B;AAEA,SAAO;AAAA,IACL,MAAM,CAAC,YAAoB;AAEzB,UAAI,eAAe,SAAS,GAAG;AAC7B,gBAAQ,KAAK;AACb,YAAI,GAAGC,IAAG,MAAM,QAAG,CAAC,IAAI,eAAe,eAAe,SAAS,CAAC,CAAC,EAAE;AAAA,MACrE;AACA,qBAAe,KAAK,OAAO;AAC3B,cAAQ,OAAO;AACf,cAAQ,MAAM;AAAA,IAChB;AAAA,IAEA,MAAM,CAAC,YAAoB;AAEzB,UAAI,eAAe,SAAS,GAAG;AAC7B,gBAAQ,KAAK;AACb,YAAI,GAAGA,IAAG,MAAM,QAAG,CAAC,IAAI,eAAe,eAAe,SAAS,CAAC,CAAC,EAAE;AAAA,MACrE,OAAO;AACL,gBAAQ,KAAK;AAAA,MACf;AACA,sBAAgB;AAChB,UAAI,SAAS;AACX,YAAI,EAAE;AACN,YAAI,GAAGA,IAAG,MAAM,QAAG,CAAC,IAAI,OAAO,EAAE;AAAA,MACnC;AAAA,IACF;AAAA,IAEA,MAAM,CAAC,YAAoB;AACzB,cAAQ,KAAK;AACb,sBAAgB;AAChB,UAAI,GAAGA,IAAG,IAAI,QAAG,CAAC,IAAIA,IAAG,IAAI,OAAO,CAAC,EAAE;AAAA,IACzC;AAAA,IAEA,MAAM,OAAU,OAAqC;AACnD,UAAI;AACF,eAAO,MAAM,GAAG;AAAA,MAClB,SAAS,OAAO;AACd,cAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAE1E,gBAAQ,KAAK;AACb,wBAAgB;AAChB,YAAIA,IAAG,KAAK;AAAA,EAAK,KAAK,EAAE,CAAC;AACzB,mBAAW,QAAQ,gBAAgB;AACjC,cAAIA,IAAG,KAAK,kBAAQ,IAAI,EAAE,CAAC;AAAA,QAC7B;AACA,YAAIA,IAAG,IAAI,yBAAU,YAAY,EAAE,CAAC;AACpC,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;;;ACrHA,OAAO,WAAW;AAUlB,eAAsB,yBACpBC,iBACA,MACA,MAA8B,CAAC,GAChB;AACf,MAAI;AACF,UAAM,MAAMA,iBAAgB,MAAM;AAAA,MAChC,KAAK,EAAE,GAAG,QAAQ,KAAK,GAAG,IAAI;AAAA,MAC9B,OAAO,CAAC,QAAQ,QAAQ,MAAM;AAAA,IAChC,CAAC;AAAA,EACH,SAAS,OAAO;AACd,UAAM,MAAM;AACZ,UAAM,IAAI,MAAM,IAAIA,eAAc,IAAI,KAAK,KAAK,GAAG,CAAC,YAAY,IAAI,UAAU,EAAE,IAAI,IAAI,UAAU,EAAE,EAAE;AAAA,EACxG;AACF;AAQA,eAAsB,QAAQA,iBAAuC;AACnE,SAAO,yBAAyBA,iBAAgB,CAAC,SAAS,GAAG;AAAA,IAC3D,UAAU;AAAA,EACZ,CAAC;AACH;AAQA,eAAsB,SAASA,iBAAuC;AACpE,SAAO,yBAAyBA,iBAAgB,CAAC,UAAU,GAAG;AAAA,IAC5D,UAAU;AAAA,EACZ,CAAC;AACH;;;ARnCA,SAAS,YAAYC,OAAuB;AAC1C,SAAOA,MAAK,WAAW,GAAG,KAAKA,MAAK,WAAW,IAAI,KAAKA,MAAK,WAAW,KAAK;AAC/E;AAEA,eAAsB,OAAO;AAAA,EAC3B;AAAA,EACA;AAAA,EACA,eAAAC;AAAA,EACA,gBAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AACX,GAAiC;AAE/B,QAAM,cAAc,QAAQ,IAAI;AAGhC,QAAM,WAAW,eAAe;AAChC,QAAM,kBAAkB,eAAe,YAAY,WAAW;AAE9D,QAAM,WAAW,eAAe,oBAAoB,MAAM;AAE1D,MAAI;AAEF,aAAS,KAAK,yBAAyB;AACvC,UAAM,MAAM,cAAc,EAAE,WAAW,KAAK,CAAC;AAC7C,YAAQ,MAAM,YAAY;AAG1B,QAAI,iBAAiB;AACnB,eAAS,KAAK,wBAAwB;AACtC,YAAM,aAAaC,SAAQ,aAAa,WAAW;AAGnD,UAAI,aAAa,WAAW,UAAU,GAAG;AACvC,cAAM,IAAI;AAAA,UACR;AAAA,yDAC4D,WAAW,eAAe,WAAW;AAAA,QACnG;AAAA,MACF;AAEA,YAAM,GAAG,YAAY,cAAc;AAAA,QACjC,WAAW;AAAA,QACX,QAAQ,CAAC,QAAQ,CAAC,IAAI,SAAS,cAAc,KAAK,CAAC,IAAI,SAAS,MAAM;AAAA,MACxE,CAAC;AAAA,IACH,OAAO;AACL,eAAS,KAAK,4BAA4B;AAC1C,YAAM,iBAAiB,UAAU;AAAA,QAC/B,KAAK,QAAQ,IAAI;AAAA,QACjB,KAAK;AAAA,QACL,OAAO;AAAA,QACP,UAAU;AAAA,MACZ,CAAC;AAAA,IACH;AAGA,aAAS,KAAK,mBAAmB;AACjC,UAAM,cAAc,YAAY,QAAQ,SAAS,GAAG,EAAE,QAAQ,SAAS,CAAC,MAAM,EAAE,YAAY,CAAC;AAC7F,UAAM,cAAc,EAAE,cAAc,aAAa,aAAa,YAAY,WAAW,CAAC;AAGtF,aAAS,KAAK,yBAAyB;AACvC,UAAM,QAAQD,eAAc;AAG5B,aAAS,KAAK,uBAAuB;AACrC,UAAM,SAASA,eAAc;AAG7B,UAAM,gBAAgB,KAAK,cAAc,MAAM;AAC/C,QAAI,CAAC,WAAW,aAAa,GAAG;AAC9B,eAAS,KAAK,kBAAkB;AAChC,YAAM,QAAQ,YAAY;AAC1B,YAAM,UAAU,YAAY;AAC5B,YAAM,UAAU,cAAc,gBAAgB;AAE9C,UAAID,gBAAe;AACjB,iBAAS,KAAK,oBAAoBA,cAAa,GAAG;AAClD,cAAM,UAAU,cAAcA,cAAa;AAC3C,cAAM,YAAY,cAAcA,cAAa;AAAA,MAC/C;AAAA,IACF;AAGA,aAAS,KAAK,wBAAwB;AACtC,UAAM,UAAU,EAAE,cAAc,QAAQ,KAAK,CAAC;AAG9C,aAAS,KAAK,WAAW,WAAW,EAAE;AAAA,EACxC,SAAS,OAAO;AACd,aAAS,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AACpE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,UAAU,gBAAgB;AAChC,QAAM,eAAe,SAAS,aAAa,YAAY;AAGvD,cAAY,aAAa,cAAc,cAAc,SAASC,eAAc;AAC9E;;;ASlHA,SAAS,SAAS,gBAAgB;AAClC,SAAS,SAAS,QAAAE,aAAY;AAa9B,eAAsB,gBAAgB,cAA4C;AAChF,QAAM,YAAY,QAAQ,YAAY;AACtC,QAAM,OAAoB,CAAC;AAE3B,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,QAAQ,SAAS;AAAA,EACpC,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,aAAW,QAAQ,UAAU;AAC3B,UAAM,aAAaA,MAAK,WAAW,MAAM,qCAAqC;AAC9E,QAAI;AACF,YAAM,UAAU,MAAM,SAAS,YAAY,MAAM;AACjD,YAAM,UAAU,QAAQ,MAAM,2CAA2C;AACzE,YAAM,UAAU,QAAQ,MAAM,0CAA0C;AACxE,UAAI,WAAW,SAAS;AACtB,cAAM,WAAW,OAAO,QAAQ,CAAC,CAAC;AAClC,cAAM,UAAU,OAAO,QAAQ,CAAC,CAAC;AACjC,aAAK,KAAK;AAAA,UACR,SAAS;AAAA,UACT;AAAA,UACA;AAAA,UACA,QAAQ,WAAW;AAAA,QACrB,CAAC;AAAA,MACH;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,SAAO;AACT;AAGO,SAAS,eAAe,WAAgC;AAC7D,QAAM,cAAc,IAAI,IAAI,UAAU,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC;AAC1D,WAAS,SAAS,GAAG,UAAU,KAAK,UAAU,IAAI;AAChD,QAAI,CAAC,YAAY,IAAI,MAAM,EAAG,QAAO;AAAA,EACvC;AACA,SAAO;AACT;;;ACxDA,OAAO,WAAW;AAUlB,eAAsB,iCAAiC,eAAuB,SAAS,QAAyB;AAE9G,QAAM,CAAC,OAAO,IAAI,IAAI,cAAc,QAAQ,WAAW,EAAE,EAAE,MAAM,GAAG;AAGpE,QAAM,iBAAiB,qCAAqC,KAAK,IAAI,IAAI,IAAI,MAAM;AAEnF,MAAI;AAEF,UAAM,WAAW,MAAM,MAAM,IAAI,cAAc;AAC/C,UAAM,cAAc,SAAS;AAG7B,WAAO,YAAY,WAAW;AAAA,EAChC,SAAS,OAAO;AAEd,WAAO;AAAA,EACT;AACF;;;AC5BA,SAAS,WAAAC,gBAAe;AASxB,eAAsB,iBAAiBC,OAAgC;AACrE,QAAM,QAAQ,MAAMD,SAAQC,KAAI;AAGhC,SAAO,MAAM,WAAW,KAAM,MAAM,WAAW,KAAK,MAAM,CAAC,MAAM;AACnE;;;AdAA,eAAe,OAAsB;AAEnC,QAAM,kBAAkB,MAAM,iCAAiC,YAAY;AAG3E,cAAY,eAAe;AAG3B,QAAM,cAAc,EAAE,QAAQ,IAAI,OAAO,EAAE,QAAQ,CAAC,SAAiB,KAAK,EAAE;AAC5E,QAAM,gBAAgB,EAAE,mBAAmB,KAAK;AAGhD,MAAI,CAAC,IAAI,WAAW;AAClB,QAAI,YAAY,MAAM;AAAA,MACpB;AAAA,QACE,SAAS;AAAA,QACT,SAAS;AAAA,QACT,OAAO;AAAA,QACP,UAAU,CAAC,SAAS;AAClB,gBAAM,aAAa,oBAAoBC,UAASC,SAAQ,IAAI,CAAC,CAAC;AAC9D,iBAAO,WAAW,QAAQ,OAAO,yBAAyB,WAAW,WAAW,CAAC,KAAK,eAAe;AAAA,QACvG;AAAA,MACF;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAGA,MAAI,CAAC,IAAI,eAAe;AACtB,QAAI,gBAAgB;AAAA,EACtB;AAEA,QAAM,eAAeA,SAAQ,IAAI,SAAS;AAC1C,QAAM,cAAcD,UAAS,YAAY,GAAG,YAAY,KAAK;AAG7D,MAAIE,YAAW,YAAY,KAAK,CAAE,MAAM,iBAAiB,YAAY,GAAI;AACvE,UAAM,UAAU,IAAI,cAAc,MAAM,sBAAsB,qBAAqB,YAAY;AAC/F,UAAM,UAAU,GAAG,OAAO;AAE1B,UAAM,SAAS,MAAM;AAAA,MACnB;AAAA,QACE;AAAA,QACA,OAAO;AAAA,QACP,SAAS;AAAA,UACP,EAAE,MAAM,mBAAmB,OAAO,SAAS;AAAA,UAC3C,EAAE,MAAM,sCAAsC,OAAO,SAAS;AAAA,QAChE;AAAA,MACF;AAAA,MACA;AAAA,IACF;AAEA,QAAI,WAAW,UAAU;AACvB,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAGA,QAAM,aAAa,MAAM,iBAAiB,cAAc,aAAa,aAAa;AAGlF,QAAM,aAAa,MAAM;AAAA,IACvB;AAAA,MACE,SAAS;AAAA,MACT,SAAS,SAAS,WAAW;AAAA,MAC7B,OAAO;AAAA,IACT;AAAA,IACA;AAAA,EACF;AAGA,QAAM,gBAA+B;AAAA,IACnC;AAAA,IACA;AAAA,IACA,eAAe,IAAI;AAAA,IACnB,gBAAgB,IAAI;AAAA,IACpB,aAAa,IAAI,QAAQ;AAAA,IACzB;AAAA,IACA;AAAA,EACF;AAEA,QAAM,OAAO,aAAa;AAC5B;AAEA,KAAK,EAAE,MAAM,QAAQ,KAAK;AAG1B,SAAS,aAAa,GAAW,SAAS,IAAY;AACpD,SAAO,GAAG,CAAC,YAAO,MAAO,CAAC,OAAO,MAAO,CAAC,OAAO,OAAO,CAAC,GAAG,MAAM;AACnE;AAGA,eAAe,iBAAiB,cAAsB,OAAe,SAAkC;AACrG,QAAM,YAAY,MAAM,gBAAgB,YAAY;AACpD,QAAM,YAAY,eAAe,SAAS;AAE1C,MAAI,UAAU,SAAS,GAAG;AACxB,YAAQ,KAAKC,IAAG,IAAI,gDAAgD,CAAC;AACrE,eAAW,KAAK,WAAW;AACzB,cAAQ,KAAKA,IAAG,IAAI,KAAK,EAAE,OAAO,eAAe,EAAE,QAAQ,cAAc,EAAE,OAAO,YAAY,EAAE,MAAM,GAAG,CAAC;AAAA,IAC5G;AACA,YAAQ,KAAK;AAAA,EACf;AAEA,QAAM,UAAU,CAAC,GAAG,IAAI,IAAI,EAAE,EAAE,OAAO,CAAC,MAAM,MAAM,SAAS;AAC7D,QAAM,SAAS,MAAM;AAAA,IACnB;AAAA,MACE,SAAS;AAAA,MACT;AAAA,MACA,SAAS;AAAA,QACP,GAAI,YAAY,IAAI,CAAC,EAAE,MAAM,aAAa,WAAW,cAAc,GAAG,OAAO,UAAU,CAAC,IAAI,CAAC;AAAA,QAC7F,EAAE,MAAM,aAAa,GAAG,YAAY,GAAG,OAAO,EAAE;AAAA,QAChD,GAAG,QAAQ,IAAI,CAAC,OAAO,EAAE,MAAM,aAAa,CAAC,GAAG,OAAO,EAAE,EAAE;AAAA,QAC3D,EAAE,MAAM,iBAAiB,OAAO,GAAG;AAAA,MACrC;AAAA,IACF;AAAA,IACA;AAAA,EACF;AAEA,MAAI,WAAW,GAAI,QAAO;AAE1B,QAAM,SAAS,MAAM;AAAA,IACnB;AAAA,MACE,SAAS;AAAA,MACT,SAAS,OAAO,SAAS;AAAA,MACzB;AAAA,MACA,UAAU,CAAC,QAAQ;AACjB,cAAM,IAAI,OAAO,GAAG;AACpB,YAAI,OAAO,MAAM,CAAC,KAAK,IAAI,KAAK,IAAI,IAAK,QAAO;AAChD,YAAI,IAAI,OAAO,EAAG,QAAO;AACzB,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA;AAAA,EACF;AACA,SAAO,OAAO,MAAM;AACtB;","names":["existsSync","basename","resolve","pc","readFile","resolve","options","pc","pc","packageManager","pc","pc","resolve","pc","pc","packageManager","path","newBranchName","packageManager","resolve","join","readdir","path","basename","resolve","existsSync","pc"]}
package/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import './dist/index.js'
3
+ import './dist/index.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cellajs/create-cella",
3
- "version": "0.1.7",
3
+ "version": "0.2.1",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "description": "Cella is a TypeScript template to create web apps with sync and offline capabilities.",
@@ -15,33 +15,52 @@
15
15
  "homepage": "https://cellajs.com",
16
16
  "author": "CellaJS <info@cellajs.com>",
17
17
  "engines": {
18
- "node": ">=20.14.0"
18
+ "node": "24.x"
19
19
  },
20
20
  "type": "module",
21
- "main": "./src/index.ts",
21
+ "main": "./dist/index.js",
22
+ "files": [
23
+ "configs",
24
+ "dist",
25
+ "index.js",
26
+ "README.md"
27
+ ],
28
+ "imports": {
29
+ "#/*": "./src/*"
30
+ },
22
31
  "bin": {
23
32
  "create-cella": "index.js"
24
33
  },
25
34
  "scripts": {
26
- "start": "tsx ./src/index.ts",
35
+ "start": "tsx ./src/create-cella-cli.ts",
27
36
  "clean": "rimraf ./dist",
28
37
  "build": "tsup",
29
38
  "test-build": "pnpm run build && node index.js",
30
- "prepublishOnly": "pnpm run build"
39
+ "prepublishOnly": "pnpm run test:release",
40
+ "check": "pnpm ts && pnpm biome check --write .",
41
+ "ts": "tsgo --pretty",
42
+ "lint": "biome check .",
43
+ "lint:fix": "biome check --write .",
44
+ "test": "vitest run",
45
+ "test:release": "pnpm run build && vitest run tests/release-artifact.test.ts",
46
+ "test:watch": "vitest"
31
47
  },
32
- "packageManager": "pnpm@9.11.0",
33
48
  "dependencies": {
34
- "@inquirer/prompts": "^6.0.1",
35
- "axios": "^1.8.3",
36
- "commander": "^12.1.0",
37
- "cross-spawn": "^7.0.3",
38
- "giget": "^1.2.3",
39
- "picocolors": "^1.1.0",
40
- "validate-npm-package-name": "^5.0.1",
41
- "yocto-spinner": "^0.1.0"
49
+ "@inquirer/prompts": "^8.5.2",
50
+ "axios": "^1.17.0",
51
+ "commander": "^15.0.0",
52
+ "giget": "^3.1.2",
53
+ "nano-spawn": "^2.0.0",
54
+ "ora": "^9.3.0",
55
+ "picocolors": "^1.1.1",
56
+ "validate-npm-package-name": "^8.0.0"
42
57
  },
43
58
  "devDependencies": {
44
- "tsup": "^8.3.5",
45
- "tsx": "^4.19.2"
59
+ "@types/node": "catalog:",
60
+ "@typescript/native-preview": "catalog:",
61
+ "@types/validate-npm-package-name": "^4.0.2",
62
+ "tsup": "catalog:",
63
+ "tsx": "catalog:",
64
+ "vitest": "catalog:"
46
65
  }
47
66
  }
package/src/add-remote.ts DELETED
@@ -1,50 +0,0 @@
1
- import yoctoSpinner from 'yocto-spinner';
2
- import { runGitCommand } from './utils/run-git-command.ts';
3
- import { CELLA_REMOTE_URL } from './constants.ts';
4
-
5
- interface AddRemoteOptions {
6
- targetFolder: string;
7
- remoteUrl?: string; // Optional, defaults to CELLA_REMOTE_URL
8
- remoteName?: string; // Optional, defaults to 'upstream'
9
- }
10
-
11
- export async function addRemote({
12
- targetFolder,
13
- remoteUrl = CELLA_REMOTE_URL,
14
- remoteName = 'upstream',
15
- }: AddRemoteOptions): Promise<void> {
16
-
17
- // Spinner for adding remote
18
- const remoteSpinner = yoctoSpinner({
19
- text: 'Adding remote',
20
- }).start();
21
-
22
- try {
23
- // Check if the remote exists
24
- let remote: string | null = null;
25
-
26
- try {
27
- remote = await runGitCommand({ targetFolder, command: `remote get-url ${remoteName}` });
28
- } catch (error: any) {
29
- // If the remote doesn't exist, it throws a fatal error
30
- remote = null;
31
- }
32
-
33
- // Add or update the remote if it doesn't exist or differs from `remoteUrl`
34
- if (!remote) {
35
- await runGitCommand({ targetFolder, command: `remote add ${remoteName} ${remoteUrl}` });
36
- remoteSpinner.success('Remote added successfully.');
37
- } else if (remote !== remoteUrl) {
38
- // Remove existing remote and set the new URL
39
- await runGitCommand({ targetFolder, command: `remote remove ${remoteName}` });
40
- await runGitCommand({ targetFolder, command: `remote add ${remoteName} ${remoteUrl}` });
41
- remoteSpinner.success('Remote updated successfully.');
42
- } else {
43
- remoteSpinner.success('Remote is already configured correctly.');
44
- }
45
- } catch (error) {
46
- console.error(error);
47
- remoteSpinner.error('Failed to add remote.');
48
- process.exit(1);
49
- }
50
- }
package/src/cli.ts DELETED
@@ -1,106 +0,0 @@
1
- import { basename, resolve } from 'node:path';
2
- import { Command, InvalidArgumentError } from 'commander';
3
-
4
- import { NAME, VERSION } from './constants.ts';
5
- import { validateProjectName } from './utils/validate-project-name.ts';
6
-
7
- // Define types for CLI options
8
- interface CLIOptions {
9
- skipNewBranch: boolean;
10
- skipClean: boolean;
11
- skipGit: boolean;
12
- skipInstall: boolean;
13
- skipGenerate: boolean;
14
- newBranchName?: string;
15
- }
16
-
17
- // Define CLI configuration
18
- interface CLIConfig {
19
- options: CLIOptions;
20
- args: string[];
21
- directory: string | null;
22
- newBranchName: string | null;
23
- createNewBranch: boolean | null;
24
- packageManager: string;
25
- }
26
-
27
- // Initialize CLI variables
28
- let directory: string | null = null;
29
- let newBranchName: string | null = null;
30
- let createNewBranch: boolean | null = null;
31
- const packageManager = 'pnpm';
32
-
33
- // Set up the CLI command using Commander
34
- export const command = new Command(NAME)
35
- .version(
36
- VERSION,
37
- '-v, --version',
38
- `Output the current version of ${NAME}.`
39
- )
40
- .argument('[directory]', 'The directory name for the new project.')
41
- .usage('[directory] [options]')
42
- .helpOption('-h, --help', 'Display this help message.')
43
- .option('--skip-new-branch', 'Skip creating a new branch during initialization.', false)
44
- .option('--skip-install', 'Skip the installation of packages.', false)
45
- .option('--skip-generate', 'Skip generating SQL files.', false)
46
- .option('--skip-clean', 'Skip cleaning the `cella` template.', false)
47
- .option('--skip-git', 'Skip initializing a git repository.', false)
48
- .option(
49
- '--new-branch-name <name>',
50
- 'Specify a new branch name to create and use.',
51
- (name: string) => {
52
- if (typeof name === 'string') {
53
- name = name.trim();
54
- }
55
-
56
- if (name) {
57
- const validation = validateProjectName(basename(resolve(name)));
58
-
59
- if (!validation.valid) {
60
- throw new InvalidArgumentError(
61
- `Invalid branch name: ${validation.problems[0]}`
62
- );
63
- }
64
-
65
- createNewBranch = true;
66
- newBranchName = name;
67
- }
68
- }
69
- )
70
- .action((name: string) => {
71
- if (typeof name === 'string') {
72
- name = name.trim();
73
- }
74
-
75
- if (name) {
76
- const validation = validateProjectName(basename(resolve(name)));
77
-
78
- if (!validation.valid) {
79
- throw new InvalidArgumentError(
80
- `Invalid project name: ${validation.problems[0]}`
81
- );
82
- }
83
-
84
- directory = name;
85
- }
86
- })
87
- .parse();
88
-
89
- // Gather the CLI options and arguments
90
- const options: CLIOptions = command.opts<CLIOptions>({
91
- skipNewBranch: false,
92
- skipClean: false,
93
- skipGit: false,
94
- skipInstall: false,
95
- skipGenerate: false,
96
- });
97
-
98
- // Export the CLI configuration for use in other modules
99
- export const cli: CLIConfig = {
100
- options,
101
- args: command.args,
102
- directory,
103
- newBranchName,
104
- createNewBranch,
105
- packageManager,
106
- };
package/src/constants.ts DELETED
@@ -1,61 +0,0 @@
1
- export const NAME = 'create-cella';
2
-
3
- // URL of the template repository
4
- export const TEMPLATE_URL = 'github:cellajs/cella';
5
-
6
- // URL to the repository
7
- export const CELLA_REMOTE_URL: string = 'git@github.com:cellajs/cella.git';
8
-
9
- // Import package.json dynamically for version and website information
10
- import packageJson from '../package.json' assert { type: 'json' };
11
-
12
- // Export details from package.json
13
- export const DESCRIPTION: string = packageJson.description;
14
- export const VERSION: string = packageJson.version;
15
- export const AUTHOR: string = packageJson.author;
16
- export const WEBSITE: string = packageJson.homepage;
17
- export const GITHUB: string = packageJson.repository.url;
18
-
19
- // Files or folders to be removed from the template after downloading
20
- export const TO_REMOVE: string[] = [
21
- 'info',
22
- './cli/create-cella',
23
- ];
24
-
25
- // Specific folder contents to be cleaned out from the template
26
- export const TO_CLEAN: string[] = [
27
- './backend/drizzle',
28
- ];
29
-
30
- // Files to copy/paste after downloading
31
- export const TO_COPY: Record<string, string> = {
32
- './backend/.env.example': './backend/.env',
33
- './frontend/.env.example': './frontend/.env',
34
- './info/QUICKSTART.md': 'README.md',
35
- };
36
-
37
- // Files to be editted after downloading
38
- export const TO_EDIT: Record<string, { regexMatch: RegExp; replaceWith: string }[]> = {
39
- './config/default.ts': [
40
- {
41
- regexMatch: /enabledAuthenticationStrategies:\s*\[[^\]]+\]\s*as\s*const,/g,
42
- replaceWith: "enabledAuthenticationStrategies: ['password', 'passkey'] as const,",
43
- },
44
- {
45
- regexMatch: /uploadEnabled\:\s*(true|false),/g,
46
- replaceWith: "uploadEnabled: false,",
47
- },
48
- {
49
- regexMatch: /enabledOauthProviders:\s*\[[^\]]+\]\s*as\s*const,/g,
50
- replaceWith: "enabledOauthProviders: [] as const,",
51
- },
52
- ],
53
- };
54
- // ASCII logo for the CLI output
55
- export const LOGO = `
56
- _ _
57
- ▒▓█████▓▒ ___ ___| | | __ _
58
- ▒▓█ █▓▒ / __/ _ \\ | |/ _\` |
59
- ▒▓█ █▓▒ | (_| __/ | | (_| |
60
- ▒▓█████▓▒ \\___\\___|_|_|\\__,_|
61
- `;