Stage npm releases from a release workflow, add CI, bump reallysimpledocs to 1.0.14 (#215)

* Stage npm releases from a release workflow, add CI checks, bump reallysimpledocs to 1.0.14

* Announce releases on Discord only after the site deploys

---------

Co-authored-by: grugbraindev <178141691+grugbraindev@users.noreply.github.com>
This commit is contained in:
Grug
2026-10-23 15:03:23 +08:00
committed by GitHub
co-authored by grugbraindev
parent 67d8c40543
commit 5b11997e74
9 changed files with 414 additions and 321 deletions
+38
View File
@@ -0,0 +1,38 @@
name: CI
on:
pull_request:
branches: [main]
push:
branches: [main]
permissions:
contents: read
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v7
with:
node-version: 24
cache: npm
cache-dependency-path: |
package-lock.json
site/package-lock.json
- run: npm ci
# Package: build, check every export exists and the JS parses, list the tarball.
- run: npm run check
# Docs site: build it and check llms.txt links to the .md exports.
- run: npm run check:site
-33
View File
@@ -1,33 +0,0 @@
name: Deploy site on release
# Cloudflare Workers Builds deploys the site from the `release` branch.
# Publishing a GitHub release moves `release` to the released tag.
on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: Tag to deploy (for example 1.1.0)
required: true
type: string
permissions:
contents: write
jobs:
move-release-branch:
runs-on: ubuntu-latest
if: github.event_name == 'workflow_dispatch' || github.event.release.prerelease == false
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Point release branch at tag
env:
TAG: ${{ inputs.tag || github.event.release.tag_name }}
run: |
SHA=$(git rev-parse "refs/tags/$TAG^{commit}")
git push --force origin "$SHA:refs/heads/release"
-72
View File
@@ -1,72 +0,0 @@
name: Announce release on Discord
on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: Release tag to announce
required: true
type: string
permissions:
contents: read
jobs:
announce:
runs-on: ubuntu-latest
steps:
- name: Send Discord announcement
env:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
RELEASE_TAG_INPUT: ${{ inputs.tag }}
REPO: ${{ github.repository }}
GH_TOKEN: ${{ github.token }}
run: |
test -n "$DISCORD_WEBHOOK_URL"
if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then
test -n "$RELEASE_TAG_INPUT"
curl --fail-with-body \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GH_TOKEN" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"$GITHUB_API_URL/repos/$REPO/releases/tags/$RELEASE_TAG_INPUT" \
> release.json
else
jq '.release' "$GITHUB_EVENT_PATH" > release.json
fi
if [ "$(jq -r '.prerelease' release.json)" = "true" ]; then
echo "Skipping prerelease."
exit 0
fi
jq -n \
--arg repo "$REPO" \
--arg name "$(jq -r '.name // ""' release.json)" \
--arg tag "$(jq -r '.tag_name' release.json)" \
--arg url "$(jq -r '.html_url' release.json)" \
--arg body "$(jq -r '.body // ""' release.json)" \
'($body | if length > 1500 then .[:1500] + "...\n\n[Read more](" + $url + ")" else . end) as $description |
{
username: "Basecoat Releases",
allowed_mentions: { parse: [] },
embeds: [{
title: ($repo + " " + $tag),
url: $url,
description: ((if $name != "" then "**" + $name + "**\n\n" else "" end) + $description),
color: 5793266
}]
}' > payload.json
curl --fail-with-body \
--retry 3 \
--retry-all-errors \
--retry-delay 2 \
-H "Content-Type: application/json" \
-X POST \
-d @payload.json \
"$DISCORD_WEBHOOK_URL"
+277
View File
@@ -0,0 +1,277 @@
name: Release
# Publishing a GitHub release (not a prerelease):
# 1. publish: checks the tag is on main and matches package.json, runs the checks,
# then stages the version on npm with `npm stage publish`, using trusted publishing
# (OIDC, no token). A maintainer then approves it on npmjs.com (Staged Packages tab)
# or with `npm stage approve <stage-id>`, with 2FA. Until then the version is not
# installable.
# 2. deploy-site: waits until the version is live on npm (polls every 30 seconds for
# up to about 5h50m), then points the `release` branch at the tag. Cloudflare
# Workers Builds deploys basecoatui.com from `release`, so the docs never describe
# a version npm does not serve. If you approve later than that, re-run the failed
# jobs once approved (deploy-site, then announce).
# 3. announce: posts the release to Discord (DISCORD_WEBHOOK_URL secret), only after
# deploy-site succeeded: the version is on npm and `release` has moved.
#
# Running this workflow manually never publishes to npm. Pick a mode:
# deploy move `release` to the tag (redeploy or roll back the site),
# only if that version is already on npm. No Discord post.
# announce post the tag's GitHub release to Discord again. No deploy.
# deploy and announce both, announcing only if the deploy succeeded.
#
# Prereleases are ignored by every job.
#
# npm trusted publisher settings (allow `npm stage publish` only): repository
# hunvreus/basecoat, workflow release.yml, no environment. Renaming this file breaks
# publishing.
on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: Release tag (for example 1.1.0). Never publishes to npm.
required: true
type: string
mode:
description: "deploy: move the site to this tag (version must be on npm). announce: post the release to Discord again. deploy and announce: both."
required: true
type: choice
default: deploy
options:
- deploy
- announce
- deploy and announce
permissions: {}
concurrency:
group: release
cancel-in-progress: false
jobs:
publish:
runs-on: ubuntu-latest
if: github.event_name == 'release' && github.event.release.prerelease == false
permissions:
contents: read
id-token: write
env:
TAG: ${{ github.event.release.tag_name }}
steps:
- uses: actions/checkout@v7
with:
ref: refs/tags/${{ github.event.release.tag_name }}
persist-credentials: false
fetch-depth: 0
- name: Check the tag is on main
run: |
git fetch --no-tags origin main
if ! git merge-base --is-ancestor HEAD FETCH_HEAD; then
echo "::error::Tag $TAG is not on main. Release from a commit on main."
exit 1
fi
- uses: actions/setup-node@v7
with:
node-version: 24
registry-url: https://registry.npmjs.org
# No dependency cache in a publishing job (cache poisoning).
package-manager-cache: false
- name: Ensure npm supports staged publishing (>= 11.15.0)
run: |
if ! node -e 'const [a,b]=process.argv[1].split(".").map(Number);process.exit(a>11||(a===11&&b>=15)?0:1)' "$(npm --version)"; then
npm install -g npm@^11.15.0
fi
npm --version
- name: Check package.json version matches the tag
id: version
run: |
NAME=$(node -p 'require("./package.json").name')
VERSION=$(node -p 'require("./package.json").version')
if [ "$VERSION" != "${TAG#v}" ]; then
echo "::error::package.json version is $VERSION but the release tag is $TAG. Bump package.json and tag that commit."
exit 1
fi
echo "name=$NAME" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Check whether this version is already on npm
id: npm
env:
NAME: ${{ steps.version.outputs.name }}
VERSION: ${{ steps.version.outputs.version }}
run: |
STATUS=$(curl -sS -o /dev/null -w '%{http_code}' "https://registry.npmjs.org/$NAME/$VERSION")
case "$STATUS" in
200)
echo "::notice::$NAME@$VERSION is already on npm. Skipping publish."
echo "published=true" >> "$GITHUB_OUTPUT" ;;
404)
echo "published=false" >> "$GITHUB_OUTPUT" ;;
*)
echo "::error::Unexpected HTTP $STATUS from the npm registry."
exit 1 ;;
esac
- name: Install dependencies
if: steps.npm.outputs.published == 'false'
run: npm ci
# Builds dist/ and templates/, checks every export exists, and lists the tarball.
# `npm stage publish` builds again through the prepack script.
- name: Run checks
if: steps.npm.outputs.published == 'false'
run: npm run check
# Short-lived OIDC tokens cannot run `npm stage list`, so an already-staged version
# is detected from the registry's conflict error instead: staged and published
# versions share one version index, so re-staging the same version is refused.
- name: Stage on npm
if: steps.npm.outputs.published == 'false'
run: |
set +e
npm stage publish --provenance 2>&1 | tee "$RUNNER_TEMP/stage.log"
STATUS=${PIPESTATUS[0]}
set -e
if [ "$STATUS" -eq 0 ]; then
echo "::notice::Staged. Approve it on npmjs.com (Staged Packages tab) or with npm stage approve, using 2FA. The site deploys once it is live."
elif grep -Eqi 'E409|EPUBLISHCONFLICT|previously published|already (been )?(staged|published|exists)|cannot (publish|stage) over' "$RUNNER_TEMP/stage.log"; then
echo "::notice::This version is already staged or published on npm. Skipping."
else
exit "$STATUS"
fi
deploy-site:
runs-on: ubuntu-latest
needs: publish
if: >-
always() &&
(needs.publish.result == 'success' || needs.publish.result == 'skipped') &&
((github.event_name == 'release' && github.event.release.prerelease == false) ||
(github.event_name == 'workflow_dispatch' && inputs.mode != 'announce'))
# GitHub-hosted jobs stop at 6 hours. The wait below gives up before that.
timeout-minutes: 355
permissions:
contents: write
env:
TAG: ${{ inputs.tag || github.event.release.tag_name }}
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Check the tag is on main
id: tag
run: |
SHA=$(git rev-parse --verify "refs/tags/$TAG^{commit}")
if ! git merge-base --is-ancestor "$SHA" origin/main; then
echo "::error::Tag $TAG is not on main. Only tags on main can go live."
exit 1
fi
{
echo "sha=$SHA"
echo "name=$(git show "$SHA:package.json" | jq -r .name)"
echo "version=$(git show "$SHA:package.json" | jq -r .version)"
} >> "$GITHUB_OUTPUT"
- name: Wait until the version is live on npm
env:
NAME: ${{ steps.tag.outputs.name }}
VERSION: ${{ steps.tag.outputs.version }}
# Manual runs do not wait: the version must already be on npm.
MAX_WAIT: ${{ github.event_name == 'release' && '21000' || '0' }}
run: |
while :; do
STATUS=$(curl -sS -o /dev/null -w '%{http_code}' "https://registry.npmjs.org/$NAME/$VERSION" || echo 000)
if [ "$STATUS" = "200" ]; then
echo "$NAME@$VERSION is live on npm."
exit 0
fi
if [ "$SECONDS" -ge "$MAX_WAIT" ]; then
if [ "$MAX_WAIT" = "0" ]; then
echo "::error::$NAME@$VERSION is not on npm (HTTP $STATUS). A manual run only deploys versions that are already published."
else
echo "::error::$NAME@$VERSION is still not live on npm after $((MAX_WAIT / 60)) minutes. Approve the staged package on npmjs.com (Staged Packages tab), then re-run this deploy-site job (Re-run failed jobs). If you rejected it, nothing needs to happen: the site was not deployed."
fi
exit 1
fi
echo "Waiting for $NAME@$VERSION on npm (HTTP $STATUS). Approve the staged package on npmjs.com. Checking again in 30 seconds."
sleep 30
done
- name: Point release branch at tag
env:
SHA: ${{ steps.tag.outputs.sha }}
run: git push --force origin "$SHA:refs/heads/release"
# Same message as the old discord-release.yml.
announce:
runs-on: ubuntu-latest
needs: deploy-site
if: >-
always() &&
((needs.deploy-site.result == 'success' &&
((github.event_name == 'release' && github.event.release.prerelease == false) ||
(github.event_name == 'workflow_dispatch' && inputs.mode == 'deploy and announce'))) ||
(github.event_name == 'workflow_dispatch' && inputs.mode == 'announce'))
permissions:
contents: read
steps:
- name: Send Discord announcement
env:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
RELEASE_TAG_INPUT: ${{ inputs.tag }}
REPO: ${{ github.repository }}
GH_TOKEN: ${{ github.token }}
run: |
test -n "$DISCORD_WEBHOOK_URL"
if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then
test -n "$RELEASE_TAG_INPUT"
curl --fail-with-body \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GH_TOKEN" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"$GITHUB_API_URL/repos/$REPO/releases/tags/$RELEASE_TAG_INPUT" \
> release.json
else
jq '.release' "$GITHUB_EVENT_PATH" > release.json
fi
if [ "$(jq -r '.prerelease' release.json)" = "true" ]; then
echo "Skipping prerelease."
exit 0
fi
jq -n \
--arg repo "$REPO" \
--arg name "$(jq -r '.name // ""' release.json)" \
--arg tag "$(jq -r '.tag_name' release.json)" \
--arg url "$(jq -r '.html_url' release.json)" \
--arg body "$(jq -r '.body // ""' release.json)" \
'($body | if length > 1500 then .[:1500] + "...\n\n[Read more](" + $url + ")" else . end) as $description |
{
username: "Basecoat Releases",
allowed_mentions: { parse: [] },
embeds: [{
title: ($repo + " " + $tag),
url: $url,
description: ((if $name != "" then "**" + $name + "**\n\n" else "" end) + $description),
color: 5793266
}]
}' > payload.json
curl --fail-with-body \
--retry 3 \
--retry-all-errors \
--retry-delay 2 \
-H "Content-Type: application/json" \
-X POST \
-d @payload.json \
"$DISCORD_WEBHOOK_URL"
+26 -12
View File
@@ -2,13 +2,14 @@
- Support development by sponsoring the author: https://github.com/sponsors/hunvreus
- Submit pull requests (PRs) against the `main` branch.
- For branches, we have:
- `main` is our production and default branch. This is what we deploy to https://basecoatui.com and publish to https://www.npmjs.com/package/basecoat-css.
- Branches:
- `main` is the default branch. All work lands here, and https://main-basecoat.hunvreus.workers.dev previews it.
- `release` is what https://basecoatui.com deploys. Only the release workflow moves it (see below).
- New features are worked in `feature/name-of-the-feature` branches.
- Issues are addressed in `issue/123-main-issue` branches.
- When ready, open a PR against `main`.
- Keep changes focused: one feature or fix per PR.
- Test locally before submitting.
- Test locally before submitting: `npm run check` (package) and `npm run check:site` (docs site). CI runs both on every PR.
- Follow existing code style.
## Cloudflare Workers deploys
@@ -24,19 +25,32 @@ Workers Builds settings:
- Build command: `npm run docs:build`
- Deploy command: `npx wrangler deploy`
- Non-production branch deploy command: `npx wrangler versions upload`
- Production branch: `main`
- Production branch: `release`
- Enable builds for non-production branches for branch preview URLs.
Thanks for helping!
## Releases and the docs site
## Releasing
The site at basecoatui.com deploys from the `release` branch, not `main`, so the docs always match the version on npm. Nobody commits to `release` directly.
To release:
To cut a release:
1. Bump the version in `package.json`, commit, tag (for example `1.1.0`) and push.
2. Run `npm publish`.
3. Publish a GitHub release for the tag, for example `gh release create 1.1.0 --generate-notes`.
1. Bump the version on `main`: `npm version 1.1.0 --no-git-tag-version`, then commit (for example "Prepare 1.1.0 release") and push.
2. Tag that commit with the same version, with no `v` prefix, and push: `git tag 1.1.0 && git push origin 1.1.0`.
3. Publish a GitHub release for the tag: `gh release create 1.1.0 --generate-notes --latest --verify-tag`.
4. Approve the staged version on npmjs.com, in the package's Staged Packages tab, or with `npm stage approve <stage-id>`. Both ask for 2FA.
Publishing the release runs `.github/workflows/deploy-site.yml`, which points `release` at the tag. Cloudflare then builds and deploys the site. To redeploy or roll back, run that workflow manually with the tag you want live.
Publishing the release runs `.github/workflows/release.yml`:
- **publish** checks that the tag is on `main` and matches the version in `package.json`, runs `npm run check`, and stages the version on npm with `npm stage publish`, using trusted publishing. A staged version is not installable until a maintainer approves it. If the version is already staged or published, this step is skipped.
- **deploy-site** waits until the version is live on npm, then points `release` at the tag, and Cloudflare builds and deploys the site. It checks every 30 seconds for up to about 5h50m. If you approve later than that, the job fails with a message: after approving, use Re-run failed jobs on that run, which re-runs deploy-site and then announce. If staging fails or you reject the version, the site is not deployed.
- **announce** posts the release notes to Discord, only after deploy-site succeeded. By then the version is installable from npm and `release` has moved, so Cloudflare is building the new site (that takes a few minutes).
Do not run `npm publish` by hand. Versions reach npm only through this workflow and your approval. The workflow needs no npm token and adds a provenance statement linking each version to the commit it was built from. Prereleases are not staged, deployed or announced.
You can also run the Release workflow manually from the Actions tab, with a tag and a mode. A manual run never publishes to npm.
- `deploy` (default) redeploys or rolls back the site: it moves `release` to the tag, only if that version is already on npm. It doesn't post to Discord.
- `announce` posts the tag's GitHub release to Discord again, for example after a failed post. It doesn't deploy.
- `deploy and announce` does both, and announces only if the deploy succeeded.
Thanks for helping!
+2
View File
@@ -123,6 +123,8 @@
"scripts": {
"build": "node ./scripts/build.js",
"prepack": "node ./scripts/build.js",
"check": "npm run build && node ./scripts/check.js && npm pack --dry-run --ignore-scripts",
"check:site": "npm run docs:build && node ./scripts/check.js site",
"docs:build": "npm run build && npm --prefix site clean-install --progress=false && npm --prefix site run build",
"docs:dev": "npm --prefix site run dev",
"docs:preview": "npm run docs:build && npm --prefix site run preview",
+65
View File
@@ -0,0 +1,65 @@
// Cheap sanity checks. Run after a build.
// node scripts/check.js package: every export target exists, JS parses
// node scripts/check.js site docs site: site/dist built, llms.txt links to .md exports
import fs from 'fs';
import path from 'path';
import { execFileSync } from 'child_process';
import { fileURLToPath } from 'url';
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const errors = [];
function checkPackage() {
const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
for (const [key, target] of Object.entries(pkg.exports)) {
const file = path.join(root, target);
if (target.includes('*')) {
const dir = path.dirname(file.slice(0, file.indexOf('*')) + 'x');
if (!fs.existsSync(dir) || fs.readdirSync(dir).length === 0) {
errors.push(`export "${key}": no files in ${path.relative(root, dir)}`);
}
} else if (!fs.existsSync(file) || fs.statSync(file).size === 0) {
errors.push(`export "${key}": ${target} is missing or empty`);
}
}
const jsDir = path.join(root, 'dist', 'js');
for (const name of fs.existsSync(jsDir) ? fs.readdirSync(jsDir) : []) {
if (!name.endsWith('.js')) continue;
try {
execFileSync(process.execPath, ['--check', path.join(jsDir, name)], { stdio: 'pipe' });
} catch (error) {
const message = error.stderr.toString().split('\n').find((line) => /Error/.test(line));
errors.push(`dist/js/${name} does not parse: ${message || 'syntax error'}`);
}
}
}
function checkSite() {
const dist = path.join(root, 'site', 'dist');
for (const file of ['index.html', 'llms.txt', 'llms-full.txt']) {
if (!fs.existsSync(path.join(dist, file))) errors.push(`site/dist/${file} is missing`);
}
const llms = path.join(dist, 'llms.txt');
if (!fs.existsSync(llms)) return;
const links = [...fs.readFileSync(llms, 'utf8').matchAll(/^- \[[^\]]*\]\(([^)]+)\)/gm)].map((m) => m[1]);
if (links.length === 0) errors.push('site/dist/llms.txt has no links');
for (const link of links) {
if (!link.endsWith('.md')) errors.push(`site/dist/llms.txt links to ${link}, not a .md export`);
}
}
const target = process.argv[2] || 'package';
if (target === 'package') checkPackage();
else if (target === 'site') checkSite();
else {
console.error(`Unknown target "${target}". Use "package" or "site".`);
process.exit(2);
}
if (errors.length) {
console.error(`${target} check failed:\n- ${errors.join('\n- ')}`);
process.exit(1);
}
console.log(`${target} check passed.`);
+5 -203
View File
@@ -10,7 +10,7 @@
"htmx.org": "^2.0.10",
"lucide-static": "^1.17.0",
"nunjucks": "^3.2.4",
"reallysimpledocs": "^1.0.12"
"reallysimpledocs": "^1.0.14"
},
"devDependencies": {
"astro": "^6.4.6",
@@ -19,7 +19,7 @@
},
"..": {
"name": "basecoat-css",
"version": "1.0.1",
"version": "1.0.2",
"license": "MIT",
"devDependencies": {
"@tailwindcss/cli": "^4.1.17",
@@ -812,7 +812,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -835,7 +834,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -858,7 +856,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -875,7 +872,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -892,7 +888,6 @@
"cpu": [
"arm"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -909,7 +904,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -926,7 +920,6 @@
"cpu": [
"ppc64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -943,7 +936,6 @@
"cpu": [
"riscv64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -960,7 +952,6 @@
"cpu": [
"s390x"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -977,7 +968,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -994,7 +984,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1011,7 +1000,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1028,7 +1016,6 @@
"cpu": [
"arm"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1051,7 +1038,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1074,7 +1060,6 @@
"cpu": [
"ppc64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1097,7 +1082,6 @@
"cpu": [
"riscv64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1120,7 +1104,6 @@
"cpu": [
"s390x"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1143,7 +1126,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1166,7 +1148,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1189,7 +1170,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0",
"optional": true,
"os": [
@@ -1212,7 +1192,6 @@
"cpu": [
"wasm32"
],
"dev": true,
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
"optional": true,
"dependencies": {
@@ -1232,7 +1211,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "Apache-2.0 AND LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1252,7 +1230,6 @@
"cpu": [
"ia32"
],
"dev": true,
"license": "Apache-2.0 AND LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -1272,7 +1249,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "Apache-2.0 AND LGPL-3.0-or-later",
"optional": true,
"os": [
@@ -2439,20 +2415,6 @@
"resolved": "..",
"link": true
},
"node_modules/binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/blake3-wasm": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz",
@@ -2466,20 +2428,6 @@
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
"license": "ISC"
},
"node_modules/braces": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/ccount": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
@@ -2530,32 +2478,6 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/chokidar": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.6.0"
},
"engines": {
"node": ">= 8.10.0"
},
"funding": {
"url": "https://paulmillr.com/funding/"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/ci-info": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz",
@@ -3160,20 +3082,6 @@
}
}
},
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"to-regex-range": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/flattie": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz",
@@ -3239,20 +3147,6 @@
"integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==",
"license": "ISC"
},
"node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"license": "ISC",
"optional": true,
"peer": true,
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
@@ -3575,20 +3469,6 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"binary-extensions": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/is-decimal": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
@@ -3614,31 +3494,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"is-extglob": "^2.1.1"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-hexadecimal": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz",
@@ -3682,17 +3537,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=0.12.0"
}
},
"node_modules/is-plain-obj": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
@@ -5502,38 +5346,10 @@
"integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==",
"license": "MIT"
},
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"picomatch": "^2.2.1"
},
"engines": {
"node": ">=8.10.0"
}
},
"node_modules/readdirp/node_modules/picomatch": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
"license": "MIT",
"optional": true,
"peer": true,
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/reallysimpledocs": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/reallysimpledocs/-/reallysimpledocs-1.0.12.tgz",
"integrity": "sha512-nZIvrHdDcr2TImpT3TlhjLb3420xikeyXUqOuuvfosuEPgbfTYvnfg9MpgLvOciQLCjsuSGdpQzNrdW5MTVurQ==",
"version": "1.0.14",
"resolved": "https://registry.npmjs.org/reallysimpledocs/-/reallysimpledocs-1.0.14.tgz",
"integrity": "sha512-TRiCxZZ3j6+AacD3HzeYr1+6soUdJN3ZkNtrdYON2OtiqlLc5qb0GHV1c4k+49l9z4/20FQ0qGH2r0YXoWwOQg==",
"license": "MIT",
"dependencies": {
"@astrojs/markdown-remark": "^7.2.0",
@@ -6231,20 +6047,6 @@
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"license": "MIT",
"optional": true,
"peer": true,
"dependencies": {
"is-number": "^7.0.0"
},
"engines": {
"node": ">=8.0"
}
},
"node_modules/trim-lines": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
+1 -1
View File
@@ -15,7 +15,7 @@
"htmx.org": "^2.0.10",
"lucide-static": "^1.17.0",
"nunjucks": "^3.2.4",
"reallysimpledocs": "^1.0.12"
"reallysimpledocs": "^1.0.14"
},
"devDependencies": {
"astro": "^6.4.6",