mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-04 06:20:34 +00:00
Merge upstream/main into public-achivements-ui
This commit is contained in:
@@ -126,8 +126,6 @@ jobs:
|
||||
chmod 600 ~/.ssh/id_rsa
|
||||
- name: 🚢 Deploy
|
||||
env:
|
||||
CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
|
||||
GHCR_REPO: ${{ vars.GHCR_REPO }}
|
||||
GHCR_USERNAME: ${{ vars.GHCR_USERNAME }}
|
||||
ENV: ${{ inputs.target_domain == 'openfront.io' && 'prod' || 'staging' }}
|
||||
@@ -147,10 +145,12 @@ jobs:
|
||||
echo "Deployment created in ${SECONDS} seconds" >> $GITHUB_STEP_SUMMARY
|
||||
echo "::endgroup::"
|
||||
- name: ⏳ Wait for deployment to start
|
||||
env:
|
||||
API_KEY: ${{ secrets.API_KEY }}
|
||||
run: |
|
||||
echo "::group::Wait for deployment to start"
|
||||
set -euxo pipefail
|
||||
while [ "$(curl -s https://${FQDN}/commit.txt)" != "${GITHUB_SHA}" ]; do
|
||||
while [ "$(curl -s -H "X-API-Key: ${API_KEY}" https://${FQDN}/commit.txt)" != "${GITHUB_SHA}" ]; do
|
||||
if [ "$SECONDS" -ge 300 ]; then
|
||||
echo "Timeout: deployment did not start within 5 minutes"
|
||||
exit 1
|
||||
|
||||
@@ -64,8 +64,6 @@ jobs:
|
||||
chmod 600 ~/.ssh/id_rsa
|
||||
- name: 🚀 Deploy image
|
||||
env:
|
||||
CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
|
||||
GHCR_REPO: openfront-prod
|
||||
GHCR_USERNAME: ${{ vars.GHCR_USERNAME }}
|
||||
DOMAIN: ${{ vars.DOMAIN }}
|
||||
@@ -82,10 +80,11 @@ jobs:
|
||||
- name: ⏳ Wait for deployment to start
|
||||
env:
|
||||
FQDN: alpha.${{ vars.DOMAIN }}
|
||||
API_KEY: ${{ secrets.API_KEY }}
|
||||
run: |
|
||||
echo "::group::Wait for deployment to start"
|
||||
set -euxo pipefail
|
||||
while [ "$(curl -s https://${FQDN}/commit.txt)" != "${GITHUB_SHA}" ]; do
|
||||
while [ "$(curl -s -H "X-API-Key: ${API_KEY}" https://${FQDN}/commit.txt)" != "${GITHUB_SHA}" ]; do
|
||||
if [ "$SECONDS" -ge 300 ]; then
|
||||
echo "Timeout: deployment did not start within 5 minutes"
|
||||
exit 1
|
||||
@@ -115,8 +114,6 @@ jobs:
|
||||
chmod 600 ~/.ssh/id_rsa
|
||||
- name: 🚀 Deploy image
|
||||
env:
|
||||
CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
|
||||
GHCR_REPO: ${{ vars.GHCR_REPO }}
|
||||
GHCR_USERNAME: ${{ vars.GHCR_USERNAME }}
|
||||
DOMAIN: ${{ vars.DOMAIN }}
|
||||
@@ -133,10 +130,11 @@ jobs:
|
||||
- name: ⏳ Wait for deployment to start
|
||||
env:
|
||||
FQDN: beta.${{ vars.DOMAIN }}
|
||||
API_KEY: ${{ secrets.API_KEY }}
|
||||
run: |
|
||||
echo "::group::Wait for deployment to start"
|
||||
set -euxo pipefail
|
||||
while [ "$(curl -s https://${FQDN}/commit.txt)" != "${GITHUB_SHA}" ]; do
|
||||
while [ "$(curl -s -H "X-API-Key: ${API_KEY}" https://${FQDN}/commit.txt)" != "${GITHUB_SHA}" ]; do
|
||||
if [ "$SECONDS" -ge 300 ]; then
|
||||
echo "Timeout: deployment did not start within 5 minutes"
|
||||
exit 1
|
||||
@@ -166,8 +164,6 @@ jobs:
|
||||
chmod 600 ~/.ssh/id_rsa
|
||||
- name: 🚀 Deploy image
|
||||
env:
|
||||
CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
|
||||
GHCR_REPO: ${{ vars.GHCR_REPO }}
|
||||
GHCR_USERNAME: ${{ vars.GHCR_USERNAME }}
|
||||
DOMAIN: ${{ vars.DOMAIN }}
|
||||
@@ -184,10 +180,11 @@ jobs:
|
||||
- name: ⏳ Wait for deployment to start
|
||||
env:
|
||||
FQDN: blue.${{ vars.DOMAIN }}
|
||||
API_KEY: ${{ secrets.API_KEY }}
|
||||
run: |
|
||||
echo "::group::Wait for deployment to start"
|
||||
set -euxo pipefail
|
||||
while [ "$(curl -s https://${FQDN}/commit.txt)" != "${GITHUB_SHA}" ]; do
|
||||
while [ "$(curl -s -H "X-API-Key: ${API_KEY}" https://${FQDN}/commit.txt)" != "${GITHUB_SHA}" ]; do
|
||||
if [ "$SECONDS" -ge 300 ]; then
|
||||
echo "Timeout: deployment did not start within 5 minutes"
|
||||
exit 1
|
||||
@@ -217,8 +214,6 @@ jobs:
|
||||
chmod 600 ~/.ssh/id_rsa
|
||||
- name: 🚀 Deploy image
|
||||
env:
|
||||
CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
|
||||
GHCR_REPO: ${{ vars.GHCR_REPO }}
|
||||
GHCR_USERNAME: ${{ vars.GHCR_USERNAME }}
|
||||
DOMAIN: ${{ vars.DOMAIN }}
|
||||
@@ -235,10 +230,11 @@ jobs:
|
||||
- name: ⏳ Wait for deployment to start
|
||||
env:
|
||||
FQDN: green.${{ vars.DOMAIN }}
|
||||
API_KEY: ${{ secrets.API_KEY }}
|
||||
run: |
|
||||
echo "::group::Wait for deployment to start"
|
||||
set -euxo pipefail
|
||||
while [ "$(curl -s https://${FQDN}/commit.txt)" != "${GITHUB_SHA}" ]; do
|
||||
while [ "$(curl -s -H "X-API-Key: ${API_KEY}" https://${FQDN}/commit.txt)" != "${GITHUB_SHA}" ]; do
|
||||
if [ "$SECONDS" -ge 300 ]; then
|
||||
echo "Timeout: deployment did not start within 5 minutes"
|
||||
exit 1
|
||||
|
||||
+10
-18
@@ -38,24 +38,14 @@ FROM base
|
||||
RUN apt-get update && apt-get install -y \
|
||||
nginx \
|
||||
curl \
|
||||
jq \
|
||||
wget \
|
||||
supervisor \
|
||||
apache2-utils \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb > cloudflared.deb \
|
||||
&& dpkg -i cloudflared.deb \
|
||||
&& rm cloudflared.deb
|
||||
|
||||
# Update worker_connections in nginx.conf
|
||||
RUN sed -i 's/worker_connections [0-9]*/worker_connections 8192/' /etc/nginx/nginx.conf
|
||||
|
||||
# Create cloudflared directory with proper permissions
|
||||
RUN mkdir -p /etc/cloudflared && \
|
||||
chown -R node:node /etc/cloudflared && \
|
||||
chmod -R 755 /etc/cloudflared
|
||||
|
||||
# Setup supervisor configuration
|
||||
RUN mkdir -p /var/log/supervisor
|
||||
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||
@@ -64,10 +54,6 @@ COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
RUN rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Copy and make executable the startup script
|
||||
COPY startup.sh /usr/local/bin/
|
||||
RUN chmod +x /usr/local/bin/startup.sh
|
||||
|
||||
# Copy production node_modules from prod-deps stage (cached separately from build)
|
||||
COPY --from=prod-deps /usr/src/app/node_modules ./node_modules
|
||||
COPY package*.json ./
|
||||
@@ -87,8 +73,14 @@ ARG GIT_COMMIT=unknown
|
||||
RUN echo "$GIT_COMMIT" > static/commit.txt
|
||||
|
||||
ENV GIT_COMMIT="$GIT_COMMIT"
|
||||
ENV CF_CONFIG_PATH=/etc/cloudflared/config.yml
|
||||
ENV CF_CREDS_PATH=/etc/cloudflared/creds.json
|
||||
|
||||
# Use the startup script as the entrypoint
|
||||
ENTRYPOINT ["/usr/local/bin/startup.sh"]
|
||||
RUN <<'EOF' tee /usr/local/bin/start.sh
|
||||
#!/bin/sh
|
||||
if [ "$DOMAIN" = openfront.dev ] && [ "$SUBDOMAIN" != main ]; then
|
||||
exec timeout 18h /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
|
||||
else
|
||||
exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
|
||||
fi
|
||||
EOF
|
||||
RUN chmod +x /usr/local/bin/start.sh
|
||||
ENTRYPOINT ["/usr/local/bin/start.sh"]
|
||||
|
||||
@@ -134,8 +134,6 @@ ENV=$ENV
|
||||
HOST=$HOST
|
||||
GHCR_IMAGE=$GHCR_IMAGE
|
||||
GHCR_TOKEN=$GHCR_TOKEN
|
||||
CF_ACCOUNT_ID=$CF_ACCOUNT_ID
|
||||
CF_API_TOKEN=$CF_API_TOKEN
|
||||
TURNSTILE_SECRET_KEY=$TURNSTILE_SECRET_KEY
|
||||
API_KEY=$API_KEY
|
||||
DOMAIN=$DOMAIN
|
||||
|
||||
@@ -6,9 +6,6 @@ GHCR_USERNAME=username
|
||||
GHCR_REPO=your-repo-name
|
||||
GHCR_TOKEN=your_docker_token_here
|
||||
|
||||
# Cloudflare Configuration
|
||||
CF_ACCOUNT_ID=your_cloudflare_account_id
|
||||
CF_API_TOKEN=your_cloudflare_api_token
|
||||
DOMAIN=your-domain.com
|
||||
|
||||
# API Key
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.0 MiB |
@@ -0,0 +1,195 @@
|
||||
{
|
||||
"name": "mediterranean",
|
||||
"nations": [
|
||||
{
|
||||
"coordinates": [141, 574],
|
||||
"name": "Lusitania",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [464, 519],
|
||||
"name": "Terraconensis",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [353, 787],
|
||||
"name": "Baetica",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [699, 340],
|
||||
"name": "Narbonensis",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [596, 91],
|
||||
"name": "Aquitania",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [910, 218],
|
||||
"name": "Alpes",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [1053, 39],
|
||||
"name": "Raetia",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [1316, 88],
|
||||
"name": "Noricum",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [1307, 479],
|
||||
"name": "Italia",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [1058, 534],
|
||||
"name": "Corsica et Sardinia",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [1350, 762],
|
||||
"name": "Sicilia",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [1553, 42],
|
||||
"name": "Pannonia",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [1522, 301],
|
||||
"name": "Dalmatia",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [1910, 119],
|
||||
"name": "Dacia",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [1811, 402],
|
||||
"name": "Macedonia",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [1654, 560],
|
||||
"name": "Epirus",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [1774, 774],
|
||||
"name": "Achaia",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [2030, 457],
|
||||
"name": "Thracia",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [2114, 250],
|
||||
"name": "Moesia",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [2428, 200],
|
||||
"name": "Bosporan Kingdom",
|
||||
"flag": ""
|
||||
},
|
||||
{
|
||||
"coordinates": [2065, 690],
|
||||
"name": "Asia",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [2354, 505],
|
||||
"name": "Pontus",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [2325, 669],
|
||||
"name": "Galatia",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [2447, 809],
|
||||
"name": "Cilicia",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [2196, 828],
|
||||
"name": "Lycia",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [2379, 942],
|
||||
"name": "Cyprus",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [2516, 648],
|
||||
"name": "Cappadocia",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [2772, 845],
|
||||
"name": "Mesopotamia",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [2584, 990],
|
||||
"name": "Syria",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [2490, 1141],
|
||||
"name": "Judaea",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [2481, 1292],
|
||||
"name": "Arabia Petraea",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [2263, 1240],
|
||||
"name": "Aegyptus",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [1799, 1096],
|
||||
"name": "Cyrenaica et Creta",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [2815, 659],
|
||||
"name": "Sassanid Empire",
|
||||
"flag": "Sassanid Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [1091, 989],
|
||||
"name": "Africa Proconsularis",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [597, 910],
|
||||
"name": "Mauretania Caesariensis",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [264, 1028],
|
||||
"name": "Mauretania Tingitania",
|
||||
"flag": "West Roman Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [584, 1199],
|
||||
"name": "Numidia",
|
||||
"flag": "Amazigh flag"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -84,6 +84,7 @@ var maps = []struct {
|
||||
{Name: "sanfrancisco"},
|
||||
{Name: "aegean"},
|
||||
{Name: "milkyway"},
|
||||
{Name: "mediterranean"},
|
||||
{Name: "big_plains", IsTest: true},
|
||||
{Name: "half_land_half_ocean", IsTest: true},
|
||||
{Name: "ocean_and_land", IsTest: true},
|
||||
|
||||
+7
-1
@@ -12,7 +12,13 @@ proxy_cache_path /var/cache/nginx/api levels=1:2 keys_zone=API_CACHE:10m inactiv
|
||||
|
||||
server {
|
||||
listen 80 default_server;
|
||||
|
||||
|
||||
# Large cookie support
|
||||
large_client_header_buffers 4 32k;
|
||||
proxy_buffer_size 32k;
|
||||
proxy_busy_buffers_size 48k;
|
||||
proxy_buffers 4 32k;
|
||||
|
||||
# Logging
|
||||
access_log /var/log/nginx/access.log;
|
||||
error_log /var/log/nginx/error.log;
|
||||
|
||||
Generated
+247
-376
@@ -24,7 +24,7 @@
|
||||
"dompurify": "^3.3.2",
|
||||
"dotenv": "^16.5.0",
|
||||
"ejs": "^3.1.10",
|
||||
"express": "^4.22.1",
|
||||
"express": "^5.2.1",
|
||||
"express-rate-limit": "^7.5.0",
|
||||
"fastpriorityqueue": "^0.7.5",
|
||||
"howler": "^2.2.4",
|
||||
@@ -52,7 +52,7 @@
|
||||
"@types/benchmark": "^2.1.5",
|
||||
"@types/chai": "^4.3.17",
|
||||
"@types/d3": "^7.4.3",
|
||||
"@types/express": "^4.17.23",
|
||||
"@types/express": "^5.0.6",
|
||||
"@types/google-protobuf": "^3.15.12",
|
||||
"@types/hammerjs": "^2.0.46",
|
||||
"@types/howler": "^2.2.12",
|
||||
@@ -4504,21 +4504,20 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/express": {
|
||||
"version": "4.17.23",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz",
|
||||
"integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==",
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz",
|
||||
"integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/body-parser": "*",
|
||||
"@types/express-serve-static-core": "^4.17.33",
|
||||
"@types/qs": "*",
|
||||
"@types/serve-static": "*"
|
||||
"@types/express-serve-static-core": "^5.0.0",
|
||||
"@types/serve-static": "^2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/express-serve-static-core": {
|
||||
"version": "4.19.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz",
|
||||
"integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==",
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz",
|
||||
"integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
@@ -4592,12 +4591,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/mime": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
|
||||
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/msgpack5": {
|
||||
"version": "3.4.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/msgpack5/-/msgpack5-3.4.6.tgz",
|
||||
@@ -4632,9 +4625,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/qs": {
|
||||
"version": "6.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz",
|
||||
"integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==",
|
||||
"version": "6.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz",
|
||||
"integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/range-parser": {
|
||||
@@ -4661,24 +4654,22 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/send": {
|
||||
"version": "0.17.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz",
|
||||
"integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==",
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz",
|
||||
"integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mime": "^1",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/serve-static": {
|
||||
"version": "1.15.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz",
|
||||
"integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==",
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz",
|
||||
"integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/http-errors": "*",
|
||||
"@types/node": "*",
|
||||
"@types/send": "*"
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/sinon": {
|
||||
@@ -5219,9 +5210,9 @@
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@xmldom/xmldom": {
|
||||
"version": "0.8.11",
|
||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz",
|
||||
"integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==",
|
||||
"version": "0.8.12",
|
||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.12.tgz",
|
||||
"integrity": "sha512-9k/gHF6n/pAi/9tqr3m3aqkuiNosYTurLLUtc7xQ9sxB/wm7WPygCv8GYa6mS0fLJEHhqMC1ATYhz++U/lRHqg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -5242,13 +5233,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
|
||||
"integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-types": "~2.1.34",
|
||||
"negotiator": "0.6.3"
|
||||
"mime-types": "^3.0.0",
|
||||
"negotiator": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
@@ -5361,12 +5352,6 @@
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||
"license": "Python-2.0"
|
||||
},
|
||||
"node_modules/array-flatten": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/assertion-error": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
|
||||
@@ -5525,52 +5510,23 @@
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "1.20.4",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz",
|
||||
"integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==",
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz",
|
||||
"integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "~3.1.2",
|
||||
"content-type": "~1.0.5",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"destroy": "~1.2.0",
|
||||
"http-errors": "~2.0.1",
|
||||
"iconv-lite": "~0.4.24",
|
||||
"on-finished": "~2.4.1",
|
||||
"qs": "~6.14.0",
|
||||
"raw-body": "~2.5.3",
|
||||
"type-is": "~1.6.18",
|
||||
"unpipe": "~1.0.0"
|
||||
"bytes": "^3.1.2",
|
||||
"content-type": "^1.0.5",
|
||||
"debug": "^4.4.3",
|
||||
"http-errors": "^2.0.0",
|
||||
"iconv-lite": "^0.7.0",
|
||||
"on-finished": "^2.4.1",
|
||||
"qs": "^6.14.1",
|
||||
"raw-body": "^3.0.1",
|
||||
"type-is": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8",
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser/node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser/node_modules/http-errors": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
|
||||
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"depd": "~2.0.0",
|
||||
"inherits": "~2.0.4",
|
||||
"setprototypeof": "~1.2.0",
|
||||
"statuses": "~2.0.2",
|
||||
"toidentifier": "~1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@@ -5578,30 +5534,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser/node_modules/iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
|
||||
"integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/body-parser/node_modules/statuses": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
||||
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/boolbase": {
|
||||
@@ -5617,9 +5562,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz",
|
||||
"integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
@@ -6258,15 +6203,16 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/content-disposition": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz",
|
||||
"integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "5.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/content-type": {
|
||||
@@ -6288,10 +6234,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/cookie-signature": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
|
||||
"license": "MIT"
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
|
||||
"integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-require": {
|
||||
"version": "1.1.1",
|
||||
@@ -6861,10 +6810,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
||||
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
|
||||
"dev": true,
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
@@ -6956,16 +6904,6 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/destroy": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
|
||||
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8",
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
|
||||
@@ -7625,45 +7563,42 @@
|
||||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.22.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
|
||||
"integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
|
||||
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "~1.20.3",
|
||||
"content-disposition": "~0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "~0.7.1",
|
||||
"cookie-signature": "~1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"finalhandler": "~1.3.1",
|
||||
"fresh": "~0.5.2",
|
||||
"http-errors": "~2.0.0",
|
||||
"merge-descriptors": "1.0.3",
|
||||
"methods": "~1.1.2",
|
||||
"on-finished": "~2.4.1",
|
||||
"parseurl": "~1.3.3",
|
||||
"path-to-regexp": "~0.1.12",
|
||||
"proxy-addr": "~2.0.7",
|
||||
"qs": "~6.14.0",
|
||||
"range-parser": "~1.2.1",
|
||||
"safe-buffer": "5.2.1",
|
||||
"send": "~0.19.0",
|
||||
"serve-static": "~1.16.2",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "~2.0.1",
|
||||
"type-is": "~1.6.18",
|
||||
"utils-merge": "1.0.1",
|
||||
"vary": "~1.1.2"
|
||||
"accepts": "^2.0.0",
|
||||
"body-parser": "^2.2.1",
|
||||
"content-disposition": "^1.0.0",
|
||||
"content-type": "^1.0.5",
|
||||
"cookie": "^0.7.1",
|
||||
"cookie-signature": "^1.2.1",
|
||||
"debug": "^4.4.0",
|
||||
"depd": "^2.0.0",
|
||||
"encodeurl": "^2.0.0",
|
||||
"escape-html": "^1.0.3",
|
||||
"etag": "^1.8.1",
|
||||
"finalhandler": "^2.1.0",
|
||||
"fresh": "^2.0.0",
|
||||
"http-errors": "^2.0.0",
|
||||
"merge-descriptors": "^2.0.0",
|
||||
"mime-types": "^3.0.0",
|
||||
"on-finished": "^2.4.1",
|
||||
"once": "^1.4.0",
|
||||
"parseurl": "^1.3.3",
|
||||
"proxy-addr": "^2.0.7",
|
||||
"qs": "^6.14.0",
|
||||
"range-parser": "^1.2.1",
|
||||
"router": "^2.2.0",
|
||||
"send": "^1.1.0",
|
||||
"serve-static": "^2.2.0",
|
||||
"statuses": "^2.0.1",
|
||||
"type-is": "^2.0.1",
|
||||
"vary": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
"node": ">= 18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@@ -7685,21 +7620,6 @@
|
||||
"express": "^4.11 || 5 || ^5.0.0-beta.1"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
@@ -7863,38 +7783,26 @@
|
||||
}
|
||||
},
|
||||
"node_modules/finalhandler": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
|
||||
"integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz",
|
||||
"integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "2.6.9",
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
"on-finished": "2.4.1",
|
||||
"parseurl": "~1.3.3",
|
||||
"statuses": "2.0.1",
|
||||
"unpipe": "~1.0.0"
|
||||
"debug": "^4.4.0",
|
||||
"encodeurl": "^2.0.0",
|
||||
"escape-html": "^1.0.3",
|
||||
"on-finished": "^2.4.1",
|
||||
"parseurl": "^1.3.3",
|
||||
"statuses": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
"node": ">= 18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/finalhandler/node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/finalhandler/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/flat-cache": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
|
||||
@@ -7946,12 +7854,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fresh": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
|
||||
"integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/fs-constants": {
|
||||
@@ -8130,9 +8038,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/glob/node_modules/brace-expansion": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz",
|
||||
"integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==",
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
|
||||
"integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -8274,19 +8182,23 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/http-errors": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
||||
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
|
||||
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"depd": "2.0.0",
|
||||
"inherits": "2.0.4",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "2.0.1",
|
||||
"toidentifier": "1.0.1"
|
||||
"depd": "~2.0.0",
|
||||
"inherits": "~2.0.4",
|
||||
"setprototypeof": "~1.2.0",
|
||||
"statuses": "~2.0.2",
|
||||
"toidentifier": "~1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/http-proxy-agent": {
|
||||
@@ -8540,6 +8452,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-promise": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
|
||||
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-stream": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
|
||||
@@ -9618,19 +9536,22 @@
|
||||
"license": "CC0-1.0"
|
||||
},
|
||||
"node_modules/media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
|
||||
"integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/merge-descriptors": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
|
||||
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
|
||||
"integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
@@ -9645,15 +9566,6 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/methods": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/micromatch": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||
@@ -9668,37 +9580,29 @@
|
||||
"node": ">=8.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"mime": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"version": "1.54.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
||||
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
|
||||
"integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
"mime-db": "^1.54.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/mimic-function": {
|
||||
@@ -9741,9 +9645,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch/node_modules/brace-expansion": {
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||
"version": "1.1.13",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz",
|
||||
"integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -9850,9 +9754,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/negotiator": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
|
||||
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
@@ -10001,7 +9905,6 @@
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
@@ -10200,10 +10103,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "0.1.12",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
|
||||
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
|
||||
"license": "MIT"
|
||||
"version": "8.4.1",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.1.tgz",
|
||||
"integrity": "sha512-fvU78fIjZ+SBM9YwCknCvKOUKkLVqtWDVctl0s7xIqfmfb38t2TT4ZU2gHm+Z8xGwgW+QWEU3oQSAzIbo89Ggw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/pathe": {
|
||||
"version": "0.2.0",
|
||||
@@ -10573,9 +10480,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.14.2",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
|
||||
"integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
|
||||
"version": "6.15.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz",
|
||||
"integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.1.0"
|
||||
@@ -10618,61 +10525,36 @@
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body": {
|
||||
"version": "2.5.3",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
|
||||
"integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz",
|
||||
"integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "~3.1.2",
|
||||
"http-errors": "~2.0.1",
|
||||
"iconv-lite": "~0.4.24",
|
||||
"iconv-lite": "~0.7.0",
|
||||
"unpipe": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body/node_modules/http-errors": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
|
||||
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
|
||||
"node_modules/raw-body/node_modules/iconv-lite": {
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
|
||||
"integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"depd": "~2.0.0",
|
||||
"inherits": "~2.0.4",
|
||||
"setprototypeof": "~1.2.0",
|
||||
"statuses": "~2.0.2",
|
||||
"toidentifier": "~1.0.1"
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body/node_modules/iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body/node_modules/statuses": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
||||
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/rc": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
||||
@@ -10871,6 +10753,22 @@
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/router": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
|
||||
"integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.4.0",
|
||||
"depd": "^2.0.0",
|
||||
"is-promise": "^4.0.0",
|
||||
"parseurl": "^1.3.3",
|
||||
"path-to-regexp": "^8.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/run-parallel": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||
@@ -11002,66 +10900,48 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/send": {
|
||||
"version": "0.19.0",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
|
||||
"integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz",
|
||||
"integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"destroy": "1.2.0",
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"fresh": "0.5.2",
|
||||
"http-errors": "2.0.0",
|
||||
"mime": "1.6.0",
|
||||
"ms": "2.1.3",
|
||||
"on-finished": "2.4.1",
|
||||
"range-parser": "~1.2.1",
|
||||
"statuses": "2.0.1"
|
||||
"debug": "^4.4.3",
|
||||
"encodeurl": "^2.0.0",
|
||||
"escape-html": "^1.0.3",
|
||||
"etag": "^1.8.1",
|
||||
"fresh": "^2.0.0",
|
||||
"http-errors": "^2.0.1",
|
||||
"mime-types": "^3.0.2",
|
||||
"ms": "^2.1.3",
|
||||
"on-finished": "^2.4.1",
|
||||
"range-parser": "^1.2.1",
|
||||
"statuses": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/debug/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/send/node_modules/encodeurl": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
"node": ">= 18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/serve-static": {
|
||||
"version": "1.16.2",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
|
||||
"integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz",
|
||||
"integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"encodeurl": "~2.0.0",
|
||||
"escape-html": "~1.0.3",
|
||||
"parseurl": "~1.3.3",
|
||||
"send": "0.19.0"
|
||||
"encodeurl": "^2.0.0",
|
||||
"escape-html": "^1.0.3",
|
||||
"parseurl": "^1.3.3",
|
||||
"send": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
"node": ">= 18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/setprototypeof": {
|
||||
@@ -11393,9 +11273,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
||||
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
@@ -12006,13 +11886,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/type-is": {
|
||||
"version": "1.6.18",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
|
||||
"integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"media-typer": "0.3.0",
|
||||
"mime-types": "~2.1.24"
|
||||
"content-type": "^1.0.5",
|
||||
"media-typer": "^1.1.0",
|
||||
"mime-types": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
@@ -12126,15 +12007,6 @@
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/utils-merge": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
|
||||
@@ -13195,7 +13067,6 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ws": {
|
||||
|
||||
+2
-2
@@ -36,7 +36,7 @@
|
||||
"@types/benchmark": "^2.1.5",
|
||||
"@types/chai": "^4.3.17",
|
||||
"@types/d3": "^7.4.3",
|
||||
"@types/express": "^4.17.23",
|
||||
"@types/express": "^5.0.6",
|
||||
"@types/google-protobuf": "^3.15.12",
|
||||
"@types/hammerjs": "^2.0.46",
|
||||
"@types/howler": "^2.2.12",
|
||||
@@ -107,7 +107,7 @@
|
||||
"dompurify": "^3.3.2",
|
||||
"dotenv": "^16.5.0",
|
||||
"ejs": "^3.1.10",
|
||||
"express": "^4.22.1",
|
||||
"express": "^5.2.1",
|
||||
"express-rate-limit": "^7.5.0",
|
||||
"fastpriorityqueue": "^0.7.5",
|
||||
"howler": "^2.2.4",
|
||||
|
||||
@@ -1231,6 +1231,11 @@
|
||||
"continent": "North America",
|
||||
"name": "Maine"
|
||||
},
|
||||
{
|
||||
"code": "Massachusetts",
|
||||
"continent": "North America",
|
||||
"name": "Massachusetts"
|
||||
},
|
||||
{
|
||||
"code": "mw",
|
||||
"continent": "Africa",
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"lang_code": "en"
|
||||
},
|
||||
"common": {
|
||||
"not_logged_in": "Not logged in",
|
||||
"close": "Close",
|
||||
"copy": "Copy",
|
||||
"paste": "Paste",
|
||||
@@ -362,7 +363,8 @@
|
||||
"arctic": "Arctic",
|
||||
"sanfrancisco": "San Francisco",
|
||||
"aegean": "Aegean",
|
||||
"milkyway": "Milky Way"
|
||||
"milkyway": "Milky Way",
|
||||
"mediterranean": "Mediterranean"
|
||||
},
|
||||
"map_categories": {
|
||||
"featured": "Featured",
|
||||
@@ -950,12 +952,12 @@
|
||||
"territory_patterns": {
|
||||
"title": "Skins",
|
||||
"purchase": "Purchase",
|
||||
"not_logged_in": "Not logged in",
|
||||
"pattern": {
|
||||
"default": "Default"
|
||||
},
|
||||
"select_skin": "Select Skin",
|
||||
"selected": "selected"
|
||||
"selected": "selected",
|
||||
"search": "Search..."
|
||||
},
|
||||
"cosmetics": {
|
||||
"artist_label": "Artist:"
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
{
|
||||
"map": {
|
||||
"height": 1448,
|
||||
"num_land_tiles": 2644620,
|
||||
"width": 2848
|
||||
},
|
||||
"map16x": {
|
||||
"height": 362,
|
||||
"num_land_tiles": 159261,
|
||||
"width": 712
|
||||
},
|
||||
"map4x": {
|
||||
"height": 724,
|
||||
"num_land_tiles": 652563,
|
||||
"width": 1424
|
||||
},
|
||||
"name": "mediterranean",
|
||||
"nations": [
|
||||
{
|
||||
"coordinates": [141, 574],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Lusitania"
|
||||
},
|
||||
{
|
||||
"coordinates": [464, 519],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Terraconensis"
|
||||
},
|
||||
{
|
||||
"coordinates": [353, 787],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Baetica"
|
||||
},
|
||||
{
|
||||
"coordinates": [699, 340],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Narbonensis"
|
||||
},
|
||||
{
|
||||
"coordinates": [596, 91],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Aquitania"
|
||||
},
|
||||
{
|
||||
"coordinates": [910, 218],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Alpes"
|
||||
},
|
||||
{
|
||||
"coordinates": [1053, 39],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Raetia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1316, 88],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Noricum"
|
||||
},
|
||||
{
|
||||
"coordinates": [1307, 479],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Italia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1058, 534],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Corsica et Sardinia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1350, 762],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Sicilia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1553, 42],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Pannonia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1522, 301],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Dalmatia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1910, 119],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Dacia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1811, 402],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Macedonia"
|
||||
},
|
||||
{
|
||||
"coordinates": [1654, 560],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Epirus"
|
||||
},
|
||||
{
|
||||
"coordinates": [1774, 774],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Achaia"
|
||||
},
|
||||
{
|
||||
"coordinates": [2030, 457],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Thracia"
|
||||
},
|
||||
{
|
||||
"coordinates": [2114, 250],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Moesia"
|
||||
},
|
||||
{
|
||||
"coordinates": [2428, 200],
|
||||
"name": "Bosporan Kingdom",
|
||||
"flag": ""
|
||||
},
|
||||
{
|
||||
"coordinates": [2065, 690],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Asia"
|
||||
},
|
||||
{
|
||||
"coordinates": [2354, 505],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Pontus"
|
||||
},
|
||||
{
|
||||
"coordinates": [2325, 669],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Galatia"
|
||||
},
|
||||
{
|
||||
"coordinates": [2447, 809],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Cilicia"
|
||||
},
|
||||
{
|
||||
"coordinates": [2196, 828],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Lycia"
|
||||
},
|
||||
{
|
||||
"coordinates": [2379, 942],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Cyprus"
|
||||
},
|
||||
{
|
||||
"coordinates": [2516, 648],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Cappadocia"
|
||||
},
|
||||
{
|
||||
"coordinates": [2772, 845],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Mesopotamia"
|
||||
},
|
||||
{
|
||||
"coordinates": [2584, 990],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Syria"
|
||||
},
|
||||
{
|
||||
"coordinates": [2490, 1141],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Judaea"
|
||||
},
|
||||
{
|
||||
"coordinates": [2481, 1292],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Arabia Petraea"
|
||||
},
|
||||
{
|
||||
"coordinates": [2263, 1240],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Aegyptus"
|
||||
},
|
||||
{
|
||||
"coordinates": [1799, 1096],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Cyrenaica et Creta"
|
||||
},
|
||||
{
|
||||
"coordinates": [2815, 659],
|
||||
"flag": "Sassanid Empire",
|
||||
"name": "Sassanid Empire"
|
||||
},
|
||||
{
|
||||
"coordinates": [1091, 989],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Africa Proconsularis"
|
||||
},
|
||||
{
|
||||
"coordinates": [597, 910],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Mauretania Caesariensis"
|
||||
},
|
||||
{
|
||||
"coordinates": [264, 1028],
|
||||
"flag": "West Roman Empire",
|
||||
"name": "Mauretania Tingitania"
|
||||
},
|
||||
{
|
||||
"coordinates": [584, 1199],
|
||||
"flag": "Amazigh flag",
|
||||
"name": "Numidia"
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
@@ -1,6 +1,7 @@
|
||||
import { html, TemplateResult } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import {
|
||||
AchievementsResponse,
|
||||
PlayerGame,
|
||||
PlayerStatsTree,
|
||||
UserMeResponse,
|
||||
@@ -29,6 +30,7 @@ export class AccountModal extends BaseModal {
|
||||
private userMeResponse: UserMeResponse | null = null;
|
||||
private statsTree: PlayerStatsTree | null = null;
|
||||
private recentGames: PlayerGame[] = [];
|
||||
private achievementGroups: AchievementsResponse = [];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@@ -40,15 +42,39 @@ export class AccountModal extends BaseModal {
|
||||
if (this.userMeResponse?.player?.publicId === undefined) {
|
||||
this.statsTree = null;
|
||||
this.recentGames = [];
|
||||
this.achievementGroups = [];
|
||||
} else {
|
||||
this.achievementGroups = this.getUserMeAchievementGroups(
|
||||
this.userMeResponse,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.statsTree = null;
|
||||
this.recentGames = [];
|
||||
this.achievementGroups = [];
|
||||
this.requestUpdate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private getUserMeAchievementGroups(
|
||||
userMeResponse: UserMeResponse | null,
|
||||
): AchievementsResponse {
|
||||
const achievements = userMeResponse?.player?.achievements;
|
||||
if (!achievements) return [];
|
||||
|
||||
return [
|
||||
{
|
||||
type: "singleplayer-map",
|
||||
data: achievements.singleplayerMap,
|
||||
},
|
||||
{
|
||||
type: "player",
|
||||
data: achievements.player ?? [],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
private hasAnyStats(): boolean {
|
||||
if (!this.statsTree) return false;
|
||||
// Check if statsTree has any data
|
||||
@@ -133,7 +159,10 @@ export class AccountModal extends BaseModal {
|
||||
private renderAccountInfo() {
|
||||
const me = this.userMeResponse?.user;
|
||||
const isLinked = me?.discord ?? me?.email;
|
||||
const achievements = this.userMeResponse?.player?.achievements ?? [];
|
||||
const achievements =
|
||||
this.achievementGroups.length > 0
|
||||
? this.achievementGroups
|
||||
: this.getUserMeAchievementGroups(this.userMeResponse);
|
||||
|
||||
if (!isLinked) {
|
||||
return this.renderLoginOptions();
|
||||
@@ -379,6 +408,7 @@ export class AccountModal extends BaseModal {
|
||||
.then((userMe) => {
|
||||
if (userMe) {
|
||||
this.userMeResponse = userMe;
|
||||
this.achievementGroups = this.getUserMeAchievementGroups(userMe);
|
||||
if (this.userMeResponse?.player?.publicId) {
|
||||
this.loadPlayerProfile(this.userMeResponse.player.publicId);
|
||||
}
|
||||
@@ -417,6 +447,9 @@ export class AccountModal extends BaseModal {
|
||||
|
||||
this.recentGames = data.games;
|
||||
this.statsTree = data.stats;
|
||||
this.achievementGroups =
|
||||
data.achievements ??
|
||||
this.getUserMeAchievementGroups(this.userMeResponse);
|
||||
|
||||
this.requestUpdate();
|
||||
} catch (err) {
|
||||
|
||||
@@ -25,6 +25,16 @@ declare global {
|
||||
},
|
||||
) => void;
|
||||
};
|
||||
banner: {
|
||||
requestBanner: (options: {
|
||||
id: string;
|
||||
width: number;
|
||||
height: number;
|
||||
}) => Promise<void>;
|
||||
requestResponsiveBanner: (containerId: string) => Promise<void>;
|
||||
clearBanner: (containerId: string) => void;
|
||||
clearAllBanners: () => void;
|
||||
};
|
||||
game: {
|
||||
gameplayStart: () => Promise<void>;
|
||||
gameplayStop: () => Promise<void>;
|
||||
@@ -76,11 +86,9 @@ export class CrazyGamesSDK {
|
||||
}
|
||||
return false;
|
||||
} catch (e) {
|
||||
console.log("[CrazyGames]: ", e);
|
||||
// If we get a cross-origin error, we're definitely iframed
|
||||
// Check our own referrer as fallback
|
||||
const isCrazyGames = document.referrer.includes("crazygames");
|
||||
console.log("[CrazyGames], contains referrer: ", isCrazyGames);
|
||||
if (isCrazyGames) {
|
||||
return true;
|
||||
}
|
||||
@@ -323,6 +331,70 @@ export class CrazyGamesSDK {
|
||||
}
|
||||
}
|
||||
|
||||
private bottomLeftContainerId = "cg-bottom-left-ad";
|
||||
private bottomLeftAdVisible = false;
|
||||
|
||||
createBottomLeftAd(): void {
|
||||
console.log(
|
||||
`[CrazyGames] createBottomLeftAd called, isReady=${this.isReady()}`,
|
||||
);
|
||||
if (!this.isReady()) {
|
||||
console.log("[CrazyGames] SDK not ready, skipping bottom-left ad");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.bottomLeftAdVisible) {
|
||||
console.log("[CrazyGames] Bottom-left ad already visible");
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove existing container if any
|
||||
document.getElementById(this.bottomLeftContainerId)?.remove();
|
||||
|
||||
const container = document.createElement("div");
|
||||
container.id = this.bottomLeftContainerId;
|
||||
container.style.cssText = `
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 300px;
|
||||
height: 250px;
|
||||
z-index: 9999;
|
||||
pointer-events: auto;
|
||||
`;
|
||||
document.body.appendChild(container);
|
||||
console.log("[CrazyGames] Created bottom-left ad container");
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
await window.CrazyGames!.SDK.banner.requestBanner({
|
||||
id: this.bottomLeftContainerId,
|
||||
width: 300,
|
||||
height: 250,
|
||||
});
|
||||
console.log("[CrazyGames] Bottom-left banner loaded");
|
||||
} catch (e) {
|
||||
console.log("[CrazyGames] Bottom-left banner error:", e);
|
||||
}
|
||||
})();
|
||||
|
||||
this.bottomLeftAdVisible = true;
|
||||
}
|
||||
|
||||
clearBottomLeftAd(): void {
|
||||
if (!this.bottomLeftAdVisible) return;
|
||||
|
||||
try {
|
||||
window.CrazyGames!.SDK.banner.clearBanner(this.bottomLeftContainerId);
|
||||
} catch (e) {
|
||||
console.error("[CrazyGames] Error clearing bottom-left banner:", e);
|
||||
}
|
||||
|
||||
document.getElementById(this.bottomLeftContainerId)?.remove();
|
||||
this.bottomLeftAdVisible = false;
|
||||
console.log("[CrazyGames] Bottom-left ad cleared");
|
||||
}
|
||||
|
||||
requestMidgameAd(): Promise<void> {
|
||||
return new Promise((resolve) => {
|
||||
if (!this.isReady()) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import { fetchCosmetics, flagRelationship } from "./Cosmetics";
|
||||
import { translateText } from "./Utils";
|
||||
import { BaseModal } from "./components/BaseModal";
|
||||
import "./components/FlagButton";
|
||||
import "./components/NotLoggedInWarning";
|
||||
import { modalHeader } from "./components/ui/ModalHeader";
|
||||
|
||||
@customElement("flag-input-modal")
|
||||
@@ -104,6 +105,7 @@ export class FlagInputModal extends BaseModal {
|
||||
title: translateText("flag_input.title"),
|
||||
onBack: () => this.close(),
|
||||
ariaLabel: translateText("common.back"),
|
||||
rightContent: html`<not-logged-in-warning></not-logged-in-warning>`,
|
||||
})}
|
||||
|
||||
<div class="md:flex items-center gap-2 justify-center mt-4">
|
||||
@@ -119,6 +121,17 @@ export class FlagInputModal extends BaseModal {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-center py-3 shrink-0">
|
||||
<button
|
||||
class="px-4 py-2 text-sm font-bold uppercase tracking-wider rounded-lg bg-blue-600 hover:bg-blue-700 text-white cursor-pointer transition-colors"
|
||||
@click=${() => {
|
||||
this.close();
|
||||
window.showPage?.("page-item-store");
|
||||
}}
|
||||
>
|
||||
${translateText("main.store")}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex-1 overflow-y-auto px-3 pb-3 scrollbar-thin scrollbar-thumb-white/20 scrollbar-track-transparent mr-1"
|
||||
|
||||
@@ -266,6 +266,19 @@ export class InputHandler {
|
||||
this.eventBus.emit(new MouseMoveEvent(e.clientX, e.clientY));
|
||||
}
|
||||
});
|
||||
// Clear all tracked keys when the window loses focus so keys that had
|
||||
// their keyup swallowed by the browser (e.g. cmd+zoom) don't stay stuck.
|
||||
// Also release the hold-to-view state and any active pointer/drag state
|
||||
// so the alternate view and drags aren't left latched when focus returns.
|
||||
window.addEventListener("blur", () => {
|
||||
this.activeKeys.clear();
|
||||
if (this.alternateView) {
|
||||
this.alternateView = false;
|
||||
this.eventBus.emit(new AlternateViewEvent(false));
|
||||
}
|
||||
this.pointerDown = false;
|
||||
this.pointers.clear();
|
||||
});
|
||||
this.pointers.clear();
|
||||
|
||||
this.moveInterval = setInterval(() => {
|
||||
@@ -310,13 +323,15 @@ export class InputHandler {
|
||||
|
||||
if (
|
||||
this.activeKeys.has(this.keybinds.zoomOut) ||
|
||||
this.activeKeys.has("Minus")
|
||||
this.activeKeys.has("Minus") ||
|
||||
this.activeKeys.has("NumpadSubtract")
|
||||
) {
|
||||
this.eventBus.emit(new ZoomEvent(cx, cy, this.ZOOM_SPEED));
|
||||
}
|
||||
if (
|
||||
this.activeKeys.has(this.keybinds.zoomIn) ||
|
||||
this.activeKeys.has("Equal")
|
||||
this.activeKeys.has("Equal") ||
|
||||
this.activeKeys.has("NumpadAdd")
|
||||
) {
|
||||
this.eventBus.emit(new ZoomEvent(cx, cy, -this.ZOOM_SPEED));
|
||||
}
|
||||
@@ -358,7 +373,19 @@ export class InputHandler {
|
||||
this.eventBus.emit(new ConfirmGhostStructureEvent());
|
||||
}
|
||||
|
||||
// Don't track zoom keys when a meta/ctrl modifier is held — that means
|
||||
// the browser is handling its own zoom (cmd+/cmd-) and the keyup will
|
||||
// never fire, which would leave the key stuck in activeKeys forever.
|
||||
// Also covers numpad zoom shortcuts (Ctrl+NumpadAdd/NumpadSubtract).
|
||||
const isBrowserZoomCombo =
|
||||
(e.metaKey || e.ctrlKey) &&
|
||||
(e.code === "Minus" ||
|
||||
e.code === "Equal" ||
|
||||
e.code === "NumpadAdd" ||
|
||||
e.code === "NumpadSubtract");
|
||||
|
||||
if (
|
||||
!isBrowserZoomCombo &&
|
||||
[
|
||||
this.keybinds.moveUp,
|
||||
this.keybinds.moveDown,
|
||||
@@ -372,6 +399,8 @@ export class InputHandler {
|
||||
"ArrowRight",
|
||||
"Minus",
|
||||
"Equal",
|
||||
"NumpadAdd",
|
||||
"NumpadSubtract",
|
||||
this.keybinds.attackRatioDown,
|
||||
this.keybinds.attackRatioUp,
|
||||
this.keybinds.centerCamera,
|
||||
@@ -390,6 +419,24 @@ export class InputHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
// When the meta (cmd) or ctrl key is released, any keys that were held
|
||||
// simultaneously will have had their keyup swallowed by the browser
|
||||
// (e.g. cmd+Plus for browser zoom). Clear zoom-related keys to
|
||||
// prevent them staying stuck in activeKeys.
|
||||
if (
|
||||
e.code === "MetaLeft" ||
|
||||
e.code === "MetaRight" ||
|
||||
e.code === "ControlLeft" ||
|
||||
e.code === "ControlRight"
|
||||
) {
|
||||
this.activeKeys.delete("Minus");
|
||||
this.activeKeys.delete("Equal");
|
||||
this.activeKeys.delete("NumpadAdd");
|
||||
this.activeKeys.delete("NumpadSubtract");
|
||||
this.activeKeys.delete(this.keybinds.zoomIn);
|
||||
this.activeKeys.delete(this.keybinds.zoomOut);
|
||||
}
|
||||
|
||||
if (e.code === this.keybinds.toggleView) {
|
||||
e.preventDefault();
|
||||
this.alternateView = false;
|
||||
@@ -537,8 +584,27 @@ export class InputHandler {
|
||||
const realCtrl =
|
||||
this.activeKeys.has("ControlLeft") ||
|
||||
this.activeKeys.has("ControlRight");
|
||||
const ratio = event.ctrlKey && !realCtrl ? 10 : 1; // Compensate pinch-zoom low sensitivity
|
||||
this.eventBus.emit(new ZoomEvent(event.x, event.y, event.deltaY * ratio));
|
||||
if (event.ctrlKey) {
|
||||
if (!realCtrl) {
|
||||
// Pinch-to-zoom gesture (trackpad): small deltas, amplify.
|
||||
// Ignore large deltas — those are browser zoom shortcuts (cmd+/cmd-)
|
||||
// which fire synthetic wheel events we don't want to handle.
|
||||
if (Math.abs(event.deltaY) <= 10) {
|
||||
this.eventBus.emit(
|
||||
new ZoomEvent(event.x, event.y, event.deltaY * 10),
|
||||
);
|
||||
}
|
||||
}
|
||||
// Always return when ctrlKey is set — whether it's a real ctrl scroll,
|
||||
// a pinch gesture, or a browser zoom event, none should reach the
|
||||
// regular scroll path below.
|
||||
return;
|
||||
}
|
||||
// Regular scroll wheel: ignore tiny residual momentum events that macOS
|
||||
// keeps sending after a gesture ends (especially after browser zoom changes
|
||||
// devicePixelRatio, which can cause these to accumulate into runaway zoom).
|
||||
if (Math.abs(event.deltaY) < 2) return;
|
||||
this.eventBus.emit(new ZoomEvent(event.x, event.y, event.deltaY));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-10
@@ -101,17 +101,9 @@ function updateAccountNavButton(userMeResponse: UserMeResponse | false) {
|
||||
// If the avatar fails to load (bad URL / CDN issue / offline), fall back
|
||||
// to the default sign-in UI instead of leaving a broken image.
|
||||
avatarEl.onerror = () => {
|
||||
// Only handle if this is the latest update
|
||||
if (avatarEl._navToken !== navToken) return;
|
||||
avatarEl.src = "";
|
||||
// If the user is still logged in via email, show the email badge state.
|
||||
const email =
|
||||
userMeResponse !== false ? userMeResponse.user.email : undefined;
|
||||
if (email) {
|
||||
showEmailLoggedIn();
|
||||
} else {
|
||||
showSignIn();
|
||||
}
|
||||
avatarEl.onerror = null;
|
||||
avatarEl.src = "https://cdn.discordapp.com/embed/avatars/0.png";
|
||||
};
|
||||
avatarEl.onload = () => {
|
||||
// Only handle if this is the latest update
|
||||
|
||||
@@ -19,11 +19,20 @@ export function initNavigation() {
|
||||
// Close mobile sidebar if a nav item was clicked
|
||||
closeMobileSidebar();
|
||||
|
||||
// Hide only the currently visible modal
|
||||
// Close the currently visible modal properly
|
||||
const visibleModal = document.querySelector(".page-content:not(.hidden)");
|
||||
if (visibleModal) {
|
||||
visibleModal.classList.add("hidden");
|
||||
visibleModal.classList.remove("block");
|
||||
// If it's an open modal component, call close() for proper cleanup (onClose callback, etc.)
|
||||
if (
|
||||
typeof (visibleModal as any).isOpen === "function" &&
|
||||
(visibleModal as any).isOpen() &&
|
||||
typeof (visibleModal as any).close === "function"
|
||||
) {
|
||||
(visibleModal as any).close();
|
||||
} else {
|
||||
visibleModal.classList.add("hidden");
|
||||
visibleModal.classList.remove("block");
|
||||
}
|
||||
}
|
||||
|
||||
// Handle page-play separately (it's not a page-content element)
|
||||
|
||||
@@ -145,14 +145,7 @@ export class SinglePlayerModal extends BaseModal {
|
||||
return;
|
||||
}
|
||||
|
||||
const achievements = Array.isArray(userMe.player.achievements)
|
||||
? userMe.player.achievements
|
||||
: [];
|
||||
|
||||
const completions =
|
||||
achievements.find(
|
||||
(achievement) => achievement?.type === "singleplayer-map",
|
||||
)?.data ?? [];
|
||||
const completions = userMe.player.achievements.singleplayerMap;
|
||||
|
||||
const winsMap = new Map<GameMapType, Set<Difficulty>>();
|
||||
for (const entry of completions) {
|
||||
|
||||
+2
-18
@@ -5,9 +5,9 @@ import { UserMeResponse } from "../core/ApiSchemas";
|
||||
import { ColorPalette, Cosmetics, Pattern } from "../core/CosmeticSchemas";
|
||||
import { UserSettings } from "../core/game/UserSettings";
|
||||
import { PlayerPattern } from "../core/Schemas";
|
||||
import { hasLinkedAccount } from "./Api";
|
||||
import { BaseModal } from "./components/BaseModal";
|
||||
import "./components/FlagButton";
|
||||
import "./components/NotLoggedInWarning";
|
||||
import "./components/PatternButton";
|
||||
import { modalHeader } from "./components/ui/ModalHeader";
|
||||
import {
|
||||
@@ -77,11 +77,7 @@ export class StoreModal extends BaseModal {
|
||||
title: translateText("store.title"),
|
||||
onBack: () => this.close(),
|
||||
ariaLabel: translateText("common.back"),
|
||||
rightContent: !hasLinkedAccount(this.userMeResponse)
|
||||
? html`<div class="flex items-center">
|
||||
${this.renderNotLoggedInWarning()}
|
||||
</div>`
|
||||
: undefined,
|
||||
rightContent: html`<not-logged-in-warning></not-logged-in-warning>`,
|
||||
})}
|
||||
<div class="flex items-center gap-2 justify-center pt-2">
|
||||
<button
|
||||
@@ -214,18 +210,6 @@ export class StoreModal extends BaseModal {
|
||||
`;
|
||||
}
|
||||
|
||||
private renderNotLoggedInWarning(): TemplateResult {
|
||||
return html`<button
|
||||
class="px-4 py-2 text-xs font-bold uppercase tracking-wider transition-colors duration-200 rounded-lg bg-red-500/20 text-red-400 border border-red-500/30 cursor-pointer hover:bg-red-500/30"
|
||||
@click=${() => {
|
||||
this.close();
|
||||
window.showPage?.("page-account");
|
||||
}}
|
||||
>
|
||||
${translateText("territory_patterns.not_logged_in")}
|
||||
</button>`;
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.isActive && !this.inline) return html``;
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ import { UserMeResponse } from "../core/ApiSchemas";
|
||||
import { Cosmetics, Pattern } from "../core/CosmeticSchemas";
|
||||
import { UserSettings } from "../core/game/UserSettings";
|
||||
import { PlayerPattern } from "../core/Schemas";
|
||||
import { hasLinkedAccount } from "./Api";
|
||||
import { BaseModal } from "./components/BaseModal";
|
||||
import "./components/NotLoggedInWarning";
|
||||
import "./components/PatternButton";
|
||||
import { modalHeader } from "./components/ui/ModalHeader";
|
||||
import {
|
||||
@@ -22,6 +22,7 @@ export class TerritoryPatternsModal extends BaseModal {
|
||||
|
||||
@state() private selectedPattern: PlayerPattern | null;
|
||||
@state() private selectedColor: string | null = null;
|
||||
@state() private search = "";
|
||||
|
||||
private cosmetics: Cosmetics | null = null;
|
||||
private userSettings: UserSettings = new UserSettings();
|
||||
@@ -67,6 +68,15 @@ export class TerritoryPatternsModal extends BaseModal {
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
private includedInSearch(name: string): boolean {
|
||||
const displayName = name.replace(/_/g, " ");
|
||||
return displayName.toLowerCase().includes(this.search.toLowerCase());
|
||||
}
|
||||
|
||||
private handleSearch(event: Event) {
|
||||
this.search = (event.target as HTMLInputElement).value;
|
||||
}
|
||||
|
||||
private renderPatternGrid(): TemplateResult {
|
||||
const buttons: TemplateResult[] = [];
|
||||
const patterns: (Pattern | null)[] = [
|
||||
@@ -74,6 +84,12 @@ export class TerritoryPatternsModal extends BaseModal {
|
||||
...Object.values(this.cosmetics?.patterns ?? {}),
|
||||
];
|
||||
for (const pattern of patterns) {
|
||||
if (pattern === null && this.search) {
|
||||
continue;
|
||||
}
|
||||
if (pattern !== null && !this.includedInSearch(pattern.name)) {
|
||||
continue;
|
||||
}
|
||||
const colorPalettes = pattern
|
||||
? [...(pattern.colorPalettes ?? []), null]
|
||||
: [null];
|
||||
@@ -123,18 +139,6 @@ export class TerritoryPatternsModal extends BaseModal {
|
||||
`;
|
||||
}
|
||||
|
||||
private renderNotLoggedInWarning(): TemplateResult {
|
||||
return html`<button
|
||||
class="px-4 py-2 text-xs font-bold uppercase tracking-wider transition-colors duration-200 rounded-lg bg-red-500/20 text-red-400 border border-red-500/30 cursor-pointer hover:bg-red-500/30"
|
||||
@click=${() => {
|
||||
this.close();
|
||||
window.showPage?.("page-account");
|
||||
}}
|
||||
>
|
||||
${translateText("territory_patterns.not_logged_in")}
|
||||
</button>`;
|
||||
}
|
||||
|
||||
render() {
|
||||
const content = html`
|
||||
<div class="${this.modalContainerClass}">
|
||||
@@ -145,12 +149,32 @@ export class TerritoryPatternsModal extends BaseModal {
|
||||
title: translateText("territory_patterns.title"),
|
||||
onBack: () => this.close(),
|
||||
ariaLabel: translateText("common.back"),
|
||||
rightContent: !hasLinkedAccount(this.userMeResponse)
|
||||
? html`<div class="flex items-center">
|
||||
${this.renderNotLoggedInWarning()}
|
||||
</div>`
|
||||
: undefined,
|
||||
rightContent: html`<not-logged-in-warning></not-logged-in-warning>`,
|
||||
})}
|
||||
|
||||
<div class="md:flex items-center gap-2 justify-center mt-4">
|
||||
<input
|
||||
class="h-12 w-full max-w-md border border-white/10 bg-black/60
|
||||
rounded-xl shadow-inner text-xl text-center focus:outline-none
|
||||
focus:ring-2 focus:ring-blue-500/50 focus:border-blue-500 text-white placeholder-white/30 transition-all"
|
||||
type="text"
|
||||
placeholder=${translateText("territory_patterns.search")}
|
||||
.value=${this.search}
|
||||
@change=${this.handleSearch}
|
||||
@keyup=${this.handleSearch}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-center py-3 shrink-0">
|
||||
<button
|
||||
class="px-4 py-2 text-sm font-bold uppercase tracking-wider rounded-lg bg-blue-600 hover:bg-blue-700 text-white cursor-pointer transition-colors"
|
||||
@click=${() => {
|
||||
this.close();
|
||||
window.showPage?.("page-item-store");
|
||||
}}
|
||||
>
|
||||
${translateText("main.store")}
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="flex-1 overflow-y-auto px-3 pb-3 scrollbar-thin scrollbar-thumb-white/20 scrollbar-track-transparent mr-1"
|
||||
@@ -181,6 +205,10 @@ export class TerritoryPatternsModal extends BaseModal {
|
||||
await this.refresh();
|
||||
}
|
||||
|
||||
protected onClose(): void {
|
||||
this.search = "";
|
||||
}
|
||||
|
||||
private selectPattern(pattern: PlayerPattern | null) {
|
||||
this.selectedColor = null;
|
||||
this.userSettings.setSelectedColor(undefined);
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { UserMeResponse } from "../../core/ApiSchemas";
|
||||
import { hasLinkedAccount } from "../Api";
|
||||
|
||||
@customElement("not-logged-in-warning")
|
||||
export class NotLoggedInWarning extends LitElement {
|
||||
@state() private linked = false;
|
||||
|
||||
private _onUserMe = (event: CustomEvent<UserMeResponse | false>) => {
|
||||
this.linked = hasLinkedAccount(event.detail);
|
||||
};
|
||||
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
document.addEventListener(
|
||||
"userMeResponse",
|
||||
this._onUserMe as EventListener,
|
||||
);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
document.removeEventListener(
|
||||
"userMeResponse",
|
||||
this._onUserMe as EventListener,
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.linked) return html``;
|
||||
|
||||
return html`<div class="flex items-center">
|
||||
<button
|
||||
class="px-4 py-2 text-xs font-bold uppercase tracking-wider transition-colors duration-200 rounded-lg bg-red-500/20 text-red-400 border border-red-500/30 cursor-pointer hover:bg-red-500/30"
|
||||
data-i18n="common.not_logged_in"
|
||||
@click=${() => {
|
||||
window.showPage?.("page-account");
|
||||
}}
|
||||
>
|
||||
Not logged in
|
||||
</button>
|
||||
</div>`;
|
||||
}
|
||||
}
|
||||
@@ -31,15 +31,20 @@ export class DiscordUserHeader extends LitElement {
|
||||
}
|
||||
|
||||
render() {
|
||||
const defaultAvatar = "https://cdn.discordapp.com/embed/avatars/0.png";
|
||||
const imgSrc = this.avatarUrl ?? defaultAvatar;
|
||||
return html`
|
||||
<div class="flex items-center gap-2">
|
||||
${this.avatarUrl
|
||||
${this._data
|
||||
? html`
|
||||
<div class="p-[3px] rounded-full bg-gray-500">
|
||||
<img
|
||||
class="w-12 h-12 rounded-full block"
|
||||
src="${this.avatarUrl}"
|
||||
src="${imgSrc}"
|
||||
alt="${translateText("discord_user_header.avatar_alt")}"
|
||||
@error=${(e: Event) => {
|
||||
(e.target as HTMLImageElement).src = defaultAvatar;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
`
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
import { GameView } from "../../../core/game/GameView";
|
||||
import { crazyGamesSDK } from "../../CrazyGamesSDK";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
const AD_TYPE = "standard_iab_left1";
|
||||
@@ -30,6 +31,7 @@ export class InGamePromo extends LitElement implements Layer {
|
||||
}
|
||||
if (!this.cornerAdShown) {
|
||||
this.cornerAdShown = true;
|
||||
console.log("[InGamePromo] Spawn phase ended, triggering showAd");
|
||||
this.showAd();
|
||||
}
|
||||
}
|
||||
@@ -73,10 +75,19 @@ export class InGamePromo extends LitElement implements Layer {
|
||||
}
|
||||
|
||||
private showAd(): void {
|
||||
if (!window.adsEnabled) return;
|
||||
console.log(
|
||||
`[InGamePromo] showAd called, isOnCrazyGames=${crazyGamesSDK.isOnCrazyGames()}`,
|
||||
);
|
||||
if (window.innerWidth < 1100) return;
|
||||
if (window.innerHeight < 750) return;
|
||||
|
||||
if (crazyGamesSDK.isOnCrazyGames()) {
|
||||
this.showCrazyGamesAd();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!window.adsEnabled) return;
|
||||
|
||||
this.shouldShow = true;
|
||||
this.requestUpdate();
|
||||
|
||||
@@ -85,6 +96,25 @@ export class InGamePromo extends LitElement implements Layer {
|
||||
});
|
||||
}
|
||||
|
||||
private showCrazyGamesAd(): void {
|
||||
console.log(
|
||||
`[InGamePromo] showCrazyGamesAd called, isReady=${crazyGamesSDK.isReady()}, width=${window.innerWidth}, height=${window.innerHeight}`,
|
||||
);
|
||||
if (!crazyGamesSDK.isReady()) {
|
||||
console.log(
|
||||
"[InGamePromo] CrazyGames SDK not ready, skipping in-game ad",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.requestUpdate();
|
||||
|
||||
this.updateComplete.then(() => {
|
||||
console.log("[InGamePromo] DOM updated, calling createBottomLeftAd");
|
||||
crazyGamesSDK.createBottomLeftAd();
|
||||
});
|
||||
}
|
||||
|
||||
private loadAd(): void {
|
||||
if (!window.ramp) {
|
||||
console.warn("Playwire RAMP not available for in-game ad");
|
||||
@@ -112,6 +142,14 @@ export class InGamePromo extends LitElement implements Layer {
|
||||
|
||||
public hideAd(): void {
|
||||
this.destroyBottomRail();
|
||||
|
||||
if (crazyGamesSDK.isOnCrazyGames()) {
|
||||
crazyGamesSDK.clearBottomLeftAd();
|
||||
this.shouldShow = false;
|
||||
this.requestUpdate();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!window.ramp) {
|
||||
console.warn("Playwire RAMP not available for in-game ad");
|
||||
return;
|
||||
|
||||
@@ -246,12 +246,6 @@ export class NukeTrajectoryPreviewLayer implements Layer {
|
||||
game: this.game,
|
||||
targetTile,
|
||||
magnitude: this.game.config().nukeMagnitudes(ghostStructure),
|
||||
allySmallIds: new Set(
|
||||
this.game
|
||||
.myPlayer()
|
||||
?.allies()
|
||||
.map((a) => a.smallID()),
|
||||
),
|
||||
threshold: this.game.config().nukeAllianceBreakThreshold(),
|
||||
});
|
||||
// Find the point where SAM can intercept
|
||||
|
||||
@@ -420,18 +420,19 @@ function createMenuElements(
|
||||
: !BuildableAttacks.has(item.unitType)),
|
||||
)
|
||||
.map((item: BuildItemDisplay) => {
|
||||
const canBuildOrUpgrade = params.buildMenu.canBuildOrUpgrade(item);
|
||||
return {
|
||||
id: `${elementIdPrefix}_${item.unitType}`,
|
||||
name: item.key
|
||||
? item.key.replace("unit_type.", "")
|
||||
: item.unitType.toString(),
|
||||
disabled: () => !canBuildOrUpgrade,
|
||||
color: canBuildOrUpgrade
|
||||
? filterType === "attack"
|
||||
? COLORS.attack
|
||||
: COLORS.building
|
||||
: undefined,
|
||||
disabled: (p: MenuElementParams) =>
|
||||
!p.buildMenu.canBuildOrUpgrade(item),
|
||||
color: (p: MenuElementParams) =>
|
||||
p.buildMenu.canBuildOrUpgrade(item)
|
||||
? filterType === "attack"
|
||||
? COLORS.attack
|
||||
: COLORS.building
|
||||
: COLORS.building,
|
||||
icon: item.icon,
|
||||
tooltipItems: [
|
||||
{ text: translateText(item.key ?? ""), className: "title" },
|
||||
@@ -456,7 +457,7 @@ function createMenuElements(
|
||||
if (buildableUnit === undefined) {
|
||||
return;
|
||||
}
|
||||
if (canBuildOrUpgrade) {
|
||||
if (params.buildMenu.canBuildOrUpgrade(item)) {
|
||||
params.buildMenu.sendBuildOrUpgrade(buildableUnit, params.tile);
|
||||
}
|
||||
params.closeMenu();
|
||||
|
||||
@@ -83,6 +83,11 @@ export const AchievementsResponseSchema = z.array(
|
||||
);
|
||||
export type AchievementsResponse = z.infer<typeof AchievementsResponseSchema>;
|
||||
|
||||
const UserMeAchievementsSchema = z.object({
|
||||
singleplayerMap: z.array(SingleplayerMapAchievementSchema),
|
||||
player: z.array(PlayerAchievementSchema).optional(),
|
||||
});
|
||||
|
||||
export const UserMeResponseSchema = z.object({
|
||||
user: z.object({
|
||||
discord: DiscordUserSchema.optional(),
|
||||
@@ -92,7 +97,7 @@ export const UserMeResponseSchema = z.object({
|
||||
publicId: z.string(),
|
||||
roles: z.string().array().optional(),
|
||||
flares: z.string().array().optional(),
|
||||
achievements: AchievementsResponseSchema.optional(),
|
||||
achievements: UserMeAchievementsSchema,
|
||||
leaderboard: z
|
||||
.object({
|
||||
oneVone: z
|
||||
|
||||
@@ -89,7 +89,6 @@ export class NukeExecution implements Execution {
|
||||
game: this.mg,
|
||||
targetTile: this.dst,
|
||||
magnitude,
|
||||
allySmallIds: new Set(this.player.allies().map((a) => a.smallID())),
|
||||
threshold: this.mg.config().nukeAllianceBreakThreshold(),
|
||||
});
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ export interface NukeAllianceCheckParams {
|
||||
game: Game | GameView;
|
||||
targetTile: TileRef;
|
||||
magnitude: NukeMagnitude;
|
||||
allySmallIds: Set<number>;
|
||||
allySmallIds?: Set<number>;
|
||||
threshold: number;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ export function wouldNukeBreakAlliance(
|
||||
): boolean {
|
||||
const { game, targetTile, magnitude, allySmallIds, threshold } = params;
|
||||
|
||||
if (allySmallIds.size === 0) {
|
||||
if (!allySmallIds || allySmallIds.size === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -144,6 +144,7 @@ export enum GameMapType {
|
||||
SanFrancisco = "San Francisco",
|
||||
Aegean = "Aegean",
|
||||
MilkyWay = "MilkyWay",
|
||||
Mediterranean = "Mediterranean",
|
||||
}
|
||||
|
||||
export type GameMapName = keyof typeof GameMapType;
|
||||
@@ -195,6 +196,7 @@ export const mapCategories: Record<string, GameMapType[]> = {
|
||||
GameMapType.Arctic,
|
||||
GameMapType.SanFrancisco,
|
||||
GameMapType.Aegean,
|
||||
GameMapType.Mediterranean,
|
||||
],
|
||||
fantasy: [
|
||||
GameMapType.Pangaea,
|
||||
|
||||
@@ -59,6 +59,7 @@ export async function readGameRecord(
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"x-api-key": config.apiKey(),
|
||||
},
|
||||
});
|
||||
const record = await response.json();
|
||||
|
||||
@@ -55,6 +55,9 @@ export function registerGamePreviewRoute(opts: {
|
||||
const apiDomain = config.jwtIssuer();
|
||||
const encodedID = encodeURIComponent(gameID);
|
||||
const response = await fetch(`${apiDomain}/game/${encodedID}`, {
|
||||
headers: {
|
||||
"x-api-key": config.apiKey(),
|
||||
},
|
||||
signal: controller.signal,
|
||||
});
|
||||
if (!response.ok) return null;
|
||||
|
||||
@@ -85,6 +85,7 @@ const frequency: Partial<Record<GameMapName, number>> = {
|
||||
SanFrancisco: 3,
|
||||
Aegean: 6,
|
||||
MilkyWay: 8,
|
||||
Mediterranean: 6,
|
||||
};
|
||||
|
||||
const TEAM_WEIGHTS: { config: TeamCountConfig; weight: number }[] = [
|
||||
|
||||
@@ -158,7 +158,7 @@ app.get("/api/instance", (_req, res) => {
|
||||
});
|
||||
|
||||
// SPA fallback route
|
||||
app.get("*", async function (_req, res) {
|
||||
app.get("/{*splat}", async function (_req, res) {
|
||||
try {
|
||||
const htmlPath = path.join(__dirname, "../../static/index.html");
|
||||
await renderAppShell(res, htmlPath);
|
||||
|
||||
@@ -109,9 +109,6 @@ export async function startWorker() {
|
||||
app.use(compression());
|
||||
app.use(express.json());
|
||||
|
||||
// Configure MIME types for webp files
|
||||
express.static.mime.define({ "image/webp": ["webp"] });
|
||||
|
||||
app.use(
|
||||
express.static(path.join(__dirname, "../../out"), {
|
||||
setHeaders: (res) => {
|
||||
|
||||
@@ -74,6 +74,7 @@ export async function getUserMe(
|
||||
const response = await fetch(config.jwtIssuer() + "/users/@me", {
|
||||
headers: {
|
||||
authorization: `Bearer ${token}`,
|
||||
"x-api-key": config.apiKey(),
|
||||
},
|
||||
});
|
||||
if (response.status !== 200) {
|
||||
|
||||
-92
@@ -1,92 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Check if required environment variables are set
|
||||
if [ -z "$CF_API_TOKEN" ] || [ -z "$CF_ACCOUNT_ID" ] || [ -z "$SUBDOMAIN" ] || [ -z "$DOMAIN" ]; then
|
||||
echo "Error: Required environment variables not set"
|
||||
echo "Please set CF_API_TOKEN, CF_ACCOUNT_ID, SUBDOMAIN, and DOMAIN"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Generate a unique tunnel name using timestamp
|
||||
TIMESTAMP=$(date +%Y%m%d%H%M%S)
|
||||
TUNNEL_NAME="${SUBDOMAIN}-tunnel-${TIMESTAMP}"
|
||||
echo "Using unique tunnel name: ${TUNNEL_NAME}"
|
||||
|
||||
# Create a new tunnel
|
||||
echo "Creating Cloudflare tunnel for subdomain ${SUBDOMAIN}..."
|
||||
TUNNEL_RESPONSE=$(curl -s -X POST "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/cfd_tunnel" \
|
||||
-H "Authorization: Bearer ${CF_API_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data "{\"name\":\"${TUNNEL_NAME}\"}")
|
||||
|
||||
# Extract tunnel ID and token
|
||||
TUNNEL_ID=$(echo $TUNNEL_RESPONSE | jq -r '.result.id')
|
||||
TUNNEL_TOKEN=$(echo $TUNNEL_RESPONSE | jq -r '.result.token')
|
||||
|
||||
if [ -z "$TUNNEL_ID" ] || [ "$TUNNEL_ID" == "null" ]; then
|
||||
echo "Failed to create tunnel"
|
||||
echo $TUNNEL_RESPONSE
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Tunnel created with ID: ${TUNNEL_ID}"
|
||||
|
||||
# Configure the tunnel with hostname
|
||||
echo "Configuring tunnel to point to tunnel-${SUBDOMAIN}.${DOMAIN}..."
|
||||
curl -s -X PUT "https://api.cloudflare.com/client/v4/accounts/${CF_ACCOUNT_ID}/cfd_tunnel/${TUNNEL_ID}/configurations" \
|
||||
-H "Authorization: Bearer ${CF_API_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data "{\"config\":{\"ingress\":[{\"hostname\":\"tunnel-${SUBDOMAIN}.${DOMAIN}\",\"service\":\"http://localhost:80\"},{\"service\":\"http_status:404\"}]}}"
|
||||
|
||||
# Update DNS record to point to the new tunnel
|
||||
echo "Updating DNS record to point to the new tunnel..."
|
||||
|
||||
# First check if DNS record exists
|
||||
DNS_RECORDS=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones?name=${DOMAIN}" \
|
||||
-H "Authorization: Bearer ${CF_API_TOKEN}" \
|
||||
-H "Content-Type: application/json")
|
||||
|
||||
ZONE_ID=$(echo $DNS_RECORDS | jq -r '.result[0].id')
|
||||
|
||||
if [ -z "$ZONE_ID" ] || [ "$ZONE_ID" == "null" ]; then
|
||||
echo "Could not find zone ID for domain ${DOMAIN}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for existing record
|
||||
EXISTING_RECORDS=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records?name=tunnel-${SUBDOMAIN}.${DOMAIN}" \
|
||||
-H "Authorization: Bearer ${CF_API_TOKEN}" \
|
||||
-H "Content-Type: application/json")
|
||||
|
||||
RECORD_ID=$(echo $EXISTING_RECORDS | jq -r '.result[0].id')
|
||||
|
||||
# Create or update the DNS record
|
||||
if [ -z "$RECORD_ID" ] || [ "$RECORD_ID" == "null" ]; then
|
||||
# Create new record
|
||||
echo "Creating new DNS record..."
|
||||
DNS_RESPONSE=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records" \
|
||||
-H "Authorization: Bearer ${CF_API_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data "{\"type\":\"CNAME\",\"name\":\"tunnel-${SUBDOMAIN}.${DOMAIN}\",\"content\":\"${TUNNEL_ID}.cfargotunnel.com\",\"ttl\":1,\"proxied\":true}")
|
||||
else
|
||||
# Update existing record
|
||||
echo "Updating existing DNS record..."
|
||||
DNS_RESPONSE=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records/${RECORD_ID}" \
|
||||
-H "Authorization: Bearer ${CF_API_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data "{\"type\":\"CNAME\",\"name\":\"tunnel-${SUBDOMAIN}.${DOMAIN}\",\"content\":\"${TUNNEL_ID}.cfargotunnel.com\",\"ttl\":1,\"proxied\":true}")
|
||||
fi
|
||||
|
||||
# Log the tunnel information
|
||||
echo "Tunnel configuration is set up! Site will be available at: https://tunnel-${SUBDOMAIN}.${DOMAIN}"
|
||||
|
||||
# Export the tunnel token for supervisord
|
||||
export CLOUDFLARE_TUNNEL_TOKEN=${TUNNEL_TOKEN}
|
||||
|
||||
# Start supervisord
|
||||
if [ "$DOMAIN" = openfront.dev ] && [ "$SUBDOMAIN" != main ]; then
|
||||
exec timeout 18h /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
|
||||
else
|
||||
exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
|
||||
fi
|
||||
@@ -23,11 +23,3 @@ stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
|
||||
[program:cloudflared]
|
||||
command=cloudflared tunnel run --token %(ENV_CLOUDFLARE_TUNNEL_TOKEN)s
|
||||
autostart=true
|
||||
autorestart=true
|
||||
user=node
|
||||
stdout_logfile=/var/log/cloudflared.log
|
||||
stderr_logfile=/var/log/cloudflared-err.log
|
||||
@@ -557,7 +557,11 @@ describe("RadialMenuElements", () => {
|
||||
const subMenu = buildMenuElement.subMenu!(mockParams);
|
||||
const cityElement = subMenu.find((item) => item.id === "build_City");
|
||||
|
||||
expect(cityElement!.color).toBe(COLORS.building);
|
||||
expect(
|
||||
(cityElement!.color as (params: MenuElementParams) => string)(
|
||||
mockParams,
|
||||
),
|
||||
).toBe(COLORS.building);
|
||||
});
|
||||
|
||||
it("should use correct colors for attack elements", () => {
|
||||
@@ -572,16 +576,24 @@ describe("RadialMenuElements", () => {
|
||||
(item) => item.id === "attack_Atom Bomb",
|
||||
);
|
||||
|
||||
expect(atomBombElement!.color).toBe(COLORS.attack);
|
||||
expect(
|
||||
(atomBombElement!.color as (params: MenuElementParams) => string)(
|
||||
mockParams,
|
||||
),
|
||||
).toBe(COLORS.attack);
|
||||
});
|
||||
|
||||
it("should not set color when element is disabled", () => {
|
||||
it("should use disabled color when element is disabled", () => {
|
||||
mockBuildMenu.canBuildOrUpgrade = vi.fn(() => false);
|
||||
|
||||
const subMenu = buildMenuElement.subMenu!(mockParams);
|
||||
const cityElement = subMenu.find((item) => item.id === "build_City");
|
||||
|
||||
expect(cityElement!.color).toBeUndefined();
|
||||
expect(
|
||||
(cityElement!.color as (params: MenuElementParams) => string)(
|
||||
mockParams,
|
||||
),
|
||||
).toBe(COLORS.building);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -44,50 +44,56 @@ app.get("/api/maps", (req: Request, res: Response) => {
|
||||
* GET /api/maps/:name
|
||||
* Get map metadata (map data, dimensions)
|
||||
*/
|
||||
app.get("/api/maps/:name", async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { name } = req.params;
|
||||
const metadata = await getMapMetadata(name);
|
||||
res.json(metadata);
|
||||
} catch (error) {
|
||||
console.error(`Error loading map ${req.params.name}:`, error);
|
||||
app.get(
|
||||
"/api/maps/:name",
|
||||
async (req: Request<{ name: string }>, res: Response) => {
|
||||
try {
|
||||
const { name } = req.params;
|
||||
const metadata = await getMapMetadata(name);
|
||||
res.json(metadata);
|
||||
} catch (error) {
|
||||
console.error(`Error loading map ${req.params.name}:`, error);
|
||||
|
||||
if (error instanceof Error && error.message.includes("ENOENT")) {
|
||||
res.status(404).json({
|
||||
error: "Map not found",
|
||||
message: `Map "${req.params.name}" does not exist`,
|
||||
});
|
||||
} else {
|
||||
res.status(500).json({
|
||||
error: "Failed to load map",
|
||||
message: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
if (error instanceof Error && error.message.includes("ENOENT")) {
|
||||
res.status(404).json({
|
||||
error: "Map not found",
|
||||
message: `Map "${req.params.name}" does not exist`,
|
||||
});
|
||||
} else {
|
||||
res.status(500).json({
|
||||
error: "Failed to load map",
|
||||
message: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* GET /api/maps/:name/thumbnail
|
||||
* Get map thumbnail image
|
||||
*/
|
||||
app.get("/api/maps/:name/thumbnail", (req: Request, res: Response) => {
|
||||
try {
|
||||
const { name } = req.params;
|
||||
const thumbnailPath = join(
|
||||
dirname(fileURLToPath(import.meta.url)),
|
||||
"../../../resources/maps",
|
||||
name,
|
||||
"thumbnail.webp",
|
||||
);
|
||||
res.sendFile(thumbnailPath);
|
||||
} catch (error) {
|
||||
console.error(`Error loading thumbnail for ${req.params.name}:`, error);
|
||||
res.status(404).json({
|
||||
error: "Thumbnail not found",
|
||||
message: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
}
|
||||
});
|
||||
app.get(
|
||||
"/api/maps/:name/thumbnail",
|
||||
(req: Request<{ name: string }>, res: Response) => {
|
||||
try {
|
||||
const { name } = req.params;
|
||||
const thumbnailPath = join(
|
||||
dirname(fileURLToPath(import.meta.url)),
|
||||
"../../../resources/maps",
|
||||
name,
|
||||
"thumbnail.webp",
|
||||
);
|
||||
res.sendFile(thumbnailPath);
|
||||
} catch (error) {
|
||||
console.error(`Error loading thumbnail for ${req.params.name}:`, error);
|
||||
res.status(404).json({
|
||||
error: "Thumbnail not found",
|
||||
message: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* POST /api/pathfind
|
||||
@@ -237,7 +243,11 @@ app.use((err: Error, req: Request, res: Response, next: any) => {
|
||||
});
|
||||
|
||||
// Start server
|
||||
app.listen(PORT, () => {
|
||||
app.listen(PORT, (error?: Error) => {
|
||||
if (error) {
|
||||
console.error("Failed to start server", error);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(`
|
||||
╔════════════════════════════════════════════════════════════╗
|
||||
║ Pathfinding Playground Server ║
|
||||
|
||||
@@ -62,14 +62,10 @@ echo "Starting new container for ${HOST} environment..."
|
||||
# Ensure the traefik network exists
|
||||
docker network create web 2> /dev/null || true
|
||||
|
||||
# Remove any existing volume for this container if it exists
|
||||
docker volume rm "cloudflared-${CONTAINER_NAME}" 2> /dev/null || true
|
||||
|
||||
docker run -d \
|
||||
--restart="${RESTART}" \
|
||||
--env-file "$ENV_FILE" \
|
||||
--name "${CONTAINER_NAME}" \
|
||||
-v "cloudflared-${CONTAINER_NAME}:/etc/cloudflared" \
|
||||
--network web \
|
||||
--label "traefik.enable=true" \
|
||||
--label "traefik.http.routers.${CONTAINER_NAME}.rule=Host(\`${SUBDOMAIN}.${DOMAIN}\`)" \
|
||||
|
||||
Reference in New Issue
Block a user