commit a92e2f7024b881f8025854af3227decd090ee560 Author: mifi Date: Thu Feb 12 17:31:08 2026 -0300 Initial commit diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..6ac4dee --- /dev/null +++ b/.dockerignore @@ -0,0 +1,9 @@ +# Avoid sending secrets or dev tooling into the build context +# conf/config.local.php is included (no secrets; reads from ENV) +node_modules +.git +.prettierrc +.prettierignore +*.md +.env +.env.* diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..78b1d97 --- /dev/null +++ b/.env.example @@ -0,0 +1,66 @@ +# PostfixAdmin environment variables. Copy to .env and set secrets; optional overrides below. +# For Portainer: use Stack → ENV (advanced) and paste KEY=VALUE lines, or mount .env. +# Required (no default; app will error if missing): +PFA_SETUP_PASSWORD= +PFA_DATABASE_PASSWORD= +PFA_ADMIN_SMTP_PASSWORD= + +# --- Database --- +PFA_DATABASE_HOST=mail.mifi.holdings +PFA_DATABASE_USER=postfixadmin +PFA_DATABASE_NAME=postfix +PFA_DATABASE_TYPE=mysqli + +# --- Encryption --- +PFA_ENCRYPT=php_crypt:BLOWFISH:13:{BLF-CRYPT} + +# --- Branding and URLs --- +PFA_SITE_URL=https://postmaster.mifi.holdings +PFA_SITE_NAME=mifi Ventures Email Service +PFA_WELCOME_TEXT=Welcome to mifi Ventures Email Service — help +PFA_SHOW_HEADER_TEXT=YES +PFA_HEADER_TEXT=mifi Ventures Email Service +PFA_FOOTER_TEXT=mifi Ventures Mail +PFA_FOOTER_LINK=https://mail.mifi.holdings + +# --- Language --- +PFA_DEFAULT_LANGUAGE=en +PFA_DEFAULT_CHARSET=UTF-8 + +# --- Features (YES/NO) --- +PFA_SPECIAL_ALIAS_CONTROL=NO +PFA_BACKUP=NO +PFA_FETCHMAIL=NO +PFA_SENDMAIL=NO +PFA_APP_PASSWORDS=YES + +# --- Quotas --- +PFA_QUOTA=YES +PFA_USED_QUOTAS=YES +PFA_MAILBOXES=100 +PFA_MAXQUOTA=10240 +PFA_DOMAIN_QUOTA_DEFAULT=102400 + +# --- SMTP for admin notifications --- +PFA_ADMIN_EMAIL=noreply@mifi.holdings +PFA_ADMIN_NAME=mifi Ventures Postmaster +PFA_SMTP_SERVER=mail.mifi.holdings +PFA_SMTP_PORT=587 +PFA_SMTP_TYPE=starttls +PFA_SMTP_AUTH=true + +# --- Self-service and security (true/false) --- +PFA_EDIT_MAILBOX=true +PFA_EDIT_ALIAS=true +PFA_FORGOTTEN_ADMIN_PASSWORD_RESET=true +PFA_FORGOTTEN_USER_PASSWORD_RESET=true +PFA_ADMIN_2FA=true + +# --- API --- +PFA_API_ENABLED=true +PFA_API_ALLOW_FROM=127.0.0.1,::1 + +# --- Session (seconds) --- +PFA_SESSION_TIMEOUT=1800 + +# Note: password_validation and default_aliases are defined in conf/config.local.php (PHP arrays). diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2cef73e --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.env +.env.* +!.env.example diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..f14cb74 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +node_modules +pnpm-lock.yaml +conf diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..85b0780 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,15 @@ +{ + "semi": false, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "none", + "overrides": [ + { + "files": "*.yml", + "options": { + "tabWidth": 4, + "proseWrap": "preserve" + } + } + ] +} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..486a7e7 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,8 @@ +# Registry image for mifi PostfixAdmin stack. +# config.local.php reads all sensitive/configurable values from ENV (PFA_*); no bind mount needed. +ARG PFA_VERSION=latest +FROM postfixadmin:${PFA_VERSION} + +COPY conf/config.local.php /var/www/html/config.local.php + +LABEL org.opencontainers.image.source="https://git.mifi.dev/mifi-holdings/mail-postfixadmin" diff --git a/conf/config.local.php b/conf/config.local.php new file mode 100644 index 0000000..104996c --- /dev/null +++ b/conf/config.local.php @@ -0,0 +1,109 @@ + 'password_too_short 5', + '/([a-zA-Z].*){3}/' => 'password_no_characters 3', + '/([0-9].*){1}/' => 'password_no_digits 2', + '/([!\".,*&^%$£)(_+=\-`\'#@~\[\]\\<>\/].*){1,}/' => 'password_no_special 1', +); + +// Features +$CONF['special_alias_control'] = pfa_env('PFA_SPECIAL_ALIAS_CONTROL', 'NO'); +$CONF['backup'] = pfa_env('PFA_BACKUP', 'NO'); +$CONF['fetchmail'] = pfa_env('PFA_FETCHMAIL', 'NO'); +$CONF['sendmail'] = pfa_env('PFA_SENDMAIL', 'NO'); +$CONF['app_passwords'] = pfa_env('PFA_APP_PASSWORDS', 'YES'); + +// Welcome and footer text (branding + docs) +$CONF['welcome_text'] = pfa_env('PFA_WELCOME_TEXT', 'Welcome to mifi Ventures Email Service — help'); +$CONF['show_header_text'] = pfa_env('PFA_SHOW_HEADER_TEXT', 'YES'); +$CONF['header_text'] = pfa_env('PFA_HEADER_TEXT', 'mifi Ventures Email Service'); +$CONF['footer_text'] = pfa_env('PFA_FOOTER_TEXT', 'mifi Ventures Mail'); +$CONF['footer_link'] = pfa_env('PFA_FOOTER_LINK', 'https://mail.mifi.holdings'); + +// Default aliases (array; edit in file or extend via config.inc.php) +$CONF['default_aliases'] = array( + 'alerts' => 'alerts@mifi.holdings', + 'abuse' => 'abuse@mifi.holdings', + 'hostmaster' => 'hostmaster@mifi.holdings', + 'postmaster' => 'postmaster@mifi.holdings', + 'webmaster' => 'webmaster@mifi.holdings' +); + +// Quota and mailbox limits +$CONF['quota'] = pfa_env('PFA_QUOTA', 'YES'); +$CONF['used_quotas'] = pfa_env('PFA_USED_QUOTAS', 'YES'); +$CONF['mailboxes'] = pfa_env('PFA_MAILBOXES', '100'); +$CONF['maxquota'] = pfa_env('PFA_MAXQUOTA', '10240'); +$CONF['domain_quota_default'] = pfa_env('PFA_DOMAIN_QUOTA_DEFAULT', '102400'); + +// SMTP settings for admin notifications +$CONF['admin_email'] = pfa_env('PFA_ADMIN_EMAIL', 'noreply@mifi.holdings'); +$CONF['admin_name'] = pfa_env('PFA_ADMIN_NAME', 'mifi Ventures Postmaster'); +$CONF['smtp_server'] = pfa_env('PFA_SMTP_SERVER', 'mail.mifi.holdings'); +$CONF['smtp_port'] = pfa_env('PFA_SMTP_PORT', '587'); +$CONF['smtp_type'] = pfa_env('PFA_SMTP_TYPE', 'starttls'); +$CONF['smtp_auth'] = pfa_env_bool('PFA_SMTP_AUTH', true); + +// Self-service and editing +$CONF['edit_mailbox'] = pfa_env_bool('PFA_EDIT_MAILBOX', true); +$CONF['edit_alias'] = pfa_env_bool('PFA_EDIT_ALIAS', true); +$CONF['forgotten_admin_password_reset'] = pfa_env_bool('PFA_FORGOTTEN_ADMIN_PASSWORD_RESET', true); +$CONF['forgotten_user_password_reset'] = pfa_env_bool('PFA_FORGOTTEN_USER_PASSWORD_RESET', true); +$CONF['admin_2fa'] = pfa_env_bool('PFA_ADMIN_2FA', true); + +// API (comma-separated list for api_allow_from) +$CONF['api_enabled'] = pfa_env_bool('PFA_API_ENABLED', true); +$CONF['api_allow_from'] = array_map('trim', explode(',', pfa_env('PFA_API_ALLOW_FROM', '127.0.0.1,::1'))); + +// Session timeout (seconds) +$CONF['session_timeout'] = (int) pfa_env('PFA_SESSION_TIMEOUT', '1800'); diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..c67756d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,26 @@ +services: + postfixadmin: + image: git.mifi.dev/mifi-holdings/mail-postfixadmin:latest + container_name: mifi-mail-postfixadmin + # ENV: set PFA_* in Portainer stack → ENV (advanced); paste from .env or .env.example + healthcheck: + test: curl --fail http://localhost || exit 1 + retries: 5 + start_period: 30s + restart: unless-stopped + networks: + - traefik + volumes: + - /var/run/mysqld/mysqld.sock:/var/run/mysqld/mysqld.sock:ro + labels: + - 'traefik.enable=true' + - 'traefik.docker.network=traefik' + - 'traefik.http.routers.postfixadmin.rule=Host(`postmaster.mifi.holdings`)' + - 'traefik.http.routers.postfixadmin.middlewares=gzip@file,security-medium@file' + - 'traefik.http.routers.postfixadmin.entrypoints=websecure' + - 'traefik.http.routers.postfixadmin.tls=true' + - 'traefik.http.routers.postfixadmin.tls.certresolver=letsencrypt' + +networks: + traefik: + external: true diff --git a/package.json b/package.json new file mode 100644 index 0000000..dc7e296 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "mail-postfixadmin", + "version": "1.0.0", + "private": true, + "packageManager": "pnpm@10.29.3+sha512.498e1fb4cca5aa06c1dcf2611e6fafc50972ffe7189998c409e90de74566444298ffe43e6cd2acdc775ba1aa7cc5e092a8b7054c811ba8c5770f84693d33d2dc", + "scripts": { + "format": "prettier --write .", + "format:check": "prettier --check .", + "lint": "yamllint .woodpecker/ci.yml .woodpecker/build.yml .woodpecker/deploy.yml docker-compose.yml", + "docker:build": "docker build --platform linux/amd64 -t git.mifi.dev/mifi-holdings/mail-postfixadmin:latest .", + "docker:push": "docker push git.mifi.dev/mifi-holdings/mail-postfixadmin:latest" + }, + "devDependencies": { + "prettier": "^3.4.2", + "yaml-lint": "^1.7.0" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..64b68ab --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,381 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + prettier: + specifier: ^3.4.2 + version: 3.8.1 + yaml-lint: + specifier: ^1.7.0 + version: 1.7.0 + +packages: + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + consola@2.15.3: + resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ini@2.0.0: + resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} + engines: {node: '>=10'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + nconf@0.12.1: + resolution: {integrity: sha512-p2cfF+B3XXacQdswUYWZ0w6Vld0832A/tuqjLBu3H1sfUcby4N2oVbGhyuCkZv+t3iY3aiFEj7gZGqax9Q2c1w==} + engines: {node: '>= 0.4.0'} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + prettier@3.8.1: + resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} + engines: {node: '>=14'} + hasBin: true + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + secure-keys@1.0.0: + resolution: {integrity: sha512-nZi59hW3Sl5P3+wOO89eHBAAGwmCPd2aE1+dLZV5MO+ItQctIvAqihzaAXIQhvtH4KJPxM080HsnqltR2y8cWg==} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yaml-lint@1.7.0: + resolution: {integrity: sha512-zeBC/kskKQo4zuoGQ+IYjw6C9a/YILr2SXoEZA9jM0COrSwvwVbfTiFegT8qYBSBgOwLMWGL8sY137tOmFXGnQ==} + hasBin: true + + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + + yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + +snapshots: + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + array-union@2.1.0: {} + + async@3.2.6: {} + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + cliui@7.0.4: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + consola@2.15.3: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + emoji-regex@8.0.0: {} + + escalade@3.2.0: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + get-caller-file@2.0.5: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + ignore@5.3.2: {} + + ini@2.0.0: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + nconf@0.12.1: + dependencies: + async: 3.2.6 + ini: 2.0.0 + secure-keys: 1.0.0 + yargs: 16.2.0 + + path-type@4.0.0: {} + + picomatch@2.3.1: {} + + prettier@3.8.1: {} + + queue-microtask@1.2.3: {} + + require-directory@2.1.1: {} + + reusify@1.1.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + secure-keys@1.0.0: {} + + slash@3.0.0: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + y18n@5.0.8: {} + + yaml-lint@1.7.0: + dependencies: + consola: 2.15.3 + globby: 11.1.0 + js-yaml: 4.1.1 + nconf: 0.12.1 + + yargs-parser@20.2.9: {} + + yargs@16.2.0: + dependencies: + cliui: 7.0.4 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9