Umami analytics stack
This commit is contained in:
8
.env.example
Normal file
8
.env.example
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Umami stack environment variables.
|
||||||
|
# Set real values in Portainer (Stack → Editor → Environment variables) or in a
|
||||||
|
# local .env file. Do not commit real secrets to the repo.
|
||||||
|
|
||||||
|
POSTGRES_DB=umami
|
||||||
|
POSTGRES_USER=umami
|
||||||
|
POSTGRES_PASSWORD=REPLACE_ME
|
||||||
|
APP_SECRET=REPLACE_ME
|
||||||
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Environment (never commit real secrets)
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Editor / IDE
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# Logs and temp
|
||||||
|
*.log
|
||||||
|
.tmp/
|
||||||
|
tmp/
|
||||||
2
.prettierignore
Normal file
2
.prettierignore
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
node_modules
|
||||||
|
pnpm-lock.yaml
|
||||||
2
.prettierrc.yaml
Normal file
2
.prettierrc.yaml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
tabWidth: 2
|
||||||
|
singleQuote: true
|
||||||
89
.woodpecker/ci.yaml
Normal file
89
.woodpecker/ci.yaml
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
# CI: format check (Prettier) and lint (YAML). Runs on every PR and every push to main.
|
||||||
|
when:
|
||||||
|
- event: pull_request
|
||||||
|
- branch: main
|
||||||
|
event: push
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: install
|
||||||
|
image: node:22-alpine
|
||||||
|
commands:
|
||||||
|
- corepack enable
|
||||||
|
- corepack prepare pnpm@10.29.2 --activate
|
||||||
|
- pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: format check
|
||||||
|
image: node:22-alpine
|
||||||
|
commands:
|
||||||
|
- corepack enable
|
||||||
|
- corepack prepare pnpm@10.29.2 --activate
|
||||||
|
- pnpm install --frozen-lockfile
|
||||||
|
- pnpm format:check
|
||||||
|
depends_on:
|
||||||
|
- install
|
||||||
|
|
||||||
|
- name: Send Prettier Status Notification (failure)
|
||||||
|
image: curlimages/curl
|
||||||
|
environment:
|
||||||
|
MATTERMOST_BOT_ACCESS_TOKEN:
|
||||||
|
from_secret: mattermost_bot_access_token
|
||||||
|
MATTERMOST_CHANNEL_ID:
|
||||||
|
from_secret: mattermost_tests_channel_id
|
||||||
|
MATTERMOST_POST_API_URL:
|
||||||
|
from_secret: mattermost_post_api_url
|
||||||
|
commands:
|
||||||
|
- |
|
||||||
|
BODY=$(printf '{"channel_id":"%s","message":"[%s - Build #%s] Prettier failure"}' "$MATTERMOST_CHANNEL_ID" "$CI_REPO" "$CI_PIPELINE_NUMBER")
|
||||||
|
curl -sS -X POST -H "Content-Type: application/json" -d "$BODY" -H "Authorization: Bearer $MATTERMOST_BOT_ACCESS_TOKEN" $MATTERMOST_POST_API_URL
|
||||||
|
depends_on:
|
||||||
|
- format check
|
||||||
|
when:
|
||||||
|
- status: [failure]
|
||||||
|
|
||||||
|
- name: lint
|
||||||
|
image: node:22-alpine
|
||||||
|
commands:
|
||||||
|
- corepack enable
|
||||||
|
- corepack prepare pnpm@10.29.2 --activate
|
||||||
|
- pnpm install --frozen-lockfile
|
||||||
|
- pnpm lint
|
||||||
|
depends_on:
|
||||||
|
- format check
|
||||||
|
|
||||||
|
- name: Send Lint Status Notification (failure)
|
||||||
|
image: curlimages/curl
|
||||||
|
environment:
|
||||||
|
MATTERMOST_BOT_ACCESS_TOKEN:
|
||||||
|
from_secret: mattermost_bot_access_token
|
||||||
|
MATTERMOST_CHANNEL_ID:
|
||||||
|
from_secret: mattermost_tests_channel_id
|
||||||
|
MATTERMOST_POST_API_URL:
|
||||||
|
from_secret: mattermost_post_api_url
|
||||||
|
commands:
|
||||||
|
- |
|
||||||
|
BODY=$(printf '{"channel_id":"%s","message":"[%s - Build #%s] Lint failure"}' "$MATTERMOST_CHANNEL_ID" "$CI_REPO" "$CI_PIPELINE_NUMBER")
|
||||||
|
curl -sS -X POST -H "Content-Type: application/json" -d "$BODY" -H "Authorization: Bearer $MATTERMOST_BOT_ACCESS_TOKEN" $MATTERMOST_POST_API_URL
|
||||||
|
depends_on:
|
||||||
|
- lint
|
||||||
|
when:
|
||||||
|
- status: [failure]
|
||||||
|
|
||||||
|
- name: Send CI Pipeline Status Notification (success)
|
||||||
|
image: curlimages/curl
|
||||||
|
environment:
|
||||||
|
MATTERMOST_BOT_ACCESS_TOKEN:
|
||||||
|
from_secret: mattermost_bot_access_token
|
||||||
|
MATTERMOST_CHANNEL_ID:
|
||||||
|
from_secret: mattermost_tests_channel_id
|
||||||
|
MATTERMOST_POST_API_URL:
|
||||||
|
from_secret: mattermost_post_api_url
|
||||||
|
commands:
|
||||||
|
- |
|
||||||
|
BODY=$(printf '{"channel_id":"%s","message":"[%s - Build #%s] CI pipeline success"}' "$MATTERMOST_CHANNEL_ID" "$CI_REPO" "$CI_PIPELINE_NUMBER")
|
||||||
|
curl -sS -X POST -H "Content-Type: application/json" -d "$BODY" -H "Authorization: Bearer $MATTERMOST_BOT_ACCESS_TOKEN" $MATTERMOST_POST_API_URL
|
||||||
|
depends_on:
|
||||||
|
- install
|
||||||
|
- format check
|
||||||
|
- lint
|
||||||
|
when:
|
||||||
|
- status: [success]
|
||||||
69
.woodpecker/deploy.yaml
Normal file
69
.woodpecker/deploy.yaml
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# Deploy: trigger Portainer stack redeploy (stack-from-repo; no image build).
|
||||||
|
# Runs on push/tag/manual to main only, after ci workflow succeeds.
|
||||||
|
skip_clone: true
|
||||||
|
workspace:
|
||||||
|
base: /tmp
|
||||||
|
path: deploy
|
||||||
|
when:
|
||||||
|
- branch: main
|
||||||
|
event: [push, tag, manual]
|
||||||
|
- event: deployment
|
||||||
|
evaluate: 'CI_PIPELINE_DEPLOY_TARGET == "production"'
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- ci
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Trigger Portainer stack redeploy
|
||||||
|
image: curlimages/curl:latest
|
||||||
|
environment:
|
||||||
|
PORTAINER_WEBHOOK_URL:
|
||||||
|
from_secret: portainer_webhook_url
|
||||||
|
commands:
|
||||||
|
- set -e
|
||||||
|
- echo "=== Triggering Portainer stack redeploy ==="
|
||||||
|
- |
|
||||||
|
resp=$(curl -s -w "\n%{http_code}" -X POST "$PORTAINER_WEBHOOK_URL")
|
||||||
|
body=$(echo "$resp" | head -n -1)
|
||||||
|
code=$(echo "$resp" | tail -n 1)
|
||||||
|
if [ "$code" != "200" ] && [ "$code" != "204" ]; then
|
||||||
|
echo "Webhook failed (HTTP $code): $body"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "✓ Portainer redeploy triggered (HTTP $code)"
|
||||||
|
|
||||||
|
- name: Send Deploy Status Notification (success)
|
||||||
|
image: curlimages/curl
|
||||||
|
environment:
|
||||||
|
MATTERMOST_BOT_ACCESS_TOKEN:
|
||||||
|
from_secret: mattermost_bot_access_token
|
||||||
|
MATTERMOST_CHANNEL_ID:
|
||||||
|
from_secret: mattermost_pushes_channel_id
|
||||||
|
MATTERMOST_POST_API_URL:
|
||||||
|
from_secret: mattermost_post_api_url
|
||||||
|
commands:
|
||||||
|
- |
|
||||||
|
BODY=$(printf '{"channel_id":"%s","message":"[%s - Build #%s] Production Deploy success 🎉"}' "$MATTERMOST_CHANNEL_ID" "$CI_REPO" "$CI_PIPELINE_NUMBER")
|
||||||
|
curl -sS -X POST -H "Content-Type: application/json" -d "$BODY" -H "Authorization: Bearer $MATTERMOST_BOT_ACCESS_TOKEN" "$MATTERMOST_POST_API_URL"
|
||||||
|
depends_on:
|
||||||
|
- Trigger Portainer stack redeploy
|
||||||
|
when:
|
||||||
|
- status: [success]
|
||||||
|
|
||||||
|
- name: Send Deploy Status Notification (failure)
|
||||||
|
image: curlimages/curl
|
||||||
|
environment:
|
||||||
|
MATTERMOST_BOT_ACCESS_TOKEN:
|
||||||
|
from_secret: mattermost_bot_access_token
|
||||||
|
MATTERMOST_CHANNEL_ID:
|
||||||
|
from_secret: mattermost_pushes_channel_id
|
||||||
|
MATTERMOST_POST_API_URL:
|
||||||
|
from_secret: mattermost_post_api_url
|
||||||
|
commands:
|
||||||
|
- |
|
||||||
|
BODY=$(printf '{"channel_id":"%s","message":"[%s - Build #%s] Production Deploy failure 💩"}' "$MATTERMOST_CHANNEL_ID" "$CI_REPO" "$CI_PIPELINE_NUMBER")
|
||||||
|
curl -sS -X POST -H "Content-Type: application/json" -d "$BODY" -H "Authorization: Bearer $MATTERMOST_BOT_ACCESS_TOKEN" "$MATTERMOST_POST_API_URL"
|
||||||
|
depends_on:
|
||||||
|
- Trigger Portainer stack redeploy
|
||||||
|
when:
|
||||||
|
- status: [failure]
|
||||||
47
AGENTS.md
Normal file
47
AGENTS.md
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# Agent guide – Umami analytics stack
|
||||||
|
|
||||||
|
This repo is a **self-hosted Umami analytics stack** for deployment via **Portainer** (stack from repository). It does not contain application source code; it defines the Docker Compose stack, CI, and docs.
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
- Run [Umami](https://umami.is) (privacy-focused analytics) with PostgreSQL.
|
||||||
|
- Reverse proxy: **Traefik** on the existing Docker network **marina-net**.
|
||||||
|
- Deploy by adding the stack in Portainer and pointing it at this repo.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
- **marina-net** (external): Shared network where Traefik runs. Only the **umami** service from this stack is attached so Traefik can route to it. No database on this network.
|
||||||
|
- **umami-backend** (internal): Only **umami** and **db** (PostgreSQL). Isolated from Traefik and the rest of the host.
|
||||||
|
- **umami** is on both networks; **db** is only on umami-backend.
|
||||||
|
- **Volumes**: One external volume, **umami-db-data**, for PostgreSQL data. Declared with `external: true`; the user must create it (e.g. `docker volume create umami-db-data`) before first deploy.
|
||||||
|
|
||||||
|
## Design decisions
|
||||||
|
|
||||||
|
- **No secrets in repo**: All sensitive values (APP_SECRET, POSTGRES_PASSWORD, etc.) come from Portainer stack environment variables or a local `.env`; see `.env.example` and SETUP.md.
|
||||||
|
- **External volume only**: DB data lives in a named external volume with an `umami-` prefix so it is explicit and portable.
|
||||||
|
- **Traefik Host rule**: Default host is **analytics.mifi.holdings**; can be changed in `docker-compose.yml` if deploying elsewhere. HTTPS is handled by Traefik (entrypoints `web` and `websecure`); no cert paths in this repo.
|
||||||
|
- **Variable substitution**: Compose uses `${POSTGRES_DB}`, `${POSTGRES_USER}`, `${POSTGRES_PASSWORD}`, `${APP_SECRET}` so Portainer (or `.env`) injects values.
|
||||||
|
|
||||||
|
## Key files
|
||||||
|
|
||||||
|
| File | Role |
|
||||||
|
| ------------------------- | --------------------------------------------------------------------------------------------------- |
|
||||||
|
| `docker-compose.yml` | Stack definition: services, networks, volumes, Traefik labels, env substitution. |
|
||||||
|
| `.env.example` | List of env var names and placeholders; no real values. |
|
||||||
|
| `SETUP.md` | Human-facing setup: volume creation, secret generation, Portainer env vars, Host rule, first login. |
|
||||||
|
| `AGENTS.md` | This file; for agents and developers. |
|
||||||
|
| `.woodpecker/ci.yaml` | CI: install deps, format check (Prettier). |
|
||||||
|
| `.woodpecker/deploy.yaml` | Deploy: trigger Portainer webhook after CI (no image build in this repo). |
|
||||||
|
|
||||||
|
## Where to change things
|
||||||
|
|
||||||
|
- **Traefik hostname**: In `docker-compose.yml`, label `traefik.http.routers.umami.rule=Host(\`...\`)`. Default is **analytics.mifi.holdings**.
|
||||||
|
- **Secrets**: Only in Portainer stack env or a local `.env`; never commit real values.
|
||||||
|
- **DB volume name**: Compose uses `umami-db-data`; if you change it, update the `volumes` section and document the new name (e.g. in SETUP.md).
|
||||||
|
|
||||||
|
## Deployment context
|
||||||
|
|
||||||
|
- **Repo**: `git.mifi.dev/mifi-holdings/umami.git`
|
||||||
|
- **CI**: Woodpecker; pipelines under `.woodpecker/` (ci, deploy). Deploy depends on **ci** (not build); this repo has no Docker image build (stack-from-repo only).
|
||||||
|
- **Prerequisites**: Existing Docker network **marina-net** and Traefik so the Umami service can be reached via the configured host.
|
||||||
|
- **First login**: No default credentials; the admin account is created in the Umami UI on first visit.
|
||||||
56
SETUP.md
Normal file
56
SETUP.md
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# Umami analytics stack – setup
|
||||||
|
|
||||||
|
Deploy this stack in Portainer as **Stack → Add stack → Build method: Git repository**, then use the web editor and the steps below.
|
||||||
|
|
||||||
|
## 1. Create the external volume (once)
|
||||||
|
|
||||||
|
If the volume does not exist yet, create it on the host (or the node where the stack runs):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker volume create umami-db-data
|
||||||
|
```
|
||||||
|
|
||||||
|
The compose file expects this name; the stack will fail to start if the volume is missing.
|
||||||
|
|
||||||
|
## 2. Generate secrets
|
||||||
|
|
||||||
|
Run these commands and paste each output into the corresponding Portainer env var (no quotes):
|
||||||
|
|
||||||
|
**APP_SECRET:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openssl rand -hex 32
|
||||||
|
```
|
||||||
|
|
||||||
|
**POSTGRES_PASSWORD:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openssl rand -hex 32
|
||||||
|
```
|
||||||
|
|
||||||
|
Use different values for each.
|
||||||
|
|
||||||
|
## 3. Environment variables in Portainer
|
||||||
|
|
||||||
|
In Portainer: **Stack → your stack → Editor → Add/Edit environment variables** (or the “Web editor” env vars section). Paste the block below, then replace the `REPLACE_ME` values with the outputs from step 2.
|
||||||
|
|
||||||
|
```
|
||||||
|
POSTGRES_DB=umami
|
||||||
|
POSTGRES_USER=umami
|
||||||
|
POSTGRES_PASSWORD=REPLACE_ME
|
||||||
|
APP_SECRET=REPLACE_ME
|
||||||
|
```
|
||||||
|
|
||||||
|
Create the external volume(s) (step 1) before the first deploy if they do not exist.
|
||||||
|
|
||||||
|
## 4. Traefik Host rule
|
||||||
|
|
||||||
|
The stack is configured for **analytics.mifi.holdings**. If you deploy elsewhere, edit `docker-compose.yml` and change the Traefik label:
|
||||||
|
|
||||||
|
- `traefik.http.routers.umami.rule=Host(\`your-domain.example.com\`)`
|
||||||
|
|
||||||
|
Then redeploy the stack (and reload Traefik if needed).
|
||||||
|
|
||||||
|
## 5. First login
|
||||||
|
|
||||||
|
After the stack is running, open the app at your configured host (e.g. https://analytics.mifi.holdings). Create the admin account in the Umami UI; there are no default credentials.
|
||||||
54
docker-compose.yml
Normal file
54
docker-compose.yml
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
services:
|
||||||
|
umami:
|
||||||
|
image: ghcr.io/umami-software/umami:postgresql-latest
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
|
||||||
|
APP_SECRET: ${APP_SECRET}
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
['CMD-SHELL', 'curl -f http://localhost:3000/api/heartbeat || exit 1']
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
networks:
|
||||||
|
- marina-net
|
||||||
|
- umami-backend
|
||||||
|
labels:
|
||||||
|
- traefik.enable=true
|
||||||
|
- traefik.docker.network=marina-net
|
||||||
|
- traefik.http.routers.umami.rule=Host(`analytics.mifi.holdings`)
|
||||||
|
- traefik.http.routers.umami.entrypoints=websecure
|
||||||
|
- traefik.http.routers.umami.tls=true
|
||||||
|
- traefik.http.routers.umami.tls.certresolver=letsencrypt
|
||||||
|
- traefik.http.services.umami.loadbalancer.server.port=3000
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: ${POSTGRES_DB}
|
||||||
|
POSTGRES_USER: ${POSTGRES_USER}
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
- umami-db-data:/var/lib/postgresql/data
|
||||||
|
networks:
|
||||||
|
- umami-backend
|
||||||
|
healthcheck:
|
||||||
|
test: ['CMD-SHELL', 'pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}']
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
networks:
|
||||||
|
marina-net:
|
||||||
|
external: true
|
||||||
|
umami-backend:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
umami-db-data:
|
||||||
|
external: true
|
||||||
18
package.json
Normal file
18
package.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "umami-stack",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"packageManager": "pnpm@10.29.2",
|
||||||
|
"engines": {
|
||||||
|
"pnpm": ">=10"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"format": "prettier --write .",
|
||||||
|
"format:check": "prettier --check .",
|
||||||
|
"lint": "yamllint docker-compose.yml .prettierrc.yaml .woodpecker/*.yaml"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"prettier": "3.4.2",
|
||||||
|
"yaml-lint": "1.7.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
381
pnpm-lock.yaml
generated
Normal file
381
pnpm-lock.yaml
generated
Normal file
@@ -0,0 +1,381 @@
|
|||||||
|
lockfileVersion: '9.0'
|
||||||
|
|
||||||
|
settings:
|
||||||
|
autoInstallPeers: true
|
||||||
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
|
importers:
|
||||||
|
|
||||||
|
.:
|
||||||
|
devDependencies:
|
||||||
|
prettier:
|
||||||
|
specifier: 3.4.2
|
||||||
|
version: 3.4.2
|
||||||
|
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.4.2:
|
||||||
|
resolution: {integrity: sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==}
|
||||||
|
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.4.2: {}
|
||||||
|
|
||||||
|
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
|
||||||
Reference in New Issue
Block a user