From 7fea6370793d388702c5903579c759edb87be057 Mon Sep 17 00:00:00 2001 From: Jon Fairbanks Date: Tue, 29 Sep 2026 22:47:16 -0700 Subject: [PATCH 1/4] Target Dependabot Updates to Develop (#297) * Target Dependabot Updates to Develop * Update Vulnerable Transitive Dependencies --- .github/dependabot.yml | 3 + .github/workflows/dependabot-auto-merge.yml | 35 +++++ package-lock.json | 143 ++++++++------------ 3 files changed, 93 insertions(+), 88 deletions(-) create mode 100644 .github/workflows/dependabot-auto-merge.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ea3a0c0..5a5c3de 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -2,6 +2,7 @@ version: 2 updates: - package-ecosystem: npm directory: / + target-branch: develop schedule: interval: weekly groups: @@ -9,9 +10,11 @@ updates: patterns: ["*"] - package-ecosystem: github-actions directory: / + target-branch: develop schedule: interval: weekly - package-ecosystem: docker directory: / + target-branch: develop schedule: interval: weekly diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml new file mode 100644 index 0000000..cdeddab --- /dev/null +++ b/.github/workflows/dependabot-auto-merge.yml @@ -0,0 +1,35 @@ +name: Dependabot Auto Merge + +on: + pull_request_target: + types: [opened, synchronize, reopened, ready_for_review] + +permissions: + contents: write + pull-requests: write + +jobs: + enable-automerge: + if: >- + github.event.pull_request.base.ref == 'develop' && + github.event.pull_request.user.login == 'dependabot[bot]' && + startsWith(github.event.pull_request.head.ref, 'dependabot/') && + github.event.pull_request.head.repo.full_name == github.repository && + github.event.pull_request.draft == false + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Fetch Dependabot Metadata + id: metadata + uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3.1.0 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Enable Auto-Merge + if: >- + steps.metadata.outputs.update-type == 'version-update:semver-patch' || + steps.metadata.outputs.update-type == 'version-update:semver-minor' + run: gh pr merge --auto --squash "$PR_URL" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_URL: ${{ github.event.pull_request.html_url }} diff --git a/package-lock.json b/package-lock.json index 57d9e0b..305f85c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -81,29 +81,6 @@ "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@eslint/config-array/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", - "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, "node_modules/@eslint/config-array/node_modules/minimatch": { "version": "10.2.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", @@ -367,6 +344,16 @@ "dev": true, "license": "MIT" }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/body-parser": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", @@ -404,6 +391,19 @@ "url": "https://opencollective.com/express" } }, + "node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -771,29 +771,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", - "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, "node_modules/eslint/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -1624,15 +1601,20 @@ } }, "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz", + "integrity": "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==", + "license": "MIT", "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" }, "engines": { "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/punycode": { @@ -1646,9 +1628,9 @@ } }, "node_modules/qs": { - "version": "6.15.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", - "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", + "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", "license": "BSD-3-Clause", "dependencies": { "es-define-property": "^1.0.1", @@ -2061,21 +2043,6 @@ "minimatch": "^10.2.4" }, "dependencies": { - "balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true - }, - "brace-expansion": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", - "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", - "dev": true, - "requires": { - "balanced-match": "^4.0.2" - } - }, "minimatch": { "version": "10.2.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", @@ -2245,6 +2212,12 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, + "balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true + }, "body-parser": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", @@ -2268,6 +2241,15 @@ } } }, + "brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "requires": { + "balanced-match": "^4.0.2" + } + }, "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -2481,21 +2463,6 @@ "optionator": "^0.9.3" }, "dependencies": { - "balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true - }, - "brace-expansion": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", - "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", - "dev": true, - "requires": { - "balanced-match": "^4.0.2" - } - }, "escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -3060,9 +3027,9 @@ "dev": true }, "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz", + "integrity": "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==", "requires": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -3075,9 +3042,9 @@ "dev": true }, "qs": { - "version": "6.15.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", - "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", + "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", "requires": { "es-define-property": "^1.0.1", "side-channel": "^1.1.1" From 20819aebb6c97afb809d1130421f6559bafd9b9a Mon Sep 17 00:00:00 2001 From: Jon Fairbanks Date: Tue, 29 Sep 2026 22:54:46 -0700 Subject: [PATCH 2/4] Require Successful Security Checks Before Publishing --- .github/workflows/ci.yaml | 1 + .github/workflows/codeql.yml | 1 + .github/workflows/publish.yaml | 92 ++++++++++++++++++++++++++++++---- .github/workflows/snyk.yml | 18 +++---- 4 files changed, 92 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ba7d44c..159f9b7 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,6 +1,7 @@ name: CI on: + workflow_dispatch: push: branches: [develop, master] pull_request: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 0bdcea8..f451d01 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,6 +1,7 @@ name: CodeQL on: + workflow_dispatch: push: branches: [develop, master] pull_request: diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 9cfa3a6..b5f1aa2 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -1,6 +1,7 @@ name: Publish on: + workflow_dispatch: workflow_run: workflows: [CI] types: [completed] @@ -10,18 +11,90 @@ permissions: contents: read concurrency: - group: publish-${{ github.event.workflow_run.head_branch }} + group: publish-${{ github.event_name == 'workflow_dispatch' && github.ref_name || github.event.workflow_run.head_branch }} cancel-in-progress: false jobs: + verify: + name: Verify Release Checks + if: >- + github.event_name == 'workflow_dispatch' || + (github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push') + runs-on: ubuntu-latest + timeout-minutes: 15 + permissions: + actions: write + checks: read + contents: read + outputs: + sha: ${{ steps.release.outputs.sha }} + branch: ${{ steps.release.outputs.branch }} + steps: + - name: Verify Exact Release Commit + id: release + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + RELEASE_SHA: ${{ github.event_name == 'workflow_dispatch' && github.sha || github.event.workflow_run.head_sha }} + RELEASE_BRANCH: ${{ github.event_name == 'workflow_dispatch' && github.ref_name || github.event.workflow_run.head_branch }} + MANUAL_RELEASE: ${{ github.event_name == 'workflow_dispatch' }} + run: | + case "$RELEASE_BRANCH" in + develop|master) ;; + *) echo "Unsupported release branch: $RELEASE_BRANCH" >&2; exit 1 ;; + esac + current_sha="$(gh api "repos/$GH_REPO/commits/$RELEASE_BRANCH" --jq .sha)" + if [ "$current_sha" != "$RELEASE_SHA" ]; then + echo "Release commit is no longer the branch head." >&2 + exit 1 + fi + + # Manual releases rerun all checks, including the current vulnerability data. + check_after="$(date -u -d '24 hours ago' +%Y-%m-%dT%H:%M:%SZ)" + if [ "$MANUAL_RELEASE" = true ]; then + check_after="$(date -u +%Y-%m-%dT%H:%M:%SZ)" + for workflow in ci.yaml snyk.yml codeql.yml; do + gh workflow run "$workflow" --ref "$RELEASE_BRANCH" + done + fi + + if [ "$RELEASE_BRANCH" = master ]; then + required='["Test (Node 26)","Lint and audit","Helm lint and render","Build container","Open Source and Container","Analyze JavaScript"]' + else + required='["Test (Node 22)","Test (Node 24)","Lint and audit","Helm lint and render","Build container","Open Source","Analyze JavaScript"]' + fi + + for attempt in {1..60}; do + checks="$(gh api --paginate --slurp "repos/$GH_REPO/commits/$RELEASE_SHA/check-runs?per_page=100")" + missing="$(jq -r --argjson required "$required" --arg after "$check_after" ' + [.[].check_runs[] | select(.app.slug == "github-actions")] as $runs + | $required[] as $name + | ([$runs[] | select(.name == $name)] | max_by(.id)) as $latest + | select($latest == null or $latest.status != "completed" + or $latest.conclusion != "success" or $latest.started_at < $after) + | $name + ' <<< "$checks")" + if [ -z "$missing" ]; then + current_sha="$(gh api "repos/$GH_REPO/commits/$RELEASE_BRANCH" --jq .sha)" + test "$current_sha" = "$RELEASE_SHA" + echo "sha=$RELEASE_SHA" >> "$GITHUB_OUTPUT" + echo "branch=$RELEASE_BRANCH" >> "$GITHUB_OUTPUT" + exit 0 + fi + echo "Waiting for successful checks on $RELEASE_SHA: $missing" + sleep 10 + done + echo "Release blocked by missing, stale, skipped, or failed checks: $missing" >&2 + exit 1 + container: name: Publish container - if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' + needs: verify runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: - ref: ${{ github.event.workflow_run.head_sha }} + ref: ${{ needs.verify.outputs.sha }} - name: Set up QEMU uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx @@ -41,9 +114,9 @@ jobs: images: jonfairbanks/docker-node-app tags: | type=sha - type=raw,value=develop,enable=${{ github.event.workflow_run.head_branch == 'develop' }} - type=raw,value=latest,enable=${{ github.event.workflow_run.head_branch == 'master' }} - type=raw,value=${{ steps.package.outputs.version }},enable=${{ github.event.workflow_run.head_branch == 'master' }} + type=raw,value=develop,enable=${{ needs.verify.outputs.branch == 'develop' }} + type=raw,value=latest,enable=${{ needs.verify.outputs.branch == 'master' }} + type=raw,value=${{ steps.package.outputs.version }},enable=${{ needs.verify.outputs.branch == 'master' }} - name: Build and push uses: docker/build-push-action@v7 with: @@ -58,12 +131,13 @@ jobs: helm: name: Publish Helm chart - if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' && github.event.workflow_run.head_branch == 'master' + needs: verify + if: needs.verify.outputs.branch == 'master' runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: - ref: ${{ github.event.workflow_run.head_sha }} + ref: ${{ needs.verify.outputs.sha }} - uses: azure/setup-helm@v4 - name: Check out chart repository uses: actions/checkout@v6 @@ -83,4 +157,4 @@ jobs: git commit -m "Publish docker-node-app chart ${SOURCE_SHA::7}" git push env: - SOURCE_SHA: ${{ github.event.workflow_run.head_sha }} + SOURCE_SHA: ${{ needs.verify.outputs.sha }} diff --git a/.github/workflows/snyk.yml b/.github/workflows/snyk.yml index afba1c5..06e90dd 100644 --- a/.github/workflows/snyk.yml +++ b/.github/workflows/snyk.yml @@ -19,36 +19,32 @@ permissions: jobs: open-source: name: Open Source - if: >- - github.actor != 'dependabot[bot]' && - (github.event_name != 'pull_request' || - github.event.pull_request.head.repo.full_name == github.repository) runs-on: ubuntu-latest + timeout-minutes: 15 env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} steps: - - name: Check Snyk configuration - if: env.SNYK_TOKEN == '' - run: echo "::warning::SNYK_TOKEN is not configured; skipping Snyk scan." + - name: Require Snyk Token + run: | + if [ -z "$SNYK_TOKEN" ]; then + echo "::error::SNYK_TOKEN is required. Configure it in Actions and Dependabot secrets." + exit 1 + fi - name: Checkout - if: env.SNYK_TOKEN != '' uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - name: Use Node.js 24.x - if: env.SNYK_TOKEN != '' uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 24.x cache: npm - name: Install dependencies - if: env.SNYK_TOKEN != '' run: npm ci - name: Scan dependencies - if: env.SNYK_TOKEN != '' uses: snyk/actions/node@9adf32b1121593767fc3c057af55b55db032dc04 # v1.0.0 with: args: >- From a932031d7c667e5cc797b99e6dd45270016a4d6b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Sep 2026 06:03:26 +0000 Subject: [PATCH 3/4] Bump the npm group with 4 updates (#300) Bumps the npm group with 4 updates: [dayjs](https://github.com/iamkun/dayjs), [eslint](https://github.com/eslint/eslint), [globals](https://github.com/sindresorhus/globals) and [supertest](https://github.com/ladjs/supertest). Updates `dayjs` from 1.11.21 to 1.11.23 - [Release notes](https://github.com/iamkun/dayjs/releases) - [Changelog](https://github.com/iamkun/dayjs/blob/v1.11.23/CHANGELOG.md) - [Commits](https://github.com/iamkun/dayjs/compare/v1.11.21...v1.11.23) Updates `eslint` from 10.6.0 to 10.11.0 - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](https://github.com/eslint/eslint/compare/v10.6.0...v10.11.0) Updates `globals` from 17.7.0 to 17.12.0 - [Release notes](https://github.com/sindresorhus/globals/releases) - [Commits](https://github.com/sindresorhus/globals/compare/v17.7.0...v17.12.0) Updates `supertest` from 7.2.2 to 7.3.0 - [Release notes](https://github.com/ladjs/supertest/releases) - [Commits](https://github.com/ladjs/supertest/compare/v7.2.2...v7.3.0) --- updated-dependencies: - dependency-name: dayjs dependency-version: 1.11.23 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: npm - dependency-name: eslint dependency-version: 10.11.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: npm - dependency-name: globals dependency-version: 17.12.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: npm - dependency-name: supertest dependency-version: 7.3.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: npm ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 366 +++++++++++++++++++++++++++++++++------------- package.json | 8 +- 2 files changed, 271 insertions(+), 103 deletions(-) diff --git a/package-lock.json b/package-lock.json index 305f85c..566b8d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,21 +9,45 @@ "version": "3.0.0", "license": "MIT", "dependencies": { - "dayjs": "^1.11.21", + "dayjs": "^1.11.23", "ejs": "^6.0.1", "express": "^5.2.1" }, "devDependencies": { "@eslint/js": "^10.0.1", - "eslint": "^10.6.0", - "globals": "^17.7.0", - "supertest": "^7.2.2" + "eslint": "^10.11.0", + "globals": "^17.12.0", + "supertest": "^7.3.0" }, "engines": { "node": ">=22.13 <25", "npm": ">=10" } }, + "node_modules/@cacheable/memory": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.2.0.tgz", + "integrity": "sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cacheable/utils": "^2.5.0", + "@keyv/bigmap": "^1.3.1", + "hookified": "^1.15.1", + "keyv": "^5.6.0" + } + }, + "node_modules/@cacheable/utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.5.0.tgz", + "integrity": "sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hashery": "^1.5.1", + "keyv": "^5.6.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", @@ -98,9 +122,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", - "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", + "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -155,9 +179,9 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", - "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.3.tgz", + "integrity": "sha512-IkO+/KEUvwbVpiURZg+P7zF74z5Jxe0UgJxVni+RtoHQ6IZieXaO02kmadomap/q+l6bc/jdPGGqTjhuZnuz1Q==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -233,6 +257,30 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@keyv/bigmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@keyv/bigmap/-/bigmap-1.3.1.tgz", + "integrity": "sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hashery": "^1.4.0", + "hookified": "^1.15.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "keyv": "^5.6.0" + } + }, + "node_modules/@keyv/serialize": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", + "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", + "dev": true, + "license": "MIT" + }, "node_modules/@noble/hashes": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", @@ -413,6 +461,20 @@ "node": ">= 0.8" } }, + "node_modules/cacheable": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.5.0.tgz", + "integrity": "sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cacheable/memory": "^2.2.0", + "@cacheable/utils": "^2.5.0", + "hookified": "^1.15.0", + "keyv": "^5.6.0", + "qified": "^0.10.1" + } + }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -528,9 +590,9 @@ } }, "node_modules/dayjs": { - "version": "1.11.21", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz", - "integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==", + "version": "1.11.23", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.23.tgz", + "integrity": "sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ==", "license": "MIT" }, "node_modules/debug": { @@ -681,9 +743,9 @@ "license": "MIT" }, "node_modules/eslint": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.6.0.tgz", - "integrity": "sha512-6lVbcqSodALYo+4ELD0heG6lFiFxnLMuLkiMi2qV8LMp54N8tE8FT1GMH+ev4Ti00nFjNze2+Su6DsV5OQW3Dg==", + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.11.0.tgz", + "integrity": "sha512-P7a6UEEqb9G95MYAtqkmsTbVXIYyzIfl6NGOIJk162PaahFxFyeGcrlXYFSiagECg4sEm8IseJdZBKR3rx6MsQ==", "dev": true, "license": "MIT", "workspaces": [ @@ -693,9 +755,9 @@ "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.5", - "@eslint/config-helpers": "^0.6.0", + "@eslint/config-helpers": "^0.7.0", "@eslint/core": "^1.2.1", - "@eslint/plugin-kit": "^0.7.2", + "@eslint/plugin-kit": "^0.7.3", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -710,14 +772,14 @@ "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", + "file-entry-cache": "11.1.5 || >11.1.6 <12", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "minimatch": "^10.2.4", + "minimatch": "^10.2.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, @@ -955,16 +1017,13 @@ "license": "MIT" }, "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-11.1.5.tgz", + "integrity": "sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q==", "dev": true, "license": "MIT", "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" + "flat-cache": "^6.1.23" } }, "node_modules/finalhandler": { @@ -1005,23 +1064,21 @@ } }, "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "version": "6.1.23", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.23.tgz", + "integrity": "sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA==", "dev": true, "license": "MIT", "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" + "cacheable": "^2.5.0", + "flatted": "^3.4.2", + "hookified": "^1.15.0" } }, "node_modules/flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", "dev": true, "license": "ISC" }, @@ -1147,9 +1204,9 @@ } }, "node_modules/globals": { - "version": "17.7.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.7.0.tgz", - "integrity": "sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==", + "version": "17.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.12.0.tgz", + "integrity": "sha512-cezEd/DTyyht9cvSSURyygXPfy04GtWO/5e6ZPvH7fCtjKz9PYOmuawphw1Ctd1f6C+5JypXfGD7ahNMXvevBA==", "dev": true, "license": "MIT", "engines": { @@ -1199,6 +1256,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hashery": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.5.1.tgz", + "integrity": "sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookified": "^1.15.0" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/hasown": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", @@ -1211,6 +1281,13 @@ "node": ">= 0.4" } }, + "node_modules/hookified": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz", + "integrity": "sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==", + "dev": true, + "license": "MIT" + }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", @@ -1314,13 +1391,6 @@ "dev": true, "license": "ISC" }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -1335,13 +1405,13 @@ "dev": true }, "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "dev": true, "license": "MIT", "dependencies": { - "json-buffer": "3.0.1" + "@keyv/serialize": "^1.1.1" } }, "node_modules/levn": { @@ -1627,6 +1697,26 @@ "node": ">=6" } }, + "node_modules/qified": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/qified/-/qified-0.10.1.tgz", + "integrity": "sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookified": "^2.1.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/qified/node_modules/hookified": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-2.2.0.tgz", + "integrity": "sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==", + "dev": true, + "license": "MIT" + }, "node_modules/qs": { "version": "6.16.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", @@ -1870,9 +1960,9 @@ } }, "node_modules/supertest": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.2.2.tgz", - "integrity": "sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==", + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.3.0.tgz", + "integrity": "sha512-UwwmWq3xLhyU96c521wYNaBg7IqX78Wpj4FKs6Katp6vdklX42zU5ESvX3KeZRLFyO44RwFp4yvxYMNZzCcX9g==", "dev": true, "license": "MIT", "dependencies": { @@ -2009,6 +2099,28 @@ } }, "dependencies": { + "@cacheable/memory": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.2.0.tgz", + "integrity": "sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==", + "dev": true, + "requires": { + "@cacheable/utils": "^2.5.0", + "@keyv/bigmap": "^1.3.1", + "hookified": "^1.15.1", + "keyv": "^5.6.0" + } + }, + "@cacheable/utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.5.0.tgz", + "integrity": "sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==", + "dev": true, + "requires": { + "hashery": "^1.5.1", + "keyv": "^5.6.0" + } + }, "@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", @@ -2055,9 +2167,9 @@ } }, "@eslint/config-helpers": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", - "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", + "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", "dev": true, "requires": { "@eslint/core": "^1.2.1" @@ -2086,9 +2198,9 @@ "dev": true }, "@eslint/plugin-kit": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", - "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.3.tgz", + "integrity": "sha512-IkO+/KEUvwbVpiURZg+P7zF74z5Jxe0UgJxVni+RtoHQ6IZieXaO02kmadomap/q+l6bc/jdPGGqTjhuZnuz1Q==", "dev": true, "requires": { "@eslint/core": "^1.2.1", @@ -2133,6 +2245,22 @@ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true }, + "@keyv/bigmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@keyv/bigmap/-/bigmap-1.3.1.tgz", + "integrity": "sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==", + "dev": true, + "requires": { + "hashery": "^1.4.0", + "hookified": "^1.15.0" + } + }, + "@keyv/serialize": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", + "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", + "dev": true + }, "@noble/hashes": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", @@ -2255,6 +2383,19 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" }, + "cacheable": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.5.0.tgz", + "integrity": "sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==", + "dev": true, + "requires": { + "@cacheable/memory": "^2.2.0", + "@cacheable/utils": "^2.5.0", + "hookified": "^1.15.0", + "keyv": "^5.6.0", + "qified": "^0.10.1" + } + }, "call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", @@ -2326,9 +2467,9 @@ } }, "dayjs": { - "version": "1.11.21", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz", - "integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==" + "version": "1.11.23", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.23.tgz", + "integrity": "sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ==" }, "debug": { "version": "4.4.3", @@ -2426,17 +2567,17 @@ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "eslint": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.6.0.tgz", - "integrity": "sha512-6lVbcqSodALYo+4ELD0heG6lFiFxnLMuLkiMi2qV8LMp54N8tE8FT1GMH+ev4Ti00nFjNze2+Su6DsV5OQW3Dg==", + "version": "10.11.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.11.0.tgz", + "integrity": "sha512-P7a6UEEqb9G95MYAtqkmsTbVXIYyzIfl6NGOIJk162PaahFxFyeGcrlXYFSiagECg4sEm8IseJdZBKR3rx6MsQ==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.5", - "@eslint/config-helpers": "^0.6.0", + "@eslint/config-helpers": "^0.7.0", "@eslint/core": "^1.2.1", - "@eslint/plugin-kit": "^0.7.2", + "@eslint/plugin-kit": "^0.7.3", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -2451,14 +2592,14 @@ "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", + "file-entry-cache": "11.1.5 || >11.1.6 <12", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "minimatch": "^10.2.4", + "minimatch": "^10.2.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, @@ -2613,12 +2754,12 @@ "dev": true }, "file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "version": "11.1.5", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-11.1.5.tgz", + "integrity": "sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q==", "dev": true, "requires": { - "flat-cache": "^4.0.0" + "flat-cache": "^6.1.23" } }, "finalhandler": { @@ -2645,19 +2786,20 @@ } }, "flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "version": "6.1.23", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.23.tgz", + "integrity": "sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA==", "dev": true, "requires": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" + "cacheable": "^2.5.0", + "flatted": "^3.4.2", + "hookified": "^1.15.0" } }, "flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", "dev": true }, "form-data": { @@ -2743,9 +2885,9 @@ } }, "globals": { - "version": "17.7.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.7.0.tgz", - "integrity": "sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==", + "version": "17.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.12.0.tgz", + "integrity": "sha512-cezEd/DTyyht9cvSSURyygXPfy04GtWO/5e6ZPvH7fCtjKz9PYOmuawphw1Ctd1f6C+5JypXfGD7ahNMXvevBA==", "dev": true }, "gopd": { @@ -2767,6 +2909,15 @@ "has-symbols": "^1.0.3" } }, + "hashery": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.5.1.tgz", + "integrity": "sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==", + "dev": true, + "requires": { + "hookified": "^1.15.0" + } + }, "hasown": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", @@ -2775,6 +2926,12 @@ "function-bind": "^1.1.2" } }, + "hookified": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz", + "integrity": "sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==", + "dev": true + }, "http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", @@ -2843,12 +3000,6 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -2862,12 +3013,12 @@ "dev": true }, "keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", + "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "dev": true, "requires": { - "json-buffer": "3.0.1" + "@keyv/serialize": "^1.1.1" } }, "levn": { @@ -3041,6 +3192,23 @@ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true }, + "qified": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/qified/-/qified-0.10.1.tgz", + "integrity": "sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==", + "dev": true, + "requires": { + "hookified": "^2.1.1" + }, + "dependencies": { + "hookified": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-2.2.0.tgz", + "integrity": "sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==", + "dev": true + } + } + }, "qs": { "version": "6.16.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", @@ -3199,9 +3367,9 @@ } }, "supertest": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.2.2.tgz", - "integrity": "sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==", + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.3.0.tgz", + "integrity": "sha512-UwwmWq3xLhyU96c521wYNaBg7IqX78Wpj4FKs6Katp6vdklX42zU5ESvX3KeZRLFyO44RwFp4yvxYMNZzCcX9g==", "dev": true, "requires": { "cookie-signature": "^1.2.2", diff --git a/package.json b/package.json index 5fd3bad..e47e96f 100644 --- a/package.json +++ b/package.json @@ -18,15 +18,15 @@ }, "packageManager": "npm@11.11.0", "dependencies": { - "dayjs": "^1.11.21", + "dayjs": "^1.11.23", "ejs": "^6.0.1", "express": "^5.2.1" }, "license": "MIT", "devDependencies": { "@eslint/js": "^10.0.1", - "eslint": "^10.6.0", - "globals": "^17.7.0", - "supertest": "^7.2.2" + "eslint": "^10.11.0", + "globals": "^17.12.0", + "supertest": "^7.3.0" } } From 6b7e1c3491319189f0cec22e9a9394bd66688d83 Mon Sep 17 00:00:00 2001 From: Jon Fairbanks Date: Tue, 29 Sep 2026 23:20:06 -0700 Subject: [PATCH 4/4] Modernize application status UI (#265) --- .../workflows/weekly-develop-to-master.yml | 140 +++++++++ AGENTS.md | 32 ++ public/css/index.css | 297 ++++++++++++++---- views/index.ejs | 130 ++++---- 4 files changed, 462 insertions(+), 137 deletions(-) create mode 100644 .github/workflows/weekly-develop-to-master.yml create mode 100644 AGENTS.md diff --git a/.github/workflows/weekly-develop-to-master.yml b/.github/workflows/weekly-develop-to-master.yml new file mode 100644 index 0000000..539d038 --- /dev/null +++ b/.github/workflows/weekly-develop-to-master.yml @@ -0,0 +1,140 @@ +name: Weekly Develop to Master + +on: + schedule: + # Match F5's Monday schedule, away from the start-of-hour hot spot. + - cron: '17 12 * * 1' + workflow_dispatch: + +permissions: + actions: read + contents: write + pull-requests: write + +concurrency: + group: weekly-develop-to-master + cancel-in-progress: false + +jobs: + promote: + name: Promote Develop and Verify Publishing + if: github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + timeout-minutes: 30 + env: + GH_REPO: ${{ github.repository }} + steps: + - name: Detect Unreleased Changes + id: changes + env: + GH_TOKEN: ${{ github.token }} + run: | + count="$(gh api "repos/$GH_REPO/compare/master...develop" --jq '.files | length')" + echo "has_changes=$([ "$count" -gt 0 ] && echo true || echo false)" >> "$GITHUB_OUTPUT" + + - name: Open and Merge Release PR + if: steps.changes.outputs.has_changes == 'true' + id: release + env: + # As in F5, this token lets PR creation and merging trigger normal checks. + GH_TOKEN: ${{ secrets.PERSONAL_TOKEN }} + run: | + if [ -z "$GH_TOKEN" ]; then + echo "::error::PERSONAL_TOKEN is required for release PR workflow events." + exit 1 + fi + # Keep strict branch protection without writing directly to develop. + behind="$(gh api "repos/$GH_REPO/compare/master...develop" --jq .behind_by)" + if [ "$behind" -gt 0 ]; then + sync_branch="feature/weekly-sync-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" + develop_sha="$(gh api "repos/$GH_REPO/commits/develop" --jq .sha)" + gh api --method POST "repos/$GH_REPO/git/refs" \ + -f "ref=refs/heads/$sync_branch" -f "sha=$develop_sha" >/dev/null + sync_sha="$(gh api --method POST "repos/$GH_REPO/merges" \ + -f "base=$sync_branch" -f head=master --jq .sha)" + sync_url="$(gh pr create --base develop --head "$sync_branch" \ + --title 'Sync Develop With Production' \ + --body '## Summary + + - Bring the production history into develop before its next release.')" + required='["Test (Node 26)","Lint and audit","Helm lint and render","Build container","Open Source and Container","Analyze JavaScript"]' + ready=false + for attempt in {1..60}; do + runs="$(gh api "repos/$GH_REPO/commits/$sync_sha/check-runs?per_page=100")" + ready="$(jq --argjson required "$required" ' + .check_runs as $runs | all($required[]; . as $name | + any($runs[]; .name == $name and .app.slug == "github-actions")) + ' <<< "$runs")" + if [ "$ready" = true ]; then break; fi + sleep 10 + done + if [ "$ready" != true ]; then + echo "Required sync checks did not start: $sync_url" >&2 + exit 1 + fi + gh pr checks "$sync_url" --required --watch --fail-fast --interval 10 + gh pr merge "$sync_url" --merge --match-head-commit "$sync_sha" + fi + pr_url="$(gh pr list --base master --head develop --state open --json url --jq '.[0].url')" + if [ -z "$pr_url" ]; then + pr_url="$(gh pr create --base master --head develop \ + --title 'Promote Develop to Production' \ + --body '## Summary + + - Promote develop to production after every required check passes.')" + fi + head_sha="$(gh pr view "$pr_url" --json headRefOid --jq .headRefOid)" + # Refresh the vulnerability data even when reusing an older release PR. + check_after="$(date -u +%Y-%m-%dT%H:%M:%SZ)" + for workflow in ci.yaml snyk.yml codeql.yml; do + gh workflow run "$workflow" --ref develop + done + required='["Test (Node 26)","Lint and audit","Helm lint and render","Build container","Open Source and Container","Analyze JavaScript"]' + missing=checks + for attempt in {1..60}; do + runs="$(gh api --paginate --slurp "repos/$GH_REPO/commits/$head_sha/check-runs?per_page=100")" + missing="$(jq -r --argjson required "$required" --arg after "$check_after" ' + [.[].check_runs[] | select(.app.slug == "github-actions")] as $runs + | $required[] as $name + | ([$runs[] | select(.name == $name)] | max_by(.id)) as $latest + | select($latest == null or $latest.status != "completed" + or $latest.conclusion != "success" or $latest.started_at < $after) + | $name + ' <<< "$runs")" + if [ -z "$missing" ]; then break; fi + echo "Waiting for fresh checks on $head_sha: $missing" + sleep 10 + done + if [ -n "$missing" ]; then + echo "Release blocked by missing or unsuccessful fresh checks: $missing" >&2 + exit 1 + fi + gh pr merge "$pr_url" --merge --auto --match-head-commit "$head_sha" + for attempt in {1..30}; do + if [ "$(gh pr view "$pr_url" --json state --jq .state)" = MERGED ]; then + echo "sha=$(gh pr view "$pr_url" --json mergeCommit --jq .mergeCommit.oid)" >> "$GITHUB_OUTPUT" + exit 0 + fi + sleep 10 + done + echo "Release PR has not merged: $pr_url" >&2 + exit 1 + + - name: Verify Gated Publishing + if: steps.changes.outputs.has_changes == 'true' + env: + GH_TOKEN: ${{ github.token }} + RELEASE_SHA: ${{ steps.release.outputs.sha }} + run: | + for attempt in {1..60}; do + run_id="$(gh run list --workflow publish.yaml --branch master --event workflow_run \ + --limit 30 --json databaseId,headSha \ + --jq ".[] | select(.headSha == \"$RELEASE_SHA\") | .databaseId" | head -n 1)" + if [ -n "$run_id" ]; then + gh run watch "$run_id" --exit-status --interval 10 + exit 0 + fi + sleep 10 + done + echo "No Publish run found for $RELEASE_SHA." >&2 + exit 1 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..f6bbe80 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,32 @@ +# Repository Guidelines + +## Project Structure & Module Organization + +This is a Kubernetes HPA validation app: sustained request traffic should scale its deployment, and the UI must clearly identify the pod handling each request. The Express application lives in `app.js`; `index.js` starts the server and owns graceful shutdown. The auto-refreshing hostname display is in `views/index.ejs`; static assets are in `public/`; tests are in `__tests__/`. Container configuration is in `Dockerfile` and `docker-compose.yaml`; the Helm chart is under `chart/`. + +## Build, Test, and Development Commands + +Use Node 26 and npm 11 (see `package.json`). Install the locked dependency set with `npm ci`. + +- `npm run dev` starts the app with file watching and the Node inspector on port 9229. +- `npm start` runs the production-style server locally. +- `npm run lint` checks all JavaScript with ESLint; `npm run lint:fix` applies safe fixes. +- `npm test` runs the built-in Node test suite; `npm run test:watch` reruns it during development. +- `docker compose up --build` starts the development image with source mounts. +- `helm lint chart` validates the bundled Helm chart. + +## Coding Style & Naming Conventions + +Use CommonJS (`require`/`module.exports`), two-space indentation, semicolons, and single quotes, matching the existing source. Keep route handlers and middleware in `app.js`; expose startup or lifecycle helpers from `index.js`. Use descriptive camelCase JavaScript names and lowercase, hyphenated Kubernetes/Helm resource names. Run ESLint before committing rather than hand-formatting around its rules. + +## Testing Guidelines + +Write tests with `node:test`, `node:assert/strict`, and Supertest where HTTP behavior is involved. Place files in `__tests__/` with a `.test.js` suffix, and name tests as observable outcomes (for example, `Health check should return app status`). Preserve coverage for pod hostname headers/UI output, health checks, and shutdown behavior. Run `npm run lint && npm test` before opening a pull request. + +## Commit & Pull Request Guidelines + +Recent history uses short, imperative subjects such as `Upgrade runtime to Node 26`, `Fix Helm chart non-root runtime`, and `Harden container runtime and CI validation`; keep that style. Keep changes focused, explain the behavioral or deployment impact in the PR description, and include screenshots for UI-visible changes. Do not commit generated dependencies, credentials, or local environment files. + +## Security & Configuration + +Treat `package-lock.json` as authoritative and use `npm ci`; do not bypass engine checks. Preserve the non-root production image and `tini` signal handling. HPA is disabled by default; test scaling with an explicit Helm values override and keep its CPU request/target settings meaningful. diff --git a/public/css/index.css b/public/css/index.css index e2fc40a..49a2a0a 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -1,90 +1,253 @@ -#body { - background-color: #DEF1F9; - font-family: 'Lato', sans-serif; +:root { + --ink: #f1f8fc; + --muted: #9eb2c1; + --line: rgba(221, 240, 250, 0.16); + --blue: #2496ed; + --navy: #071b2a; } -#container { - max-width: 500px; - margin: auto; - padding-top: 20px; +* { + box-sizing: border-box; } -#logo { - display:block; - margin:auto; - padding-bottom: 5px; +html { + background: var(--navy); } -#content { - display:block; - margin:auto; - text-align: center; +body { + min-width: 320px; + min-height: 100vh; + margin: 0; + color: var(--ink); + background: var(--navy); + font-family: 'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; } -table { - border-collapse: inherit; - border-spacing: 0 28px; - text-align: center; +.shell { + width: min(100%, 1120px); + min-height: 100vh; + margin: 0 auto; + padding: 0 48px; } -span { - font-size: 17px; +.topbar, +.service-status, +.request-details { + border-bottom: 1px solid var(--line); } -sub { - vertical-align: sub; - font-size: 12px; - font-weight: 900; - display: inline-block; - padding-top: 5px; +.topbar { + display: flex; + min-height: 76px; + align-items: center; + justify-content: space-between; } -.spinner { - margin: 2px auto; - width: 20px; - height: 20px; - position: relative; +.brand, +.repo-link { + display: inline-flex; + align-items: center; + color: var(--ink); + text-decoration: none; } -.cube1, .cube2 { - background-color: #333; - width: 7px; - height: 7px; - position: absolute; - top: 0; - left: 0; +.brand { + font-size: 14px; + font-weight: 700; + letter-spacing: -0.02em; +} + +.brand-mark { + display: grid; + width: 27px; + height: 27px; + place-items: center; + margin-right: 10px; + overflow: hidden; + border-radius: 7px; + background: #fff; +} + +.brand-mark img { + width: 20px; + height: 20px; +} + +.repo-link { + gap: 8px; + color: var(--muted); + font-family: 'DM Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, monospace; + font-size: 12px; +} + +.repo-link:hover { + color: var(--blue); +} + +.status-dot, +.pulse { + display: inline-block; + border-radius: 50%; + background: var(--blue); + box-shadow: 0 0 0 0 rgba(36, 150, 237, 0.5); + animation: ping 2s infinite; +} + +.status-dot { + width: 7px; + height: 7px; +} + +.service-status { + display: flex; + min-height: 220px; + align-items: center; + justify-content: space-between; + gap: 40px; + padding: 50px 0; +} + +.section-label { + margin: 0; + color: var(--blue); + font-family: 'DM Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, monospace; + font-size: 11px; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +h1, +p { + margin-top: 0; +} + +h1 { + margin: 10px 0; + font-size: clamp(2rem, 4.5vw, 3.6rem); + font-weight: 700; + letter-spacing: -0.065em; + line-height: 1; +} + +.health-state { + display: flex; + min-width: 185px; + align-items: center; + gap: 11px; + padding: 18px 0 18px 18px; + border-left: 1px solid var(--line); +} + +.pulse { + width: 10px; + height: 10px; + background: #32b96a; + box-shadow: 0 0 0 0 rgba(50, 185, 106, 0.5); + animation-name: health-ping; +} + +.health-state strong, +.health-state span { + display: block; +} - -webkit-animation: sk-cubemove 1.8s infinite ease-in-out; - animation: sk-cubemove 1.8s infinite ease-in-out; +.health-state strong { + font-size: 14px; } -.cube2 { - -webkit-animation-delay: -0.9s; - animation-delay: -0.9s; +.health-state span { + margin-top: 4px; + color: var(--muted); + font-family: 'DM Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, monospace; + font-size: 10px; } -@-webkit-keyframes sk-cubemove { - 25% { -webkit-transform: translateX(21px) rotate(-90deg) scale(0.5) } - 50% { -webkit-transform: translateX(21px) translateY(21px) rotate(-180deg) } - 75% { -webkit-transform: translateX(0px) translateY(21px) rotate(-270deg) scale(0.5) } - 100% { -webkit-transform: rotate(-360deg) } +.request-details { + display: grid; + grid-template-columns: 210px 1fr; + gap: 55px; + padding: 34px 0; } -@keyframes sk-cubemove { - 25% { - transform: translateX(21px) rotate(-90deg) scale(0.5); - -webkit-transform: translateX(21px) rotate(-90deg) scale(0.5); - } 50% { - transform: translateX(21px) translateY(21px) rotate(-179deg); - -webkit-transform: translateX(21px) translateY(21px) rotate(-179deg); - } 50.1% { - transform: translateX(21px) translateY(21px) rotate(-180deg); - -webkit-transform: translateX(21px) translateY(21px) rotate(-180deg); - } 75% { - transform: translateX(0px) translateY(21px) rotate(-270deg) scale(0.5); - -webkit-transform: translateX(0px) translateY(21px) rotate(-270deg) scale(0.5); - } 100% { - transform: rotate(-360deg); - -webkit-transform: rotate(-360deg); - } -} \ No newline at end of file +dl { + margin: -10px 0; +} + +.detail-row { + display: grid; + grid-template-columns: 145px 1fr; + gap: 22px; + padding: 10px 0; + border-bottom: 1px solid var(--line); +} + +.detail-row:last-child { + border-bottom: 0; +} + +dt, +dd, +footer { + font-family: 'DM Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, monospace; + font-size: 12px; +} + +dt { + color: var(--muted); +} + +dd { + min-width: 0; + margin: 0; + overflow-wrap: anywhere; + line-height: 1.45; +} + +footer { + padding: 22px 0 28px; + color: var(--muted); +} + +#refresh-countdown { + display: inline-block; + min-width: 1ch; + text-align: right; +} + +.refresh-indicator { + display: inline-block; + width: 13px; + height: 13px; + margin: 0 8px -3px 0; + border: 1px solid var(--blue); + border-left-color: transparent; + border-radius: 50%; + animation: turn 1.8s linear infinite; +} + +@keyframes turn { + to { transform: rotate(360deg); } +} + +@keyframes ping { + 0%, 100% { box-shadow: 0 0 0 0 rgba(36, 150, 237, 0.45); } + 45% { box-shadow: 0 0 0 7px rgba(36, 150, 237, 0); } +} + +@keyframes health-ping { + 0%, 100% { box-shadow: 0 0 0 0 rgba(50, 185, 106, 0.45); } + 45% { box-shadow: 0 0 0 7px rgba(50, 185, 106, 0); } +} + +@media (max-width: 680px) { + .shell { padding: 0 22px; } + .topbar { min-height: 70px; } + .service-status { min-height: 0; align-items: flex-start; flex-direction: column; gap: 28px; padding: 42px 0; } + .health-state { padding: 0; border-left: 0; } + .request-details { grid-template-columns: 1fr; gap: 30px; padding: 32px 0; } + .detail-row { grid-template-columns: 1fr; gap: 7px; padding: 13px 0; } +} + +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; } +} diff --git a/views/index.ejs b/views/index.ejs index 4ccca20..0f7a6f4 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -1,77 +1,67 @@ - + - - + + + + + + + + - docker-node-app - -
- - - -
- - - - - - - - - - - - - - - - -
- <% if (hostname) { %> - Hello from <%= hostname %>! - <% } %> -
- <% if (ip) { %> - <%= ip %> -
- Request IP - <% } %> -
- <% if (user_agent) { %> - <%= user_agent %> -
- User Agent - <% } %> -
- <% if (timestamp) { %> - <%= timestamp %> -
- Timestamp - <% } %> -
- This page will automatically reload and connect to new - hosts... -
-
-
-
-
-
-
+ +
+
+ + + docker-node-app + +
+ +
+
+ + <% if (hostname) { %> +

<%= hostname %>

+ <% } %> +
+
+ +
HealthyGET /healthz ยท 200 OK
+
+
+ +
+
+
+ <% if (ip) { %>
Client Address
<%= ip %>
<% } %> + <% if (user_agent) { %>
User Agent
<%= user_agent %>
<% } %> + <% if (timestamp) { %>
Rendered At
<%= timestamp %>
<% } %> +
+
+ +
Refreshing in 5 seconds...
+
+