From 94b647eb854412d3b33002dc6ea0ded34590fb9a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Dec 2025 01:17:11 +0000 Subject: [PATCH 1/4] Initial plan From 434ee6cd3e94c29e3510c788efd6f011f4fc85fd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Dec 2025 01:27:47 +0000 Subject: [PATCH 2/4] feat: add /install/prettier endpoint with package manager support - Create install scripts data structure (install-scripts.yml) - Implement install-prettier.sh script supporting pnpm, npm, yarn, bun - Add dynamic route for /install/[script] with package manager query param - Update /install route to list available scripts when accessed from browser - Script installs Prettier, Husky, lint-staged and creates GitHub Actions workflow - Tested with npm and yarn, works correctly Co-authored-by: marcelocra <2532492+marcelocra@users.noreply.github.com> --- setup/install-prettier.sh | 367 ++++++++++++++++++++++ www/app/install/[script]/route.ts | 115 +++++++ www/app/install/route.ts | 177 +++++++++++ www/data/install-scripts.yml | 22 ++ www/lib/install-scripts.ts | 40 +++ www/pnpm-lock.yaml | 500 +++++++++++++++++++++++++++--- 6 files changed, 1180 insertions(+), 41 deletions(-) create mode 100644 setup/install-prettier.sh create mode 100644 www/app/install/[script]/route.ts create mode 100644 www/data/install-scripts.yml create mode 100644 www/lib/install-scripts.ts diff --git a/setup/install-prettier.sh b/setup/install-prettier.sh new file mode 100644 index 0000000..eb737ae --- /dev/null +++ b/setup/install-prettier.sh @@ -0,0 +1,367 @@ +#!/usr/bin/env bash +# DevMagic - Prettier Installation Script +# Installs and configures Prettier, Husky, and lint-staged +# Usage: +# Latest version: curl -fsSL https://devmagic.run/install/prettier | bash +# With npm: curl -fsSL https://devmagic.run/install/prettier?pm=npm | bash + +set -e + +# --- Configuration --- +PACKAGE_MANAGER="${PACKAGE_MANAGER:-pnpm}" # Default to pnpm + +# --- Colors for output --- +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +PURPLE='\033[0;35m' +NC='\033[0m' # No Color + +# --- Header --- +echo -e "${PURPLE}" +echo "✨ Prettier Installation" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "Setting up automatic code formatting" +echo -e "Package Manager: ${PACKAGE_MANAGER}${NC}" +echo + +# --- Prerequisite Checks --- + +# Check if package.json exists +if [ ! -f "package.json" ]; then + echo -e "${RED}❌ package.json not found. This script must be run in a Node.js project.${NC}" + exit 1 +fi + +# Check if Git repository exists +if ! git rev-parse --is-inside-work-tree &> /dev/null; then + echo -e "${RED}❌ Not a Git repository. Husky requires Git to function.${NC}" + exit 1 +fi + +# Verify package manager is installed +if ! command -v "$PACKAGE_MANAGER" &> /dev/null; then + echo -e "${RED}❌ ${PACKAGE_MANAGER} is not installed. Please install it first.${NC}" + exit 1 +fi + +# Validate package manager +case "$PACKAGE_MANAGER" in + pnpm|npm|yarn|bun) + ;; + *) + echo -e "${RED}❌ Unsupported package manager: ${PACKAGE_MANAGER}${NC}" + echo -e "${YELLOW} Supported: pnpm, npm, yarn, bun${NC}" + exit 1 + ;; +esac + +echo -e "${BLUE}✓ Prerequisites check passed${NC}" +echo + +# --- Helper Functions --- + +# Detect if we're in a workspace (monorepo) +is_workspace() { + if [ "$PACKAGE_MANAGER" = "pnpm" ]; then + # Check for pnpm-workspace.yaml + [ -f "pnpm-workspace.yaml" ] + elif [ "$PACKAGE_MANAGER" = "npm" ]; then + # Check for workspaces in package.json + grep -q '"workspaces"' package.json 2>/dev/null + elif [ "$PACKAGE_MANAGER" = "yarn" ]; then + # Check for workspaces in package.json + grep -q '"workspaces"' package.json 2>/dev/null + else + # bun doesn't have strong workspace support yet + false + fi +} + +# Get the appropriate install command +get_install_cmd() { + local packages="$1" + local dev_flag="$2" + + case "$PACKAGE_MANAGER" in + pnpm) + if is_workspace; then + echo "pnpm add -w ${dev_flag} ${packages}" + else + echo "pnpm add ${dev_flag} ${packages}" + fi + ;; + npm) + echo "npm install ${dev_flag} ${packages}" + ;; + yarn) + echo "yarn add ${dev_flag} ${packages}" + ;; + bun) + echo "bun add ${dev_flag} ${packages}" + ;; + esac +} + +# Get the appropriate dev flag +get_dev_flag() { + case "$PACKAGE_MANAGER" in + pnpm|npm) + echo "--save-dev" + ;; + yarn|bun) + echo "-D" + ;; + esac +} + +# Get the appropriate exact flag for prettier +get_exact_flag() { + case "$PACKAGE_MANAGER" in + pnpm|npm) + echo "--save-exact" + ;; + yarn) + echo "--exact" + ;; + bun) + echo "" # bun doesn't have an exact flag, uses exact by default + ;; + esac +} + +# Get the exec command +get_exec_cmd() { + local cmd="$1" + + case "$PACKAGE_MANAGER" in + pnpm|npm|yarn) + echo "${PACKAGE_MANAGER} exec ${cmd}" + ;; + bun) + echo "bunx ${cmd}" + ;; + esac +} + +# --- Step 1: Install Prettier --- +echo -e "${BLUE}📦 Step 1/7: Installing Prettier...${NC}" + +DEV_FLAG=$(get_dev_flag) +EXACT_FLAG=$(get_exact_flag) +INSTALL_CMD=$(get_install_cmd "prettier" "${DEV_FLAG} ${EXACT_FLAG}") + +echo -e "${YELLOW}Running: ${INSTALL_CMD}${NC}" +eval "$INSTALL_CMD" + +echo -e "${GREEN}✓ Prettier installed${NC}" +echo + +# --- Step 2: Create .prettierrc --- +echo -e "${BLUE}📝 Step 2/7: Creating .prettierrc...${NC}" + +if [ -f ".prettierrc" ]; then + echo -e "${YELLOW}⚠️ .prettierrc already exists, skipping...${NC}" +else + cat > .prettierrc << 'EOF' +{} +EOF + echo -e "${GREEN}✓ .prettierrc created${NC}" +fi +echo + +# --- Step 3: Create .prettierignore --- +echo -e "${BLUE}📝 Step 3/7: Creating .prettierignore...${NC}" + +if [ -f ".prettierignore" ]; then + echo -e "${YELLOW}⚠️ .prettierignore already exists, skipping...${NC}" +else + cat > .prettierignore << 'EOF' +# Ignore artifacts: +build +coverage +dist +.next +out +node_modules +pnpm-lock.yaml +yarn.lock +package-lock.json +bun.lockb +EOF + echo -e "${GREEN}✓ .prettierignore created${NC}" +fi +echo + +# --- Step 4: Create .editorconfig --- +echo -e "${BLUE}📝 Step 4/7: Creating .editorconfig...${NC}" + +if [ -f ".editorconfig" ]; then + echo -e "${YELLOW}⚠️ .editorconfig already exists, skipping...${NC}" +else + cat > .editorconfig << 'EOF' +root = true + +[*] +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true +end_of_line = lf +charset = utf-8 +spelling_languages = en-US,en,pt-BR,pt +max_line_length = 120 + +[*.{lua,toml,yaml,yml,json,jsonc,json5}] +max_line_length = 80 + +[*.{fs,fsi,fsx}] +indent_size = 4 +# Simplifies reordering and adding new items. Details: +# https://learn.microsoft.com/en-us/dotnet/fsharp/style-guide/formatting#multiline-bracket-formatting-style +fsharp_multiline_bracket_style = stroustrup + +[*.{py,sh,zsh,bash,gitconfig,tmux.conf}] +indent_size = 4 +max_line_length = 80 + +[*.{md,markdown,mdx}] +indent_size = 4 +max_line_length = unset # Not wrapping lines reduces diffs. +trim_trailing_whitespace = false # Double trailing spaces can be used to break lines. +EOF + echo -e "${GREEN}✓ .editorconfig created${NC}" +fi +echo + +# --- Step 5: Install Husky and lint-staged --- +echo -e "${BLUE}📦 Step 5/7: Installing Husky and lint-staged...${NC}" + +INSTALL_CMD=$(get_install_cmd "husky lint-staged" "${DEV_FLAG}") + +echo -e "${YELLOW}Running: ${INSTALL_CMD}${NC}" +eval "$INSTALL_CMD" + +echo -e "${GREEN}✓ Husky and lint-staged installed${NC}" +echo + +# --- Step 6: Initialize Husky and configure pre-commit hook --- +echo -e "${BLUE}🪝 Step 6/7: Configuring Husky...${NC}" + +HUSKY_INIT=$(get_exec_cmd "husky init") +echo -e "${YELLOW}Running: ${HUSKY_INIT}${NC}" +eval "$HUSKY_INIT" + +# Create pre-commit hook +PRE_COMMIT_CMD=$(get_exec_cmd "lint-staged") +cat > .husky/pre-commit << EOF +${PRE_COMMIT_CMD} +EOF + +chmod +x .husky/pre-commit + +echo -e "${GREEN}✓ Husky configured${NC}" +echo + +# --- Step 7: Add lint-staged configuration to package.json --- +echo -e "${BLUE}⚙️ Step 7/7: Configuring lint-staged...${NC}" + +# Check if lint-staged config already exists (look for the config object, not just dependency name) +if grep -q '"lint-staged"[[:space:]]*:[[:space:]]*{' package.json; then + echo -e "${YELLOW}⚠️ lint-staged configuration already exists in package.json, skipping...${NC}" +else + # Use node to add lint-staged config to package.json + node --eval " + const fs = require('fs'); + const pkgPath = 'package.json'; + const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); + pkg['lint-staged'] = { + '**/*': 'prettier --write --ignore-unknown' + }; + fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n'); + " + echo -e "${GREEN}✓ lint-staged configured in package.json${NC}" +fi +echo + +# --- Step 8: Create GitHub Actions workflow (optional) --- +echo -e "${BLUE}🔄 Creating GitHub Actions workflow...${NC}" + +WORKFLOW_DIR=".github/workflows" +WORKFLOW_FILE="${WORKFLOW_DIR}/format-check.yml" + +if [ -f "$WORKFLOW_FILE" ]; then + echo -e "${YELLOW}⚠️ GitHub Actions workflow already exists, skipping...${NC}" +else + mkdir -p "$WORKFLOW_DIR" + + # Determine the install command for CI + case "$PACKAGE_MANAGER" in + pnpm) + SETUP_STEP=' - uses: pnpm/action-setup@v4 + with: + version: 9' + INSTALL_CMD_CI='pnpm install --frozen-lockfile' + CHECK_CMD='pnpm exec prettier --check .' + ;; + npm) + SETUP_STEP='' + INSTALL_CMD_CI='npm ci' + CHECK_CMD='npm exec prettier -- --check .' + ;; + yarn) + SETUP_STEP='' + INSTALL_CMD_CI='yarn install --frozen-lockfile' + CHECK_CMD='yarn exec prettier --check .' + ;; + bun) + SETUP_STEP=' - uses: oven-sh/setup-bun@v2' + INSTALL_CMD_CI='bun install --frozen-lockfile' + CHECK_CMD='bunx prettier --check .' + ;; + esac + + cat > "$WORKFLOW_FILE" << EOF +name: Format Check +on: [push, pull_request] + +jobs: + check-format: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 +${SETUP_STEP} + - uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "${PACKAGE_MANAGER}" + - run: ${INSTALL_CMD_CI} + - run: ${CHECK_CMD} +EOF + echo -e "${GREEN}✓ GitHub Actions workflow created at ${WORKFLOW_FILE}${NC}" +fi +echo + +# --- Success --- +echo -e "${GREEN}" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "✨ Prettier setup complete!" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo -e "${NC}" +echo "What was configured:" +echo " ✓ Prettier installed with exact version pinning" +echo " ✓ .prettierrc configuration file" +echo " ✓ .prettierignore for excluding files" +echo " ✓ .editorconfig for editor consistency" +echo " ✓ Husky for Git hooks" +echo " ✓ lint-staged for pre-commit formatting" +echo " ✓ GitHub Actions workflow for CI checks" +echo +echo "Next steps:" +echo " 1. Customize .prettierrc with your preferences (optional)" +echo " 2. Run '$(get_exec_cmd "prettier --write .")' to format all files" +echo " 3. Commit your changes - formatting will run automatically!" +echo +echo -e "${YELLOW}Note: Your commits will now be automatically formatted.${NC}" +echo diff --git a/www/app/install/[script]/route.ts b/www/app/install/[script]/route.ts new file mode 100644 index 0000000..cfb9e27 --- /dev/null +++ b/www/app/install/[script]/route.ts @@ -0,0 +1,115 @@ +import { NextRequest, NextResponse } from 'next/server' +import { getInstallScript } from '@/lib/install-scripts' + +export async function GET( + request: NextRequest, + { params }: { params: Promise<{ script: string }> } +) { + const { script: scriptId } = await params + + // Extract version from URL if present (e.g., /install/prettier@v1.0.0) + let ref = 'main' + let actualScriptId = scriptId + + const match = scriptId.match(/^(.+)@(.+)$/) + if (match && match[1] && match[2]) { + actualScriptId = match[1] + ref = match[2] + } + + // Get script metadata + const scriptMeta = getInstallScript(actualScriptId) + if (!scriptMeta) { + return new NextResponse(`Install script not found: ${actualScriptId}`, { + status: 404, + headers: { + 'Content-Type': 'text/plain', + }, + }) + } + + // Extract package manager from query params + const packageManager = request.nextUrl.searchParams.get('pm') || 'pnpm' + + // Validate package manager + const validPackageManagers = ['pnpm', 'npm', 'yarn', 'bun'] + if (!validPackageManagers.includes(packageManager)) { + return new NextResponse( + `Invalid package manager: ${packageManager}. Supported: ${validPackageManagers.join(', ')}`, + { + status: 400, + headers: { + 'Content-Type': 'text/plain', + }, + } + ) + } + + // Construct the GitHub raw URL + const scriptUrl = `https://raw.githubusercontent.com/marcelocra/devmagic/${ref}/${scriptMeta.scriptPath}` + + try { + // Fetch the script from GitHub + const response = await fetch(scriptUrl) + + if (!response.ok) { + return new NextResponse( + `Installation script not found for version: ${ref}\nScript: ${scriptMeta.name}`, + { + status: 404, + headers: { + 'Content-Type': 'text/plain', + }, + } + ) + } + + let script = await response.text() + + // If the script supports package managers, inject the PACKAGE_MANAGER variable + if (scriptMeta.supportsPackageManagers && packageManager !== 'pnpm') { + // Add the package manager as an environment variable at the top of the script + // Find the first non-shebang, non-comment line + const lines = script.split('\n') + let insertIndex = 0 + + for (let i = 0; i < lines.length; i++) { + const line = lines[i].trim() + if (line && !line.startsWith('#')) { + insertIndex = i + break + } + if (i > 0 && !line.startsWith('#')) { + insertIndex = i + break + } + } + + // Insert the environment variable + lines.splice(insertIndex, 0, `export PACKAGE_MANAGER="${packageManager}"`) + script = lines.join('\n') + } + + // Serve the script as plain text + return new NextResponse(script, { + status: 200, + headers: { + 'Content-Type': 'text/plain; charset=utf-8', + 'Cache-Control': ref === 'main' ? 'public, max-age=300' : 'public, max-age=31536000', + 'X-DevMagic-Version': ref, + 'X-DevMagic-Script': actualScriptId, + 'X-DevMagic-PackageManager': packageManager, + }, + }) + } catch (error) { + return new NextResponse( + `Error fetching installation script: ${error instanceof Error ? error.message : 'Unknown error'}`, + { + status: 500, + headers: { + 'Content-Type': 'text/plain', + }, + } + ) + } +} diff --git a/www/app/install/route.ts b/www/app/install/route.ts index 04e7506..70a3b4f 100644 --- a/www/app/install/route.ts +++ b/www/app/install/route.ts @@ -1,6 +1,183 @@ import { NextRequest, NextResponse } from 'next/server' +import { loadInstallScripts } from '@/lib/install-scripts' export async function GET(request: NextRequest) { + // Check if this is being accessed from a browser (HTML request) + const acceptHeader = request.headers.get('accept') || '' + const userAgent = request.headers.get('user-agent') || '' + + // If the request accepts HTML and is from a browser, show the listing + const isBrowser = acceptHeader.includes('text/html') && !userAgent.includes('curl') && !userAgent.includes('wget') + + if (isBrowser) { + // Redirect to a listing page or serve HTML with the list of scripts + const scripts = loadInstallScripts() + + const html = ` + + + + + DevMagic Installation Scripts + + + +

🚀 DevMagic Installation Scripts

+ +
+

Main DevMagic Installation

+

Install DevMagic dev container to your project:

+
curl -fsSL https://devmagic.run/install | bash
+

Or view the script: https://devmagic.run/install (curl/wget only)

+
+ +

Additional Installation Scripts

+

Quick setup scripts for common development tools. Run with curl:

+ + ${scripts + .map( + (script) => ` +
+

${script.name}

+

${script.description}

+ + ${ + script.requirements && script.requirements.length > 0 + ? ` +
+ Requirements: + +
+ ` + : '' + } + + ${ + script.steps && script.steps.length > 0 + ? ` + What it does: +
    + ${script.steps.map((step) => `
  1. ${step}
  2. `).join('')} +
+ ` + : '' + } + + Install with pnpm (default): +
curl -fsSL https://devmagic.run/install/${script.id} | bash
+ + ${ + script.supportsPackageManagers + ? ` + Or with another package manager: +
# npm
+curl -fsSL https://devmagic.run/install/${script.id}?pm=npm | bash
+
+# yarn
+curl -fsSL https://devmagic.run/install/${script.id}?pm=yarn | bash
+
+# bun
+curl -fsSL https://devmagic.run/install/${script.id}?pm=bun | bash
+ ` + : '' + } + +

View script: https://devmagic.run/install/${script.id} (curl/wget only)

+
+ ` + ) + .join('')} + +

+ ← Back to DevMagic +

+ +` + + return new NextResponse(html, { + status: 200, + headers: { + 'Content-Type': 'text/html; charset=utf-8', + }, + }) + } + + // Otherwise, serve the main installation script // Extract version from URL path if present (e.g., /install@v0.1.0) const pathname = request.nextUrl.pathname let ref = 'main' diff --git a/www/data/install-scripts.yml b/www/data/install-scripts.yml new file mode 100644 index 0000000..4f0b73a --- /dev/null +++ b/www/data/install-scripts.yml @@ -0,0 +1,22 @@ +# DevMagic Install Scripts +# These scripts can be run with: curl -fsSL https://devmagic.run/install/{script} | bash +# Query parameters: +# - pm: package manager (pnpm, npm, yarn, bun) - defaults to pnpm + +scripts: + - id: prettier + name: Prettier + description: Install and configure Prettier with Husky and lint-staged for automatic code formatting + scriptPath: setup/install-prettier.sh + supportsPackageManagers: true + steps: + - Install Prettier with exact version + - Create .prettierrc and .prettierignore files + - Create .editorconfig file + - Install and configure Husky + - Install and configure lint-staged + - Add lint-staged configuration to package.json + - Create GitHub Actions workflow for format checking + requirements: + - Node.js project with package.json + - Git repository (for Husky) diff --git a/www/lib/install-scripts.ts b/www/lib/install-scripts.ts new file mode 100644 index 0000000..7d5dc38 --- /dev/null +++ b/www/lib/install-scripts.ts @@ -0,0 +1,40 @@ +import { readFileSync } from "fs"; +import { join } from "path"; +import yaml from "js-yaml"; + +export interface InstallScript { + id: string; + name: string; + description: string; + scriptPath: string; + supportsPackageManagers?: boolean; + steps: string[]; + requirements?: string[]; +} + +interface InstallScriptsData { + scripts: InstallScript[]; +} + +/** + * Load install scripts from www/data/install-scripts.yml + */ +export function loadInstallScripts(): InstallScript[] { + const installScriptsPath = join(process.cwd(), "data", "install-scripts.yml"); + + try { + const content = readFileSync(installScriptsPath, "utf-8"); + const data = yaml.load(content, { schema: yaml.FAILSAFE_SCHEMA }) as InstallScriptsData; + return data?.scripts ?? []; + } catch { + return []; + } +} + +/** + * Get a specific install script by ID + */ +export function getInstallScript(id: string): InstallScript | undefined { + const scripts = loadInstallScripts(); + return scripts.find((script) => script.id === id); +} diff --git a/www/pnpm-lock.yaml b/www/pnpm-lock.yaml index 5c239a1..73a8460 100644 --- a/www/pnpm-lock.yaml +++ b/www/pnpm-lock.yaml @@ -8,9 +8,15 @@ importers: .: dependencies: + js-yaml: + specifier: ^4.1.1 + version: 4.1.1 next: - specifier: 16.0.3 - version: 16.0.3(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + specifier: 16.0.7 + version: 16.0.7(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + next-intl: + specifier: ^4.5.6 + version: 4.6.1(next@16.0.7(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)(typescript@5.9.3) react: specifier: 19.2.0 version: 19.2.0 @@ -21,6 +27,9 @@ importers: '@tailwindcss/postcss': specifier: ^4 version: 4.1.17 + '@types/js-yaml': + specifier: ^4.0.9 + version: 4.0.9 '@types/node': specifier: ^20 version: 20.19.25 @@ -163,6 +172,24 @@ packages: resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@formatjs/ecma402-abstract@2.3.6': + resolution: {integrity: sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw==} + + '@formatjs/fast-memoize@2.2.7': + resolution: {integrity: sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==} + + '@formatjs/icu-messageformat-parser@2.11.4': + resolution: {integrity: sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw==} + + '@formatjs/icu-skeleton-parser@1.8.16': + resolution: {integrity: sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ==} + + '@formatjs/intl-localematcher@0.5.10': + resolution: {integrity: sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q==} + + '@formatjs/intl-localematcher@0.6.2': + resolution: {integrity: sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==} + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -335,56 +362,56 @@ packages: '@napi-rs/wasm-runtime@0.2.12': resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} - '@next/env@16.0.3': - resolution: {integrity: sha512-IqgtY5Vwsm14mm/nmQaRMmywCU+yyMIYfk3/MHZ2ZTJvwVbBn3usZnjMi1GacrMVzVcAxJShTCpZlPs26EdEjQ==} + '@next/env@16.0.7': + resolution: {integrity: sha512-gpaNgUh5nftFKRkRQGnVi5dpcYSKGcZZkQffZ172OrG/XkrnS7UBTQ648YY+8ME92cC4IojpI2LqTC8sTDhAaw==} '@next/eslint-plugin-next@16.0.3': resolution: {integrity: sha512-6sPWmZetzFWMsz7Dhuxsdmbu3fK+/AxKRtj7OB0/3OZAI2MHB/v2FeYh271LZ9abvnM1WIwWc/5umYjx0jo5sQ==} - '@next/swc-darwin-arm64@16.0.3': - resolution: {integrity: sha512-MOnbd92+OByu0p6QBAzq1ahVWzF6nyfiH07dQDez4/Nku7G249NjxDVyEfVhz8WkLiOEU+KFVnqtgcsfP2nLXg==} + '@next/swc-darwin-arm64@16.0.7': + resolution: {integrity: sha512-LlDtCYOEj/rfSnEn/Idi+j1QKHxY9BJFmxx7108A6D8K0SB+bNgfYQATPk/4LqOl4C0Wo3LACg2ie6s7xqMpJg==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@16.0.3': - resolution: {integrity: sha512-i70C4O1VmbTivYdRlk+5lj9xRc2BlK3oUikt3yJeHT1unL4LsNtN7UiOhVanFdc7vDAgZn1tV/9mQwMkWOJvHg==} + '@next/swc-darwin-x64@16.0.7': + resolution: {integrity: sha512-rtZ7BhnVvO1ICf3QzfW9H3aPz7GhBrnSIMZyr4Qy6boXF0b5E3QLs+cvJmg3PsTCG2M1PBoC+DANUi4wCOKXpA==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@16.0.3': - resolution: {integrity: sha512-O88gCZ95sScwD00mn/AtalyCoykhhlokxH/wi1huFK+rmiP5LAYVs/i2ruk7xST6SuXN4NI5y4Xf5vepb2jf6A==} + '@next/swc-linux-arm64-gnu@16.0.7': + resolution: {integrity: sha512-mloD5WcPIeIeeZqAIP5c2kdaTa6StwP4/2EGy1mUw8HiexSHGK/jcM7lFuS3u3i2zn+xH9+wXJs6njO7VrAqww==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@16.0.3': - resolution: {integrity: sha512-CEErFt78S/zYXzFIiv18iQCbRbLgBluS8z1TNDQoyPi8/Jr5qhR3e8XHAIxVxPBjDbEMITprqELVc5KTfFj0gg==} + '@next/swc-linux-arm64-musl@16.0.7': + resolution: {integrity: sha512-+ksWNrZrthisXuo9gd1XnjHRowCbMtl/YgMpbRvFeDEqEBd523YHPWpBuDjomod88U8Xliw5DHhekBC3EOOd9g==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@16.0.3': - resolution: {integrity: sha512-Tc3i+nwt6mQ+Dwzcri/WNDj56iWdycGVh5YwwklleClzPzz7UpfaMw1ci7bLl6GRYMXhWDBfe707EXNjKtiswQ==} + '@next/swc-linux-x64-gnu@16.0.7': + resolution: {integrity: sha512-4WtJU5cRDxpEE44Ana2Xro1284hnyVpBb62lIpU5k85D8xXxatT+rXxBgPkc7C1XwkZMWpK5rXLXTh9PFipWsA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@16.0.3': - resolution: {integrity: sha512-zTh03Z/5PBBPdTurgEtr6nY0vI9KR9Ifp/jZCcHlODzwVOEKcKRBtQIGrkc7izFgOMuXDEJBmirwpGqdM/ZixA==} + '@next/swc-linux-x64-musl@16.0.7': + resolution: {integrity: sha512-HYlhqIP6kBPXalW2dbMTSuB4+8fe+j9juyxwfMwCe9kQPPeiyFn7NMjNfoFOfJ2eXkeQsoUGXg+O2SE3m4Qg2w==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@16.0.3': - resolution: {integrity: sha512-Jc1EHxtZovcJcg5zU43X3tuqzl/sS+CmLgjRP28ZT4vk869Ncm2NoF8qSTaL99gh6uOzgM99Shct06pSO6kA6g==} + '@next/swc-win32-arm64-msvc@16.0.7': + resolution: {integrity: sha512-EviG+43iOoBRZg9deGauXExjRphhuYmIOJ12b9sAPy0eQ6iwcPxfED2asb/s2/yiLYOdm37kPaiZu8uXSYPs0Q==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@16.0.3': - resolution: {integrity: sha512-N7EJ6zbxgIYpI/sWNzpVKRMbfEGgsWuOIvzkML7wxAAZhPk1Msxuo/JDu1PKjWGrAoOLaZcIX5s+/pF5LIbBBg==} + '@next/swc-win32-x64-msvc@16.0.7': + resolution: {integrity: sha512-gniPjy55zp5Eg0896qSrf3yB1dw4F/3s8VK1ephdsZZ129j2n6e1WqCbE2YgcKhW9hPB9TVZENugquWJD5x0ug==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -405,12 +432,172 @@ packages: resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} engines: {node: '>=12.4.0'} + '@parcel/watcher-android-arm64@2.5.1': + resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.1': + resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.1': + resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.1': + resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.1': + resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm-musl@2.5.1': + resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-arm64-musl@2.5.1': + resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-x64-glibc@2.5.1': + resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-linux-x64-musl@2.5.1': + resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-win32-arm64@2.5.1': + resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.5.1': + resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.5.1': + resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.5.1': + resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} + engines: {node: '>= 10.0.0'} + '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + '@schummar/icu-type-parser@1.21.5': + resolution: {integrity: sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==} + + '@swc/core-darwin-arm64@1.15.7': + resolution: {integrity: sha512-+hNVUfezUid7LeSHqnhoC6Gh3BROABxjlDNInuZ/fie1RUxaEX4qzDwdTgozJELgHhvYxyPIg1ro8ibnKtgO4g==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + + '@swc/core-darwin-x64@1.15.7': + resolution: {integrity: sha512-ZAFuvtSYZTuXPcrhanaD5eyp27H8LlDzx2NAeVyH0FchYcuXf0h5/k3GL9ZU6Jw9eQ63R1E8KBgpXEJlgRwZUQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + + '@swc/core-linux-arm-gnueabihf@1.15.7': + resolution: {integrity: sha512-K3HTYocpqnOw8KcD8SBFxiDHjIma7G/X+bLdfWqf+qzETNBrzOub/IEkq9UaeupaJiZJkPptr/2EhEXXWryS/A==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + + '@swc/core-linux-arm64-gnu@1.15.7': + resolution: {integrity: sha512-HCnVIlsLnCtQ3uXcXgWrvQ6SAraskLA9QJo9ykTnqTH6TvUYqEta+TdTdGjzngD6TOE7XjlAiUs/RBtU8Z0t+Q==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-arm64-musl@1.15.7': + resolution: {integrity: sha512-/OOp9UZBg4v2q9+x/U21Jtld0Wb8ghzBScwhscI7YvoSh4E8RALaJ1msV8V8AKkBkZH7FUAFB7Vbv0oVzZsezA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-x64-gnu@1.15.7': + resolution: {integrity: sha512-VBbs4gtD4XQxrHuQ2/2+TDZpPQQgrOHYRnS6SyJW+dw0Nj/OomRqH+n5Z4e/TgKRRbieufipeIGvADYC/90PYQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-linux-x64-musl@1.15.7': + resolution: {integrity: sha512-kVuy2unodso6p0rMauS2zby8/bhzoGRYxBDyD6i2tls/fEYAE74oP0VPFzxIyHaIjK1SN6u5TgvV9MpyJ5xVug==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-win32-arm64-msvc@1.15.7': + resolution: {integrity: sha512-uddYoo5Xmo1XKLhAnh4NBIyy5d0xk33x1sX3nIJboFySLNz878ksCFCZ3IBqrt1Za0gaoIWoOSSSk0eNhAc/sw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + + '@swc/core-win32-ia32-msvc@1.15.7': + resolution: {integrity: sha512-rqq8JjNMLx3QNlh0aPTtN/4+BGLEHC94rj9mkH1stoNRf3ra6IksNHMHy+V1HUqElEgcZyx+0yeXx3eLOTcoFw==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + + '@swc/core-win32-x64-msvc@1.15.7': + resolution: {integrity: sha512-4BK06EGdPnuplgcNhmSbOIiLdRgHYX3v1nl4HXo5uo4GZMfllXaCyBUes+0ePRfwbn9OFgVhCWPcYYjMT6hycQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + + '@swc/core@1.15.7': + resolution: {integrity: sha512-kTGB8XI7P+pTKW83tnUEDVP4zduF951u3UAOn5eTi0vyW6MvL56A3+ggMdfuVFtDI0/DsbSzf5z34HVBbuScWw==} + engines: {node: '>=10'} + peerDependencies: + '@swc/helpers': '>=0.5.17' + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + '@swc/types@0.1.25': + resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==} + '@tailwindcss/node@4.1.17': resolution: {integrity: sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==} @@ -505,6 +692,9 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/js-yaml@4.0.9': + resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -851,6 +1041,9 @@ packages: supports-color: optional: true + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -862,6 +1055,11 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -1208,6 +1406,9 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} + intl-messageformat@10.7.18: + resolution: {integrity: sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g==} + is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} @@ -1498,9 +1699,27 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - next@16.0.3: - resolution: {integrity: sha512-Ka0/iNBblPFcIubTA1Jjh6gvwqfjrGq1Y2MTI5lbjeLIAfmC+p5bQmojpRZqgHHVu5cG4+qdIiwXiBSm/8lZ3w==} + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + next-intl-swc-plugin-extractor@4.6.1: + resolution: {integrity: sha512-+HHNeVERfSvuPDF7LYVn3pxst5Rf7EYdUTw7C7WIrYhcLaKiZ1b9oSRkTQddAN3mifDMCfHqO4kAQ/pcKiBl3A==} + + next-intl@4.6.1: + resolution: {integrity: sha512-KlWgWtKLBPUsTPgxqwyjws1wCMD2QKxLlVjeeGj53DC1JWfKmBShKOrhIP0NznZrRQ0GleeoDUeHSETmyyIFeA==} + peerDependencies: + next: ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0 + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + next@16.0.7: + resolution: {integrity: sha512-3mBRJyPxT4LOxAJI6IsXeFtKfiJUbjCLgvXO02fV8Wy/lIhPvP94Fe7dGhUgHXcQy4sSuYwQNcOLhIfOm0rL0A==} engines: {node: '>=20.9.0'} + deprecated: This version has a security vulnerability. Please upgrade to a patched version. See https://nextjs.org/blog/security-update-2025-12-11 for more details. hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 @@ -1519,6 +1738,9 @@ packages: sass: optional: true + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} @@ -1596,6 +1818,9 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + po-parser@2.0.0: + resolution: {integrity: sha512-SZvoKi3PoI/hHa2V9je9CW7Xgxl4dvO74cvaa6tWShIHT51FkPxje6pt0gTJznJrU67ix91nDaQp2hUxkOYhKA==} + possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -1870,6 +2095,11 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + use-intl@4.6.1: + resolution: {integrity: sha512-mUIj6QvJZ7Rk33mLDxRziz1YiBBAnIji8YW4TXXMdYHtaPEbVucrXD3iKQGAqJhbVn0VnjrEtIKYO1B18mfSJw==} + peerDependencies: + react: ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0 + which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -2077,6 +2307,36 @@ snapshots: '@eslint/core': 0.17.0 levn: 0.4.1 + '@formatjs/ecma402-abstract@2.3.6': + dependencies: + '@formatjs/fast-memoize': 2.2.7 + '@formatjs/intl-localematcher': 0.6.2 + decimal.js: 10.6.0 + tslib: 2.8.1 + + '@formatjs/fast-memoize@2.2.7': + dependencies: + tslib: 2.8.1 + + '@formatjs/icu-messageformat-parser@2.11.4': + dependencies: + '@formatjs/ecma402-abstract': 2.3.6 + '@formatjs/icu-skeleton-parser': 1.8.16 + tslib: 2.8.1 + + '@formatjs/icu-skeleton-parser@1.8.16': + dependencies: + '@formatjs/ecma402-abstract': 2.3.6 + tslib: 2.8.1 + + '@formatjs/intl-localematcher@0.5.10': + dependencies: + tslib: 2.8.1 + + '@formatjs/intl-localematcher@0.6.2': + dependencies: + tslib: 2.8.1 + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.7': @@ -2211,34 +2471,34 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true - '@next/env@16.0.3': {} + '@next/env@16.0.7': {} '@next/eslint-plugin-next@16.0.3': dependencies: fast-glob: 3.3.1 - '@next/swc-darwin-arm64@16.0.3': + '@next/swc-darwin-arm64@16.0.7': optional: true - '@next/swc-darwin-x64@16.0.3': + '@next/swc-darwin-x64@16.0.7': optional: true - '@next/swc-linux-arm64-gnu@16.0.3': + '@next/swc-linux-arm64-gnu@16.0.7': optional: true - '@next/swc-linux-arm64-musl@16.0.3': + '@next/swc-linux-arm64-musl@16.0.7': optional: true - '@next/swc-linux-x64-gnu@16.0.3': + '@next/swc-linux-x64-gnu@16.0.7': optional: true - '@next/swc-linux-x64-musl@16.0.3': + '@next/swc-linux-x64-musl@16.0.7': optional: true - '@next/swc-win32-arm64-msvc@16.0.3': + '@next/swc-win32-arm64-msvc@16.0.7': optional: true - '@next/swc-win32-x64-msvc@16.0.3': + '@next/swc-win32-x64-msvc@16.0.7': optional: true '@nodelib/fs.scandir@2.1.5': @@ -2255,12 +2515,126 @@ snapshots: '@nolyfill/is-core-module@1.0.39': {} + '@parcel/watcher-android-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-x64@2.5.1': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.1': + optional: true + + '@parcel/watcher-win32-arm64@2.5.1': + optional: true + + '@parcel/watcher-win32-ia32@2.5.1': + optional: true + + '@parcel/watcher-win32-x64@2.5.1': + optional: true + + '@parcel/watcher@2.5.1': + dependencies: + detect-libc: 1.0.3 + is-glob: 4.0.3 + micromatch: 4.0.8 + node-addon-api: 7.1.1 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.5.1 + '@parcel/watcher-darwin-arm64': 2.5.1 + '@parcel/watcher-darwin-x64': 2.5.1 + '@parcel/watcher-freebsd-x64': 2.5.1 + '@parcel/watcher-linux-arm-glibc': 2.5.1 + '@parcel/watcher-linux-arm-musl': 2.5.1 + '@parcel/watcher-linux-arm64-glibc': 2.5.1 + '@parcel/watcher-linux-arm64-musl': 2.5.1 + '@parcel/watcher-linux-x64-glibc': 2.5.1 + '@parcel/watcher-linux-x64-musl': 2.5.1 + '@parcel/watcher-win32-arm64': 2.5.1 + '@parcel/watcher-win32-ia32': 2.5.1 + '@parcel/watcher-win32-x64': 2.5.1 + '@rtsao/scc@1.1.0': {} + '@schummar/icu-type-parser@1.21.5': {} + + '@swc/core-darwin-arm64@1.15.7': + optional: true + + '@swc/core-darwin-x64@1.15.7': + optional: true + + '@swc/core-linux-arm-gnueabihf@1.15.7': + optional: true + + '@swc/core-linux-arm64-gnu@1.15.7': + optional: true + + '@swc/core-linux-arm64-musl@1.15.7': + optional: true + + '@swc/core-linux-x64-gnu@1.15.7': + optional: true + + '@swc/core-linux-x64-musl@1.15.7': + optional: true + + '@swc/core-win32-arm64-msvc@1.15.7': + optional: true + + '@swc/core-win32-ia32-msvc@1.15.7': + optional: true + + '@swc/core-win32-x64-msvc@1.15.7': + optional: true + + '@swc/core@1.15.7': + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.25 + optionalDependencies: + '@swc/core-darwin-arm64': 1.15.7 + '@swc/core-darwin-x64': 1.15.7 + '@swc/core-linux-arm-gnueabihf': 1.15.7 + '@swc/core-linux-arm64-gnu': 1.15.7 + '@swc/core-linux-arm64-musl': 1.15.7 + '@swc/core-linux-x64-gnu': 1.15.7 + '@swc/core-linux-x64-musl': 1.15.7 + '@swc/core-win32-arm64-msvc': 1.15.7 + '@swc/core-win32-ia32-msvc': 1.15.7 + '@swc/core-win32-x64-msvc': 1.15.7 + + '@swc/counter@0.1.3': {} + '@swc/helpers@0.5.15': dependencies: tslib: 2.8.1 + '@swc/types@0.1.25': + dependencies: + '@swc/counter': 0.1.3 + '@tailwindcss/node@4.1.17': dependencies: '@jridgewell/remapping': 2.3.5 @@ -2337,6 +2711,8 @@ snapshots: '@types/estree@1.0.8': {} + '@types/js-yaml@4.0.9': {} + '@types/json-schema@7.0.15': {} '@types/json5@0.0.29': {} @@ -2704,6 +3080,8 @@ snapshots: dependencies: ms: 2.1.3 + decimal.js@10.6.0: {} + deep-is@0.1.4: {} define-data-property@1.1.4: @@ -2718,6 +3096,8 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + detect-libc@1.0.3: {} + detect-libc@2.1.2: {} doctrine@2.1.0: @@ -3214,6 +3594,13 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 + intl-messageformat@10.7.18: + dependencies: + '@formatjs/ecma402-abstract': 2.3.6 + '@formatjs/fast-memoize': 2.2.7 + '@formatjs/icu-messageformat-parser': 2.11.4 + tslib: 2.8.1 + is-array-buffer@3.0.5: dependencies: call-bind: 1.0.8 @@ -3477,9 +3864,29 @@ snapshots: natural-compare@1.4.0: {} - next@16.0.3(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + negotiator@1.0.0: {} + + next-intl-swc-plugin-extractor@4.6.1: {} + + next-intl@4.6.1(next@16.0.7(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)(typescript@5.9.3): + dependencies: + '@formatjs/intl-localematcher': 0.5.10 + '@parcel/watcher': 2.5.1 + '@swc/core': 1.15.7 + negotiator: 1.0.0 + next: 16.0.7(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + next-intl-swc-plugin-extractor: 4.6.1 + po-parser: 2.0.0 + react: 19.2.0 + use-intl: 4.6.1(react@19.2.0) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@swc/helpers' + + next@16.0.7(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: - '@next/env': 16.0.3 + '@next/env': 16.0.7 '@swc/helpers': 0.5.15 caniuse-lite: 1.0.30001756 postcss: 8.4.31 @@ -3487,19 +3894,21 @@ snapshots: react-dom: 19.2.0(react@19.2.0) styled-jsx: 5.1.6(@babel/core@7.28.5)(react@19.2.0) optionalDependencies: - '@next/swc-darwin-arm64': 16.0.3 - '@next/swc-darwin-x64': 16.0.3 - '@next/swc-linux-arm64-gnu': 16.0.3 - '@next/swc-linux-arm64-musl': 16.0.3 - '@next/swc-linux-x64-gnu': 16.0.3 - '@next/swc-linux-x64-musl': 16.0.3 - '@next/swc-win32-arm64-msvc': 16.0.3 - '@next/swc-win32-x64-msvc': 16.0.3 + '@next/swc-darwin-arm64': 16.0.7 + '@next/swc-darwin-x64': 16.0.7 + '@next/swc-linux-arm64-gnu': 16.0.7 + '@next/swc-linux-arm64-musl': 16.0.7 + '@next/swc-linux-x64-gnu': 16.0.7 + '@next/swc-linux-x64-musl': 16.0.7 + '@next/swc-win32-arm64-msvc': 16.0.7 + '@next/swc-win32-x64-msvc': 16.0.7 sharp: 0.34.5 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros + node-addon-api@7.1.1: {} + node-releases@2.0.27: {} object-assign@4.1.1: {} @@ -3583,6 +3992,8 @@ snapshots: picomatch@4.0.3: {} + po-parser@2.0.0: {} + possible-typed-array-names@1.1.0: {} postcss@8.4.31: @@ -3968,6 +4379,13 @@ snapshots: dependencies: punycode: 2.3.1 + use-intl@4.6.1(react@19.2.0): + dependencies: + '@formatjs/fast-memoize': 2.2.7 + '@schummar/icu-type-parser': 1.21.5 + intl-messageformat: 10.7.18 + react: 19.2.0 + which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 From 52db49fbf7ff335b7e2b1ad9604f3d1caedeb090 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Dec 2025 01:31:13 +0000 Subject: [PATCH 3/4] refactor: improve script readability and remove hardcoded pnpm version - Format Node.js inline script for better readability - Remove hardcoded pnpm version from GitHub Actions workflow - pnpm/action-setup@v4 will now use version from package.json Co-authored-by: marcelocra <2532492+marcelocra@users.noreply.github.com> --- setup/install-prettier.sh | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/setup/install-prettier.sh b/setup/install-prettier.sh index eb737ae..d9eaa7e 100644 --- a/setup/install-prettier.sh +++ b/setup/install-prettier.sh @@ -273,14 +273,14 @@ if grep -q '"lint-staged"[[:space:]]*:[[:space:]]*{' package.json; then else # Use node to add lint-staged config to package.json node --eval " - const fs = require('fs'); - const pkgPath = 'package.json'; - const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); - pkg['lint-staged'] = { - '**/*': 'prettier --write --ignore-unknown' - }; - fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n'); - " +const fs = require('fs'); +const pkgPath = 'package.json'; +const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); +pkg['lint-staged'] = { + '**/*': 'prettier --write --ignore-unknown' +}; +fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n'); +" echo -e "${GREEN}✓ lint-staged configured in package.json${NC}" fi echo @@ -299,9 +299,7 @@ else # Determine the install command for CI case "$PACKAGE_MANAGER" in pnpm) - SETUP_STEP=' - uses: pnpm/action-setup@v4 - with: - version: 9' + SETUP_STEP=' - uses: pnpm/action-setup@v4' INSTALL_CMD_CI='pnpm install --frozen-lockfile' CHECK_CMD='pnpm exec prettier --check .' ;; From 00a6618e14c5a5ab792f8c38d152895f5862a007 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Dec 2025 16:15:09 +0000 Subject: [PATCH 4/4] refactor: use Next.js page.tsx instead of inline HTML and improve bash script idioms - Move install scripts listing from inline HTML to proper page.tsx at /install-scripts - Restore /install/route.ts to original functionality (serve devmagic.sh only) - Rewrite install-prettier.sh with proper bash idioms: - Use [[ ]] instead of [ ] for all conditionals - Add set -euo pipefail for strict error handling - Organize code with main() function and clear pipeline structure - Extract all logic into named functions for better readability - Use readonly for constants Co-authored-by: marcelocra <2532492+marcelocra@users.noreply.github.com> --- setup/install-prettier.sh | 440 ++++++++++++++++--------------- www/app/install-scripts/page.tsx | 124 +++++++++ www/app/install/route.ts | 177 ------------- 3 files changed, 358 insertions(+), 383 deletions(-) create mode 100644 www/app/install-scripts/page.tsx diff --git a/setup/install-prettier.sh b/setup/install-prettier.sh index d9eaa7e..430def9 100644 --- a/setup/install-prettier.sh +++ b/setup/install-prettier.sh @@ -5,81 +5,76 @@ # Latest version: curl -fsSL https://devmagic.run/install/prettier | bash # With npm: curl -fsSL https://devmagic.run/install/prettier?pm=npm | bash -set -e +set -euo pipefail # --- Configuration --- -PACKAGE_MANAGER="${PACKAGE_MANAGER:-pnpm}" # Default to pnpm +PACKAGE_MANAGER="${PACKAGE_MANAGER:-pnpm}" # --- Colors for output --- -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -PURPLE='\033[0;35m' -NC='\033[0m' # No Color +readonly RED='\033[0;31m' +readonly GREEN='\033[0;32m' +readonly YELLOW='\033[1;33m' +readonly BLUE='\033[0;34m' +readonly PURPLE='\033[0;35m' +readonly NC='\033[0m' -# --- Header --- -echo -e "${PURPLE}" -echo "✨ Prettier Installation" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "Setting up automatic code formatting" -echo -e "Package Manager: ${PACKAGE_MANAGER}${NC}" -echo - -# --- Prerequisite Checks --- - -# Check if package.json exists -if [ ! -f "package.json" ]; then - echo -e "${RED}❌ package.json not found. This script must be run in a Node.js project.${NC}" - exit 1 -fi - -# Check if Git repository exists -if ! git rev-parse --is-inside-work-tree &> /dev/null; then - echo -e "${RED}❌ Not a Git repository. Husky requires Git to function.${NC}" - exit 1 -fi +# --- Helper Functions --- -# Verify package manager is installed -if ! command -v "$PACKAGE_MANAGER" &> /dev/null; then - echo -e "${RED}❌ ${PACKAGE_MANAGER} is not installed. Please install it first.${NC}" - exit 1 -fi +show_header() { + echo -e "${PURPLE}" + echo "✨ Prettier Installation" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "Setting up automatic code formatting" + echo -e "Package Manager: ${PACKAGE_MANAGER}${NC}" + echo +} -# Validate package manager -case "$PACKAGE_MANAGER" in - pnpm|npm|yarn|bun) - ;; - *) - echo -e "${RED}❌ Unsupported package manager: ${PACKAGE_MANAGER}${NC}" - echo -e "${YELLOW} Supported: pnpm, npm, yarn, bun${NC}" +check_prerequisites() { + echo -e "${BLUE}Checking prerequisites...${NC}" + + # Check if package.json exists + if [[ ! -f "package.json" ]]; then + echo -e "${RED}❌ package.json not found. This script must be run in a Node.js project.${NC}" exit 1 - ;; -esac - -echo -e "${BLUE}✓ Prerequisites check passed${NC}" -echo - -# --- Helper Functions --- + fi + + # Check if Git repository exists + if ! git rev-parse --is-inside-work-tree &>/dev/null; then + echo -e "${RED}❌ Not a Git repository. Husky requires Git to function.${NC}" + exit 1 + fi + + # Verify package manager is installed + if ! command -v "$PACKAGE_MANAGER" &>/dev/null; then + echo -e "${RED}❌ ${PACKAGE_MANAGER} is not installed. Please install it first.${NC}" + exit 1 + fi + + # Validate package manager + case "$PACKAGE_MANAGER" in + pnpm|npm|yarn|bun) + ;; + *) + echo -e "${RED}❌ Unsupported package manager: ${PACKAGE_MANAGER}${NC}" + echo -e "${YELLOW} Supported: pnpm, npm, yarn, bun${NC}" + exit 1 + ;; + esac + + echo -e "${GREEN}✓ Prerequisites check passed${NC}" + echo +} -# Detect if we're in a workspace (monorepo) is_workspace() { - if [ "$PACKAGE_MANAGER" = "pnpm" ]; then - # Check for pnpm-workspace.yaml - [ -f "pnpm-workspace.yaml" ] - elif [ "$PACKAGE_MANAGER" = "npm" ]; then - # Check for workspaces in package.json - grep -q '"workspaces"' package.json 2>/dev/null - elif [ "$PACKAGE_MANAGER" = "yarn" ]; then - # Check for workspaces in package.json + if [[ "$PACKAGE_MANAGER" == "pnpm" ]]; then + [[ -f "pnpm-workspace.yaml" ]] + elif [[ "$PACKAGE_MANAGER" == "npm" || "$PACKAGE_MANAGER" == "yarn" ]]; then grep -q '"workspaces"' package.json 2>/dev/null else - # bun doesn't have strong workspace support yet - false + return 1 fi } -# Get the appropriate install command get_install_cmd() { local packages="$1" local dev_flag="$2" @@ -104,7 +99,6 @@ get_install_cmd() { esac } -# Get the appropriate dev flag get_dev_flag() { case "$PACKAGE_MANAGER" in pnpm|npm) @@ -116,7 +110,6 @@ get_dev_flag() { esac } -# Get the appropriate exact flag for prettier get_exact_flag() { case "$PACKAGE_MANAGER" in pnpm|npm) @@ -126,12 +119,11 @@ get_exact_flag() { echo "--exact" ;; bun) - echo "" # bun doesn't have an exact flag, uses exact by default + echo "" ;; esac } -# Get the exec command get_exec_cmd() { local cmd="$1" @@ -145,39 +137,44 @@ get_exec_cmd() { esac } -# --- Step 1: Install Prettier --- -echo -e "${BLUE}📦 Step 1/7: Installing Prettier...${NC}" - -DEV_FLAG=$(get_dev_flag) -EXACT_FLAG=$(get_exact_flag) -INSTALL_CMD=$(get_install_cmd "prettier" "${DEV_FLAG} ${EXACT_FLAG}") - -echo -e "${YELLOW}Running: ${INSTALL_CMD}${NC}" -eval "$INSTALL_CMD" - -echo -e "${GREEN}✓ Prettier installed${NC}" -echo - -# --- Step 2: Create .prettierrc --- -echo -e "${BLUE}📝 Step 2/7: Creating .prettierrc...${NC}" +install_prettier() { + echo -e "${BLUE}📦 Step 1/7: Installing Prettier...${NC}" + + local dev_flag + dev_flag=$(get_dev_flag) + local exact_flag + exact_flag=$(get_exact_flag) + local install_cmd + install_cmd=$(get_install_cmd "prettier" "${dev_flag} ${exact_flag}") + + echo -e "${YELLOW}Running: ${install_cmd}${NC}" + eval "$install_cmd" + + echo -e "${GREEN}✓ Prettier installed${NC}" + echo +} -if [ -f ".prettierrc" ]; then - echo -e "${YELLOW}⚠️ .prettierrc already exists, skipping...${NC}" -else - cat > .prettierrc << 'EOF' +create_prettierrc() { + echo -e "${BLUE}📝 Step 2/7: Creating .prettierrc...${NC}" + + if [[ -f ".prettierrc" ]]; then + echo -e "${YELLOW}⚠️ .prettierrc already exists, skipping...${NC}" + else + cat > .prettierrc << 'EOF' {} EOF - echo -e "${GREEN}✓ .prettierrc created${NC}" -fi -echo - -# --- Step 3: Create .prettierignore --- -echo -e "${BLUE}📝 Step 3/7: Creating .prettierignore...${NC}" + echo -e "${GREEN}✓ .prettierrc created${NC}" + fi + echo +} -if [ -f ".prettierignore" ]; then - echo -e "${YELLOW}⚠️ .prettierignore already exists, skipping...${NC}" -else - cat > .prettierignore << 'EOF' +create_prettierignore() { + echo -e "${BLUE}📝 Step 3/7: Creating .prettierignore...${NC}" + + if [[ -f ".prettierignore" ]]; then + echo -e "${YELLOW}⚠️ .prettierignore already exists, skipping...${NC}" + else + cat > .prettierignore << 'EOF' # Ignore artifacts: build coverage @@ -190,17 +187,18 @@ yarn.lock package-lock.json bun.lockb EOF - echo -e "${GREEN}✓ .prettierignore created${NC}" -fi -echo - -# --- Step 4: Create .editorconfig --- -echo -e "${BLUE}📝 Step 4/7: Creating .editorconfig...${NC}" + echo -e "${GREEN}✓ .prettierignore created${NC}" + fi + echo +} -if [ -f ".editorconfig" ]; then - echo -e "${YELLOW}⚠️ .editorconfig already exists, skipping...${NC}" -else - cat > .editorconfig << 'EOF' +create_editorconfig() { + echo -e "${BLUE}📝 Step 4/7: Creating .editorconfig...${NC}" + + if [[ -f ".editorconfig" ]]; then + echo -e "${YELLOW}⚠️ .editorconfig already exists, skipping...${NC}" + else + cat > .editorconfig << 'EOF' root = true [*] @@ -231,48 +229,56 @@ indent_size = 4 max_line_length = unset # Not wrapping lines reduces diffs. trim_trailing_whitespace = false # Double trailing spaces can be used to break lines. EOF - echo -e "${GREEN}✓ .editorconfig created${NC}" -fi -echo - -# --- Step 5: Install Husky and lint-staged --- -echo -e "${BLUE}📦 Step 5/7: Installing Husky and lint-staged...${NC}" - -INSTALL_CMD=$(get_install_cmd "husky lint-staged" "${DEV_FLAG}") - -echo -e "${YELLOW}Running: ${INSTALL_CMD}${NC}" -eval "$INSTALL_CMD" - -echo -e "${GREEN}✓ Husky and lint-staged installed${NC}" -echo - -# --- Step 6: Initialize Husky and configure pre-commit hook --- -echo -e "${BLUE}🪝 Step 6/7: Configuring Husky...${NC}" + echo -e "${GREEN}✓ .editorconfig created${NC}" + fi + echo +} -HUSKY_INIT=$(get_exec_cmd "husky init") -echo -e "${YELLOW}Running: ${HUSKY_INIT}${NC}" -eval "$HUSKY_INIT" +install_husky_lintstaged() { + echo -e "${BLUE}📦 Step 5/7: Installing Husky and lint-staged...${NC}" + + local dev_flag + dev_flag=$(get_dev_flag) + local install_cmd + install_cmd=$(get_install_cmd "husky lint-staged" "${dev_flag}") + + echo -e "${YELLOW}Running: ${install_cmd}${NC}" + eval "$install_cmd" + + echo -e "${GREEN}✓ Husky and lint-staged installed${NC}" + echo +} -# Create pre-commit hook -PRE_COMMIT_CMD=$(get_exec_cmd "lint-staged") -cat > .husky/pre-commit << EOF -${PRE_COMMIT_CMD} +configure_husky() { + echo -e "${BLUE}🪝 Step 6/7: Configuring Husky...${NC}" + + local husky_init + husky_init=$(get_exec_cmd "husky init") + echo -e "${YELLOW}Running: ${husky_init}${NC}" + eval "$husky_init" + + # Create pre-commit hook + local pre_commit_cmd + pre_commit_cmd=$(get_exec_cmd "lint-staged") + cat > .husky/pre-commit << EOF +${pre_commit_cmd} EOF + + chmod +x .husky/pre-commit + + echo -e "${GREEN}✓ Husky configured${NC}" + echo +} -chmod +x .husky/pre-commit - -echo -e "${GREEN}✓ Husky configured${NC}" -echo - -# --- Step 7: Add lint-staged configuration to package.json --- -echo -e "${BLUE}⚙️ Step 7/7: Configuring lint-staged...${NC}" - -# Check if lint-staged config already exists (look for the config object, not just dependency name) -if grep -q '"lint-staged"[[:space:]]*:[[:space:]]*{' package.json; then - echo -e "${YELLOW}⚠️ lint-staged configuration already exists in package.json, skipping...${NC}" -else - # Use node to add lint-staged config to package.json - node --eval " +configure_lintstaged() { + echo -e "${BLUE}⚙️ Step 7/7: Configuring lint-staged...${NC}" + + # Check if lint-staged config already exists (look for the config object, not just dependency name) + if grep -q '"lint-staged"[[:space:]]*:[[:space:]]*{' package.json; then + echo -e "${YELLOW}⚠️ lint-staged configuration already exists in package.json, skipping...${NC}" + else + # Use node to add lint-staged config to package.json + node --eval " const fs = require('fs'); const pkgPath = 'package.json'; const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); @@ -281,46 +287,48 @@ pkg['lint-staged'] = { }; fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n'); " - echo -e "${GREEN}✓ lint-staged configured in package.json${NC}" -fi -echo - -# --- Step 8: Create GitHub Actions workflow (optional) --- -echo -e "${BLUE}🔄 Creating GitHub Actions workflow...${NC}" - -WORKFLOW_DIR=".github/workflows" -WORKFLOW_FILE="${WORKFLOW_DIR}/format-check.yml" + echo -e "${GREEN}✓ lint-staged configured in package.json${NC}" + fi + echo +} -if [ -f "$WORKFLOW_FILE" ]; then - echo -e "${YELLOW}⚠️ GitHub Actions workflow already exists, skipping...${NC}" -else - mkdir -p "$WORKFLOW_DIR" +create_github_workflow() { + echo -e "${BLUE}🔄 Creating GitHub Actions workflow...${NC}" - # Determine the install command for CI - case "$PACKAGE_MANAGER" in - pnpm) - SETUP_STEP=' - uses: pnpm/action-setup@v4' - INSTALL_CMD_CI='pnpm install --frozen-lockfile' - CHECK_CMD='pnpm exec prettier --check .' - ;; - npm) - SETUP_STEP='' - INSTALL_CMD_CI='npm ci' - CHECK_CMD='npm exec prettier -- --check .' - ;; - yarn) - SETUP_STEP='' - INSTALL_CMD_CI='yarn install --frozen-lockfile' - CHECK_CMD='yarn exec prettier --check .' - ;; - bun) - SETUP_STEP=' - uses: oven-sh/setup-bun@v2' - INSTALL_CMD_CI='bun install --frozen-lockfile' - CHECK_CMD='bunx prettier --check .' - ;; - esac + local workflow_dir=".github/workflows" + local workflow_file="${workflow_dir}/format-check.yml" - cat > "$WORKFLOW_FILE" << EOF + if [[ -f "$workflow_file" ]]; then + echo -e "${YELLOW}⚠️ GitHub Actions workflow already exists, skipping...${NC}" + else + mkdir -p "$workflow_dir" + + # Determine the install command for CI + local setup_step install_cmd_ci check_cmd + case "$PACKAGE_MANAGER" in + pnpm) + setup_step=' - uses: pnpm/action-setup@v4' + install_cmd_ci='pnpm install --frozen-lockfile' + check_cmd='pnpm exec prettier --check .' + ;; + npm) + setup_step='' + install_cmd_ci='npm ci' + check_cmd='npm exec prettier -- --check .' + ;; + yarn) + setup_step='' + install_cmd_ci='yarn install --frozen-lockfile' + check_cmd='yarn exec prettier --check .' + ;; + bun) + setup_step=' - uses: oven-sh/setup-bun@v2' + install_cmd_ci='bun install --frozen-lockfile' + check_cmd='bunx prettier --check .' + ;; + esac + + cat > "$workflow_file" << EOF name: Format Check on: [push, pull_request] @@ -329,37 +337,57 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 -${SETUP_STEP} +${setup_step} - uses: actions/setup-node@v4 with: node-version: "20" cache: "${PACKAGE_MANAGER}" - - run: ${INSTALL_CMD_CI} - - run: ${CHECK_CMD} + - run: ${install_cmd_ci} + - run: ${check_cmd} EOF - echo -e "${GREEN}✓ GitHub Actions workflow created at ${WORKFLOW_FILE}${NC}" -fi -echo + echo -e "${GREEN}✓ GitHub Actions workflow created at ${workflow_file}${NC}" + fi + echo +} + +show_completion() { + echo -e "${GREEN}" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "✨ Prettier setup complete!" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo -e "${NC}" + echo "What was configured:" + echo " ✓ Prettier installed with exact version pinning" + echo " ✓ .prettierrc configuration file" + echo " ✓ .prettierignore for excluding files" + echo " ✓ .editorconfig for editor consistency" + echo " ✓ Husky for Git hooks" + echo " ✓ lint-staged for pre-commit formatting" + echo " ✓ GitHub Actions workflow for CI checks" + echo + echo "Next steps:" + echo " 1. Customize .prettierrc with your preferences (optional)" + echo " 2. Run '$(get_exec_cmd "prettier --write .")' to format all files" + echo " 3. Commit your changes - formatting will run automatically!" + echo + echo -e "${YELLOW}Note: Your commits will now be automatically formatted.${NC}" + echo +} + +# --- Main Pipeline --- +main() { + show_header + check_prerequisites + install_prettier + create_prettierrc + create_prettierignore + create_editorconfig + install_husky_lintstaged + configure_husky + configure_lintstaged + create_github_workflow + show_completion +} -# --- Success --- -echo -e "${GREEN}" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo "✨ Prettier setup complete!" -echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -echo -e "${NC}" -echo "What was configured:" -echo " ✓ Prettier installed with exact version pinning" -echo " ✓ .prettierrc configuration file" -echo " ✓ .prettierignore for excluding files" -echo " ✓ .editorconfig for editor consistency" -echo " ✓ Husky for Git hooks" -echo " ✓ lint-staged for pre-commit formatting" -echo " ✓ GitHub Actions workflow for CI checks" -echo -echo "Next steps:" -echo " 1. Customize .prettierrc with your preferences (optional)" -echo " 2. Run '$(get_exec_cmd "prettier --write .")' to format all files" -echo " 3. Commit your changes - formatting will run automatically!" -echo -echo -e "${YELLOW}Note: Your commits will now be automatically formatted.${NC}" -echo +# Run main function +main "$@" diff --git a/www/app/install-scripts/page.tsx b/www/app/install-scripts/page.tsx new file mode 100644 index 0000000..7c9a72c --- /dev/null +++ b/www/app/install-scripts/page.tsx @@ -0,0 +1,124 @@ +import { loadInstallScripts } from '@/lib/install-scripts' +import Link from 'next/link' + +export default function InstallScriptsPage() { + const scripts = loadInstallScripts() + + return ( +
+
+

🚀 DevMagic Installation Scripts

+ +
+

Main DevMagic Installation

+

+ Install DevMagic dev container to your project: +

+
+            curl -fsSL https://devmagic.run/install | bash
+          
+

+ Or view the script:{' '} + + https://devmagic.run/install + + {' '}(curl/wget only) +

+
+ +

Additional Installation Scripts

+

+ Quick setup scripts for common development tools. Run with curl: +

+ +
+ {scripts.map((script) => ( +
+

{script.name}

+

{script.description}

+ + {script.requirements && script.requirements.length > 0 && ( +
+ Requirements: +
    + {script.requirements.map((req, idx) => ( +
  • {req}
  • + ))} +
+
+ )} + + {script.steps && script.steps.length > 0 && ( +
+ What it does: +
    + {script.steps.map((step, idx) => ( +
  1. {step}
  2. + ))} +
+
+ )} + +
+
+ Install with pnpm (default): +
+                    curl -fsSL https://devmagic.run/install/{script.id} | bash
+                  
+
+ + {script.supportsPackageManagers && ( +
+ Or with another package manager: +
+                      
+{`# npm
+curl -fsSL https://devmagic.run/install/${script.id}?pm=npm | bash
+
+# yarn
+curl -fsSL https://devmagic.run/install/${script.id}?pm=yarn | bash
+
+# bun
+curl -fsSL https://devmagic.run/install/${script.id}?pm=bun | bash`}
+                      
+                    
+
+ )} + +

+ View script:{' '} + + https://devmagic.run/install/{script.id} + + {' '}(curl/wget only) +

+
+
+ ))} + + {scripts.length === 0 && ( +
+

No installation scripts available yet.

+
+ )} +
+ +

+ + ← Back to DevMagic + +

+
+
+ ) +} diff --git a/www/app/install/route.ts b/www/app/install/route.ts index 70a3b4f..04e7506 100644 --- a/www/app/install/route.ts +++ b/www/app/install/route.ts @@ -1,183 +1,6 @@ import { NextRequest, NextResponse } from 'next/server' -import { loadInstallScripts } from '@/lib/install-scripts' export async function GET(request: NextRequest) { - // Check if this is being accessed from a browser (HTML request) - const acceptHeader = request.headers.get('accept') || '' - const userAgent = request.headers.get('user-agent') || '' - - // If the request accepts HTML and is from a browser, show the listing - const isBrowser = acceptHeader.includes('text/html') && !userAgent.includes('curl') && !userAgent.includes('wget') - - if (isBrowser) { - // Redirect to a listing page or serve HTML with the list of scripts - const scripts = loadInstallScripts() - - const html = ` - - - - - DevMagic Installation Scripts - - - -

🚀 DevMagic Installation Scripts

- -
-

Main DevMagic Installation

-

Install DevMagic dev container to your project:

-
curl -fsSL https://devmagic.run/install | bash
-

Or view the script: https://devmagic.run/install (curl/wget only)

-
- -

Additional Installation Scripts

-

Quick setup scripts for common development tools. Run with curl:

- - ${scripts - .map( - (script) => ` -
-

${script.name}

-

${script.description}

- - ${ - script.requirements && script.requirements.length > 0 - ? ` -
- Requirements: - -
- ` - : '' - } - - ${ - script.steps && script.steps.length > 0 - ? ` - What it does: -
    - ${script.steps.map((step) => `
  1. ${step}
  2. `).join('')} -
- ` - : '' - } - - Install with pnpm (default): -
curl -fsSL https://devmagic.run/install/${script.id} | bash
- - ${ - script.supportsPackageManagers - ? ` - Or with another package manager: -
# npm
-curl -fsSL https://devmagic.run/install/${script.id}?pm=npm | bash
-
-# yarn
-curl -fsSL https://devmagic.run/install/${script.id}?pm=yarn | bash
-
-# bun
-curl -fsSL https://devmagic.run/install/${script.id}?pm=bun | bash
- ` - : '' - } - -

View script: https://devmagic.run/install/${script.id} (curl/wget only)

-
- ` - ) - .join('')} - -

- ← Back to DevMagic -

- -` - - return new NextResponse(html, { - status: 200, - headers: { - 'Content-Type': 'text/html; charset=utf-8', - }, - }) - } - - // Otherwise, serve the main installation script // Extract version from URL path if present (e.g., /install@v0.1.0) const pathname = request.nextUrl.pathname let ref = 'main'