diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..1014ba7 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..95f03ba --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,13 @@ +version: 2 +updates: + - package-ecosystem: npm + directory: / + schedule: + interval: monthly + groups: + astro: + patterns: ["astro", "@astrojs/*"] + - package-ecosystem: github-actions + directory: / + schedule: + interval: monthly diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..6dbdc85 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,45 @@ +name: CI + +on: + push: + branches: [dev] + pull_request: + branches: [dev, main] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ci-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + quality: + name: Build and verify + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Check out repository + uses: actions/checkout@v7 + - name: Set up Node.js + uses: actions/setup-node@v7 + with: + node-version: 24 + cache: npm + - name: Install dependencies + run: npm ci + - name: Verify production promotion source + if: github.base_ref == 'main' && github.head_ref != 'dev' + run: | + echo "::error::Only the dev branch may be promoted to main." + exit 1 + - name: Check formatting + run: npm run format:check + - name: Type-check and test production build + run: npm run check + - name: Audit performance, accessibility and SEO + uses: treosh/lighthouse-ci-action@v12 + with: + configPath: ./lighthouserc.json + uploadArtifacts: true diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..5830bfd --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,57 @@ +name: Deploy to Cloudflare Pages + +on: + push: + branches: [dev, main] + workflow_dispatch: + +permissions: + contents: read + deployments: write + +concurrency: + group: cloudflare-${{ github.ref }} + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} + +jobs: + deploy: + name: Verify and deploy + runs-on: ubuntu-latest + timeout-minutes: 15 + environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'preview' }} + steps: + - name: Check out repository + uses: actions/checkout@v7 + - name: Set up Node.js + uses: actions/setup-node@v7 + with: + node-version: 24 + cache: npm + - name: Install dependencies + run: npm ci + - name: Verify production build + env: + CLOUDFLARE_WEB_ANALYTICS_TOKEN: ${{ vars.CLOUDFLARE_WEB_ANALYTICS_TOKEN }} + run: npm run check + - name: Record deployment identity + run: node scripts/write-deployment-metadata.mjs dist "$GITHUB_SHA" "$GITHUB_REF_NAME" + - name: Deploy branch to Cloudflare Pages + id: deploy + uses: cloudflare/wrangler-action@v4 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + wranglerVersion: "4" + command: pages deploy dist --project-name=${{ vars.CLOUDFLARE_PAGES_PROJECT || 'ldsa-website' }} --branch=${{ github.ref_name }} --commit-hash=${{ github.sha }} + gitHubToken: ${{ secrets.GITHUB_TOKEN }} + - name: Verify deployed site + env: + DEPLOYMENT_URL: ${{ steps.deploy.outputs.deployment-url }} + DEPLOYMENT_ALIAS_URL: ${{ steps.deploy.outputs.pages-deployment-alias-url }} + EXPECTED_COMMIT: ${{ github.sha }} + EXPECTED_BRANCH: ${{ github.ref_name }} + EXPECT_NOINDEX: ${{ github.ref != 'refs/heads/main' }} + EXPECT_ANALYTICS: ${{ vars.CLOUDFLARE_WEB_ANALYTICS_TOKEN != '' }} + CF_ACCESS_CLIENT_ID: ${{ secrets.CF_ACCESS_CLIENT_ID }} + CF_ACCESS_CLIENT_SECRET: ${{ secrets.CF_ACCESS_CLIENT_SECRET }} + run: npm run check:deployment diff --git a/.gitignore b/.gitignore index 1301e01..2cbd17e 100644 --- a/.gitignore +++ b/.gitignore @@ -147,3 +147,9 @@ vite.config.ts.timestamp-* # Turbo Repo .turbo + +# Cloudflare local state and platform files +.wrangler/ + +# OS metadata +.DS_Store diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..a45fd52 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +24 diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..739188c --- /dev/null +++ b/.prettierignore @@ -0,0 +1,5 @@ +dist +node_modules +migration/raw +package-lock.json +public/images diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..2686032 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,14 @@ +# Contributing + +Thank you for helping maintain the LDSA website. + +1. Branch from `dev` using a name such as `feature/update-prep-course`. +2. Keep time-sensitive operational details in the LDSA wiki and link to them from the site. +3. Run `npm ci`, `npm run check` and `npm run format:check`. +4. Open a pull request into `dev` and explain the visible change and how it was checked. +5. Test the merged result on the protected Cloudflare `dev` deployment. +6. Promote tested changes with a pull request from `dev` to `main`. + +Do not put subscriber exports, application data, API tokens or other secrets in the repository. + +When changing a Flodesk form, preserve the form ID, field names and preference values unless the matching Flodesk configuration has also changed. Complete the account-level submission checks in `docs/operations/qa.md` before production promotion. diff --git a/README.md b/README.md index 0ca8cc1..45cee09 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,89 @@ -# website -LDSA website +# LDSA Website + +The official website of the [Lisbon Data Science Academy](https://www.lisbondatascience.org/). It is a static [Astro](https://astro.build/) site designed for Cloudflare Pages and maintained through the `feature/* → dev → main` workflow. + +The hosting and delivery architecture uses free tooling and service tiers. Domain registration and renewal are the only expected website infrastructure cost. + +- Production: `https://www.lisbondatascience.org/` (kept on Unbounce until the migration acceptance tests pass) +- Candidate production: `https://ldsa-website.pages.dev/` +- Integration preview: `https://dev.ldsa-website.pages.dev/` + +## Local development + +Use Node.js 24 or newer: + +```bash +npm ci +npm run dev +``` + +The local server prints its URL, normally `http://localhost:4321/`. + +Before opening a pull request, run: + +```bash +npm run check +npm run format:check +``` + +`npm run check` type-checks the Astro source, builds the production site, validates internal links and local assets, rejects Unbounce dependencies, verifies the three preserved Flodesk endpoints, and confirms Cloudflare deployment files and the sitemap are present. After each Cloudflare deployment, GitHub Actions also checks the live routes, redirects, 404 page, security and indexing headers, sitemap, and deployed commit identity. + +## Branches and deployment + +- `feature/*`: one focused change; open a pull request into `dev`. +- `dev`: integration branch and protected Cloudflare preview. +- `main`: production branch. Merging here publishes the production build. + +Pull requests into `dev` or `main` run formatting, type checks, a clean production build, site contract checks and Lighthouse audits. Feature branches never deploy to Cloudflare. A push to `dev` deploys the private development preview, while a push to `main` deploys production through the Cloudflare Wrangler GitHub Action. + +The deployment workflow requires: + +- GitHub secret `CLOUDFLARE_API_TOKEN`, scoped to edit Cloudflare Pages. +- GitHub secret `CLOUDFLARE_ACCOUNT_ID`. +- Optional GitHub variable `CLOUDFLARE_PAGES_PROJECT`; it defaults to `ldsa-website`. +- Production-environment variable `CLOUDFLARE_WEB_ANALYTICS_TOKEN` after the final public hostname is registered in Cloudflare Web Analytics. Preview builds deliberately omit it. + +See [the Cloudflare runbook](docs/operations/cloudflare.md) for project creation, preview access, analytics and domain cutover. + +## Repository structure + +```text +src/ +├── components/ shared navigation, footer, FAQ and Flodesk forms +├── data/ navigation, links and migrated content data +├── layouts/ metadata and global page shell +├── pages/ public routes +└── styles/ design system and responsive styles +public/ assets and Cloudflare Pages configuration +scripts/ build-time contract checks +migration/ +├── raw/ public rendered-source snapshot from Unbounce +├── scripts/ migration audit helpers +└── *.md public-evidence audit, inventories and decisions +docs/operations/ deployment, DNS, QA and maintenance runbooks +.github/workflows/ CI and deployment automation +``` + +## Updating content + +Most page copy lives in `src/pages`. Shared links, FAQs and the preserved volunteer roster live in `src/data/site.ts`. Keep time-sensitive dates in the LDSA wiki where possible and link to that source of truth instead of duplicating values that will become stale. + +Images in `public/images` are locally hosted copies of LDSA assets recovered from the public Unbounce site. Add descriptive `alt` text for meaningful images and use an empty `alt` only for decorative images. + +## Flodesk forms + +`src/components/FlodeskForm.astro` preserves the three production Flodesk form IDs and submission endpoints: + +- General interest: `63f9e4e2f0116a4cabfef7db` +- Starters Academy: `615c9641c71e8685f183bd18` +- Prep Course: `6172e62fe625581864e3f857` + +The homepage also preserves the `option1` and `option2` interest values used by the current form. Do not replace these IDs or option values without updating the form in Flodesk and completing the end-to-end tests in [the QA runbook](docs/operations/qa.md). + +## DNS and production safety + +The selected cutover keeps authoritative DNS at Namecheap: `www` will become a CNAME to Pages and the apex will use Namecheap's permanent URL redirect to `www`. Email-related MX and TXT records remain untouched. The captured baseline, exact cutover and rollback sequence are in [the DNS runbook](docs/operations/dns.md). + +## Contributing + +Create a branch from `dev`, make a focused change, run the local checks, then open a pull request into `dev`. After the combined changes pass on the protected Cloudflare preview, open a pull request from `dev` into `main`. diff --git a/astro.config.mjs b/astro.config.mjs new file mode 100644 index 0000000..51e9373 --- /dev/null +++ b/astro.config.mjs @@ -0,0 +1,12 @@ +import { defineConfig } from "astro/config"; +import sitemap from "@astrojs/sitemap"; + +export default defineConfig({ + site: "https://www.lisbondatascience.org", + output: "static", + trailingSlash: "always", + integrations: [sitemap()], + build: { + format: "directory", + }, +}); diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..f18e87c --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,49 @@ +# Website architecture + +## System shape + +```text +GitHub repository + ├─ feature/* pull requests ── CI ──> dev + ├─ dev ── verified GitHub Action ──> protected Cloudflare preview + └─ main ── verified GitHub Action ──> Cloudflare production + │ + ├─ static HTML, CSS and images + ├─ Flodesk form submissions + ├─ LDSA Portal and Wiki links + └─ privacy-enhanced YouTube embed +``` + +The site is generated at build time by Astro and deployed as static files. There is no application server, database, runtime JavaScript framework or server-side session. Cloudflare provides TLS, CDN caching, preview deployments, Access control, deployment history and production Web Analytics. + +All migration infrastructure uses free services and open-source tooling: Astro, Node.js, npm, GitHub Actions within the repository's included allowance, Cloudflare Pages, Cloudflare Web Analytics, and Cloudflare Access Free for the small LDSA testing group. The domain registration/renewal remains the only unavoidable website infrastructure cost. Do not add a paid hosting, build, monitoring, analytics, testing, or CDN dependency without an explicit architecture decision. + +## Why Astro + +Astro provides reusable components and typed content without shipping a client framework to visitors. Header, footer, metadata, FAQs and Flodesk markup have one maintained implementation. Pages remain ordinary semantic HTML in the build output, which keeps performance high and reduces the maintenance surface for volunteers. + +## Why there is no Docker image + +The deployable artifact is the deterministic `dist` directory. GitHub-hosted runners and Cloudflare Pages only need the locked Node.js toolchain to create it. A Docker image would add another dependency and patching surface without improving runtime parity because there is no container at runtime. The `.nvmrc`, `engines` field and `package-lock.json` already pin the relevant build environment. + +## Content ownership + +- Stable marketing and explanatory content lives in version-controlled Astro pages. +- Shared navigation, official links, FAQs and roster data live in `src/data/site.ts`. +- Batch dates, admissions rules and other frequently changing details live in the LDSA wiki and are linked from the website. +- Subscriber records and email automations remain in Flodesk. +- Applications remain in the LDSA Portal. + +This separation prevents the website from becoming a second, stale source of truth for operational course details. + +## Integration boundaries + +Flodesk form identifiers, endpoints, preference values, configuration and runtime bundles are treated as public integration contracts and covered by the production-build test. A successful browser submission still requires Flodesk account verification because only Flodesk can prove which subscriber group and automation received the address. + +External destinations use normal links. The YouTube video uses the privacy-enhanced domain and lazy loading. Cloudflare's project-wide automatic analytics injection is disabled because it would mix private preview traffic into production data. The layout emits the free Web Analytics beacon only when the production GitHub environment supplies `CLOUDFLARE_WEB_ANALYTICS_TOKEN`; the build adds the matching CSP origins at the same time. Preview and ordinary local builds contain neither the beacon nor its CSP permissions. + +## Security and privacy + +Cloudflare `_headers` set a restrictive Content Security Policy, disable framing, narrow browser capabilities and prevent MIME sniffing. The policy allows only the Flodesk and YouTube origins needed by current features. Deployment credentials live in GitHub Secrets and are limited to Pages edit access. Preview access will be enforced by Cloudflare Access after the dashboard policy and CI service token in the Cloudflare runbook are configured. + +The repository must never contain Unbounce lead exports, subscriber lists, Cloudflare tokens or other personal/secret data. diff --git a/docs/operations/cloudflare.md b/docs/operations/cloudflare.md new file mode 100644 index 0000000..3f9c193 --- /dev/null +++ b/docs/operations/cloudflare.md @@ -0,0 +1,89 @@ +# Cloudflare Pages runbook + +The repository uses GitHub Actions and Wrangler Direct Upload. This keeps the build and deployment checks in one auditable workflow instead of asking Cloudflare and GitHub to build the same commit independently. + +The project must remain on free infrastructure. Cloudflare Pages serves this static site without Pages Functions, paid Workers, R2, or another billable runtime. Preview authentication uses the Cloudflare Zero Trust Free plan, which currently supports up to 50 users. Confirm that the dashboard still shows the Free plan whenever enabling a new Cloudflare product. + +## 1. Create the Pages project + +In the LDSA Cloudflare account, create a Pages project named `ldsa-website` using Direct Upload. Do not attach the production domain yet. + +If Wrangler is authenticated locally, the equivalent command is: + +```bash +npx wrangler pages project create ldsa-website --production-branch main +``` + +The build output is `dist`. The GitHub deployment workflow sends only `dev` and `main` with their real branch names. The two operational environments are: + +- development: `dev`, at `https://dev.ldsa-website.pages.dev/`, protected by Cloudflare Access; +- production: `main`, at `https://ldsa-website.pages.dev/` and, after cutover, the public custom domain. + +Feature branches never deploy to Cloudflare. Their changes reach the development environment only after a pull request is merged into `dev`. + +After uploading, the workflow tests the immutable deployment URL and confirms its routes, redirects, custom 404 page, security headers, indexing policy, branch and commit. It records the branch and commit in a non-cached `deployment.json` file generated only in CI. + +## 2. Create the deployment token + +Create a Cloudflare API token limited to the LDSA account with `Cloudflare Pages: Edit`. Add these GitHub Actions secrets: + +- `CLOUDFLARE_API_TOKEN` +- `CLOUDFLARE_ACCOUNT_ID` + +Optionally set the repository variable `CLOUDFLARE_PAGES_PROJECT` if the project name is not `ldsa-website`. + +Never commit tokens or put them in shell history. The workflow passes them directly from GitHub Secrets to the official Cloudflare action. + +## 3. Protect preview deployments + +In Cloudflare Zero Trust, protect preview deployments with Access. Complete the service-token steps first so GitHub Actions can continue checking the protected deployment: + +1. Open **Zero Trust → Access controls → Service credentials → Service Tokens**. +2. Create `github-actions-ldsa-preview-smoke`, choose an appropriate expiry, and copy its Client ID and Client Secret once. +3. In GitHub repository Actions secrets, add the values as `CF_ACCESS_CLIENT_ID` and `CF_ACCESS_CLIENT_SECRET`. +4. Open **Workers & Pages → ldsa-website → Settings → General**. If **Preview access** says deployments are restricted by a Cloudflare Access policy, protection is already enabled; select **Manage**. Otherwise, select **Enable access policy**. +5. In the generated `ldsa-website - Cloudflare Pages` application, keep the destination `*.ldsa-website.pages.dev`. Edit the interactive Allow policy so its Include rule contains only exact approved tester email addresses, or `Emails ending in @lisbondatascience.org` if every mailbox on that domain should have access. Remove `All authenticated users` or `Everyone`. +6. Add a second policy with action **Service Auth**, include only the `github-actions-ldsa-preview-smoke` service token, and save it. New Zero Trust organizations use strict service-token authentication, so an ordinary Allow policy is insufficient for CI. +7. Confirm the policy wildcard covers preview aliases but does not cover `ldsa-website.pages.dev` or a future production custom domain. +8. Re-run the `dev` deployment workflow and confirm the post-deployment contract check passes. +9. Open the preview in a signed-out private browser and confirm Access blocks it, then sign in as an approved tester and confirm it opens. + +The checker sends the service credentials only in Cloudflare Access request headers. Never put either value in repository variables, source files, logs, or chat. Record the token expiry and rotate the GitHub secrets before it expires. + +Cloudflare preview deployments should also send `X-Robots-Tag: noindex`. Verify the live response header before sharing the preview URL. + +## 4. Production-only observability + +Keep the Pages project's automatic **Web Analytics** switch disabled. It applies to both `dev` and `main`, which would mix private acceptance traffic into production results. + +After `www.lisbondatascience.org` is attached and selected as the canonical production hostname: + +1. In Cloudflare **Web Analytics**, create or select the site for `www.lisbondatascience.org` using the free service. +2. Copy its public site token. Do not use an API token or Access credential. +3. In **GitHub → LDSSA/website → Settings → Environments → production → Environment variables**, add `CLOUDFLARE_WEB_ANALYTICS_TOKEN` with that site token. +4. Re-run the `main` deployment. The Astro build emits the manual beacon and adds `static.cloudflareinsights.com` and `cloudflareinsights.com` only to the production artifact's CSP. +5. Confirm production loads `beacon.min.js`, sends a successful RUM request and records the controlled visit. +6. Confirm `dev` contains no beacon, no RUM request and no analytics CSP origins. + +The token is a public site identifier embedded in HTML, but keeping it in the production environment prevents accidental preview activation and centralizes configuration. Do not add visitor profiling scripts without a separate review. + +Use the Cloudflare dashboard to monitor: + +- deployment history and failures; +- Web Analytics and Core Web Vitals; +- 404 requests after cutover; +- bandwidth and cache status; +- security events. + +## 5. GitHub environments + +Create two GitHub environments: + +- `preview`: no approval requirement; restricted to `dev`. +- `production`: require an LDSA maintainer approval and restrict deployments to `main`. + +The required approval provides a final gate after online acceptance testing and before production deployment. It does not change DNS; domain cutover remains a separate controlled step. + +## 6. Custom domains + +Only after [the QA checklist](qa.md) passes, add `www.lisbondatascience.org` as the production custom domain. Authoritative DNS remains at Namecheap: change the existing `www` CNAME only after Cloudflare Pages has registered the hostname, and configure Namecheap's apex URL Redirect Record as a permanent `301` to `https://www.lisbondatascience.org/`. Follow [the DNS runbook](dns.md). Do not alter mail or domain-verification records. diff --git a/docs/operations/dns.md b/docs/operations/dns.md new file mode 100644 index 0000000..b7a6bbb --- /dev/null +++ b/docs/operations/dns.md @@ -0,0 +1,107 @@ +# DNS baseline and Namecheap cutover + +The selected architecture keeps Namecheap authoritative for `lisbondatascience.org`. Only the website records change: `www` points to Cloudflare Pages and Namecheap permanently redirects the apex to `www`. Mail, DKIM, SPF, DMARC and verification records remain at Namecheap. + +This choice does not prevent a later move to Cloudflare authoritative DNS. Before a future nameserver change, recreate and verify the complete Namecheap zone in Cloudflare, then change the nameservers. DNS records are not copied automatically when nameservers change. + +## Captured public baseline + +These values were observed on 2026-10-08. Export the authoritative Namecheap zone immediately before cutover because public DNS cannot reveal every record, particularly unknown DKIM selectors. + +| Name | Type | Value | Observed TTL | +| ------------------------------ | ----- | ---------------------------------------------------------------------- | ------------ | +| `lisbondatascience.org` | A | `192.64.119.34` | 1800 | +| `lisbondatascience.org` | A | `54.84.104.245` | 1800 | +| `www.lisbondatascience.org` | CNAME | `unbouncepages.com.` | 60 | +| `lisbondatascience.org` | NS | `dns1.registrar-servers.com.` | 1800 | +| `lisbondatascience.org` | NS | `dns2.registrar-servers.com.` | 1800 | +| `lisbondatascience.org` | MX 1 | `aspmx.l.google.com.` | 1800 | +| `lisbondatascience.org` | MX 5 | `alt1.aspmx.l.google.com.` | 1800 | +| `lisbondatascience.org` | MX 5 | `alt2.aspmx.l.google.com.` | 1800 | +| `lisbondatascience.org` | MX 10 | `aspmx2.googlemail.com.` | 1800 | +| `lisbondatascience.org` | MX 10 | `aspmx3.googlemail.com.` | 1800 | +| `lisbondatascience.org` | TXT | `v=spf1 a mx include:_spf.elasticemail.com ~all` | 1800 | +| `lisbondatascience.org` | TXT | `google-site-verification=fe3vJvPLBYVSEErYNCIPHTSrewzHJ-yopcfAyQz3OZg` | 1800 | +| `_dmarc.lisbondatascience.org` | TXT | `v=DMARC1; p=none` | 300 | + +No apex AAAA or CAA response was observed. + +## Private backup and rollback record + +Immediately before cutover: + +1. Export or manually record every Namecheap Advanced DNS entry, including A, AAAA, CNAME, MX, TXT, CAA, SRV, URL Redirect, Dynamic DNS and email-routing settings. +2. Capture every DKIM selector shown in Namecheap and the relevant mail provider. +3. Store the export and screenshots privately; never commit them if they include sensitive service details. +4. Confirm Google Workspace can send and receive mail. +5. Record the rollback values: + - `www` CNAME: `unbouncepages.com.` + - the two current apex A records shown above; + - any existing apex redirect configuration. + +## Register the Pages hostname before changing DNS + +1. Confirm the accepted `main` commit is healthy at `https://ldsa-website.pages.dev/`. +2. In Cloudflare, open **Workers & Pages → ldsa-website → Custom domains**. +3. Select **Set up a domain**. +4. Enter `www.lisbondatascience.org` and continue. +5. Keep the setup pending until Cloudflare displays the external-DNS instructions. + +Do not create the CNAME first. Cloudflare Pages requires the custom hostname to be associated with the project before the external DNS record points to it. + +## Namecheap cutover + +In **Namecheap → Domain List → lisbondatascience.org → Manage → Advanced DNS**: + +1. Change only the existing `www` record: + + | Type | Host | Value | TTL | + | ----- | ----- | ------------------------- | --------- | + | CNAME | `www` | `ldsa-website.pages.dev.` | Automatic | + +2. Configure the apex redirect: + + | Type | Host | Value | Redirect type | + | ------------------- | ---- | ------------------------------------ | ------------- | + | URL Redirect Record | `@` | `https://www.lisbondatascience.org/` | Permanent 301 | + +3. Remove only A, ALIAS, CNAME or URL Redirect records at host `@` that Namecheap identifies as conflicting with the new apex redirect. The observed apex A records are website/forwarding records, but compare them with the private zone export before removing them. +4. Do not edit MX, SPF, DKIM, DMARC, CAA, SRV or verification records. +5. Save the changes. Namecheap states that URL redirects normally begin working within about 30 minutes; resolver caches may take longer. +6. Wait for Cloudflare Pages to show `www.lisbondatascience.org` as active and for its TLS certificate to become valid. + +## Production verification + +Verify from at least two networks or public resolvers: + +1. `www.lisbondatascience.org` resolves through the Pages CNAME and serves the accepted commit over HTTPS. +2. `lisbondatascience.org` returns a permanent redirect to `https://www.lisbondatascience.org/`. +3. Deep apex URLs preserve their path when redirected. If Namecheap does not preserve paths as required, stop and replace the forwarding method before accepting cutover. +4. All maintained routes, legacy redirects, the custom 404, assets, headers, canonical URLs, `robots.txt` and the sitemap pass. +5. Flodesk submits a controlled production subscription successfully. +6. Google Workspace sends and receives controlled messages, including a reply from an external mailbox. +7. The protected `dev` alias remains separate and private. + +After the hostname is active, configure production-only Web Analytics using the Cloudflare runbook. + +## Rollback + +If production has a material failure: + +1. Restore the `www` CNAME to `unbouncepages.com.`. +2. Remove the new apex URL Redirect Record and restore the prior apex website records or forwarding configuration from the private backup. +3. Leave all mail and verification records untouched. +4. Verify the old public site and email before investigating the failed cutover. + +## Future move to Cloudflare DNS + +A future authoritative-DNS migration remains fully supported: + +1. Add `lisbondatascience.org` as a Cloudflare zone on the Free plan. +2. Import and manually compare every Namecheap record, including all mail, DKIM, SPF, DMARC, CAA, SRV and verification records. +3. Keep mail-related hostnames DNS-only where applicable. +4. Validate the staged Cloudflare zone against the private export. +5. Replace the Namecheap nameservers with the two nameservers assigned by Cloudflare. +6. Verify website, email and domain-verification services throughout propagation. + +The registrar can remain Namecheap even when Cloudflare becomes the authoritative DNS provider. diff --git a/docs/operations/github.md b/docs/operations/github.md new file mode 100644 index 0000000..55dbfdb --- /dev/null +++ b/docs/operations/github.md @@ -0,0 +1,31 @@ +# GitHub repository controls + +Apply repository rulesets to `dev` and `main` after those branches exist. + +## `dev` + +- Require a pull request with at least one approval. +- Require the `Build and verify` status check. +- Require branches to be up to date before merging. +- Require conversation resolution. +- Block force pushes and deletion. +- Allow squash merging. + +## `main` + +- Require a pull request with at least one approval. +- Require the `Build and verify` status check. +- Require branches to be up to date before merging. +- Require conversation resolution. +- Block force pushes and deletion. +- Restrict the normal merge source to `dev` through team convention. +- Protect the `production` GitHub environment with a maintainer approval. + +## Normal change flow + +```text +feature/* → pull request → dev → protected Cloudflare preview +dev → acceptance testing → pull request → main → production deployment +``` + +Dependabot checks npm and GitHub Action dependencies monthly. Dependency pull requests follow the same CI and review rules. diff --git a/docs/operations/qa.md b/docs/operations/qa.md new file mode 100644 index 0000000..4486858 --- /dev/null +++ b/docs/operations/qa.md @@ -0,0 +1,91 @@ +# Migration QA checklist + +Record the tester, date, deployed commit and result for each test. Do not use real subscriber data; use controlled test addresses owned by LDSA. + +## Automated checks + +- [ ] `npm ci` completes from the lockfile. +- [ ] `npm run check` passes. +- [ ] `npm run format:check` passes. +- [ ] GitHub Actions CI passes on the pull request. +- [ ] Lighthouse meets the repository thresholds for all five maintained routes. +- [ ] Cloudflare deployment is associated with the expected commit and branch. +- [ ] The post-deployment contract check passes against the immutable Cloudflare URL. + +## Preview access and indexing + +- [ ] Signed-out visitors cannot open the `dev` preview. +- [ ] Authorized LDSA testers can open it. +- [ ] Preview responses include `X-Robots-Tag: noindex`. +- [ ] The production candidate has a valid HTTPS certificate. + +## Routes and redirects + +- [ ] `/` +- [ ] `/starters-academy/` +- [ ] `/prep-course/` +- [ ] `/about-us/` +- [ ] `/jobs/` +- [ ] `/curriculum/` returns a 301 to `/starters-academy/#curriculum`. +- [ ] `/faq/` returns a 301 to `/starters-academy/#faq`. +- [ ] `/comms-associate-position/` returns a 301 to `/about-us/`. +- [ ] `/pythonzerotohero/` returns a 301 to `/prep-course/`. +- [ ] Recovered `*-form_confirmation.html` routes redirect to their maintained destinations. +- [ ] An unknown route renders the custom 404 page. + +## Responsive and browser coverage + +Test at 320, 375, 390, 768, 1024, 1440 and 1920 CSS pixels. + +- [ ] Chrome on desktop and Android-sized viewport. +- [ ] Firefox on desktop. +- [ ] Edge on desktop. +- [ ] Safari on macOS and iOS, when available. +- [ ] Mobile navigation opens, closes and retains keyboard focus visibility. +- [ ] There is no horizontal overflow. +- [ ] Images preserve their aspect ratio and meaningful images have useful alternative text. +- [ ] Reduced-motion preference does not introduce unusable interactions. + +## Links and integrations + +- [ ] Portal link opens the LDSA Portal. +- [ ] Wiki and course links open the correct current pages. +- [ ] GitHub, LinkedIn, Instagram, Facebook and email links work. +- [ ] YouTube video loads and can be operated by keyboard. +- [ ] Job submission guide opens correctly. + +## Web Analytics + +- [x] The private `dev` deployment contains no `beacon.min.js` request. +- [x] The private `dev` deployment contains no `/cdn-cgi/rum` request. +- [x] Cloudflare account-level request analytics remains available. +- [ ] Production loads `https://static.cloudflareinsights.com/beacon.min.js` after its environment token is configured. +- [ ] Production reports successfully without a Content Security Policy error. +- [ ] Cloudflare Web Analytics records a controlled production visit and Core Web Vitals. + +## Flodesk end-to-end tests + +Use a unique controlled address for each case, then verify the subscriber in the Flodesk account. + +- [ ] General form with only Starters Academy selected reaches the expected segment. +- [ ] General form with only Prep Course selected reaches the expected segment. +- [ ] General form with both selected reaches both expected groups. +- [ ] Starters form reaches the Starters Academy group and triggers the expected confirmation/workflow. +- [ ] Prep form reaches the Prep Course group and triggers the expected confirmation/workflow. +- [ ] Invalid email feedback is understandable and keyboard accessible. +- [ ] A successful submission shows or redirects to the intended confirmation state. + +HTTP checks prove that the public Flodesk scripts and POST endpoints exist, but only the Flodesk subscriber record can prove segment assignment and automation delivery. Do not mark this section complete from browser rendering alone. + +## Production cutover checks + +- [ ] `https://lisbondatascience.org/` redirects to the canonical `www` HTTPS hostname. +- [ ] `https://www.lisbondatascience.org/` serves the expected commit. +- [ ] Canonical and Open Graph URLs use the production hostname. +- [ ] `robots.txt` and `sitemap-index.xml` return 200. +- [ ] Security headers from `public/_headers` are present. +- [ ] Cloudflare Web Analytics records a controlled visit. +- [ ] Email delivery still works for the LDSA domain. +- [ ] Cloudflare shows no unexpected 404 or security errors. + +After all production checks pass, monitor for seven days before taking the final Unbounce backup and cancelling the subscription. diff --git a/docs/operations/release-acceptance.md b/docs/operations/release-acceptance.md new file mode 100644 index 0000000..f14a2dd --- /dev/null +++ b/docs/operations/release-acceptance.md @@ -0,0 +1,36 @@ +# Production candidate acceptance + +## Development acceptance + +- Date: 2026-10-11 +- Environment: `https://dev.ldsa-website.pages.dev/` +- Accepted functional baseline: `a6af0374b16fc27dbeeac636d95ffc1c162f388d` +- Tester/owner: `cmm79` + +The owner confirmed: + +- the preview requires Cloudflare Access; +- `info@lisbondatascience.org` can authenticate and an unapproved address is denied; +- preview responses send `X-Robots-Tag: noindex`; +- all GitHub CI and deployment checks pass; +- Pages-managed Web Analytics is disabled and the preview contains no beacon or RUM request; +- Flodesk subscription forms submit successfully and create the expected subscriber records in Flodesk. + +No subscriber addresses or other personal data are stored in this repository. + +## Release decisions + +- `main` is the production branch and `dev` is the private integration branch. +- Production DNS remains authoritative at Namecheap. +- `www.lisbondatascience.org` will point to `ldsa-website.pages.dev` only after the custom hostname is registered in the Pages dashboard. +- Namecheap will provide a permanent apex-to-`www` redirect. +- Browser Web Analytics will be enabled only for production after the final hostname is active. +- The existing Unbounce-hosted public site remains available until the DNS cutover. + +## Remaining acceptance gates + +- Promote the release-preparation changes from `dev` to `main` and verify the public Pages production hostname. +- Export the complete Namecheap zone privately and record rollback values. +- Attach `www`, complete the Namecheap cutover and verify email. +- Enable and verify production-only Web Analytics. +- Monitor production for seven days before closing the migration. diff --git a/lighthouserc.json b/lighthouserc.json new file mode 100644 index 0000000..5901d0c --- /dev/null +++ b/lighthouserc.json @@ -0,0 +1,23 @@ +{ + "ci": { + "collect": { + "staticDistDir": "./dist", + "url": [ + "http://localhost/", + "http://localhost/starters-academy/", + "http://localhost/prep-course/", + "http://localhost/about-us/", + "http://localhost/jobs/" + ], + "numberOfRuns": 3 + }, + "assert": { + "assertions": { + "categories:performance": ["error", { "minScore": 0.85 }], + "categories:accessibility": ["error", { "minScore": 0.95 }], + "categories:best-practices": ["error", { "minScore": 0.95 }], + "categories:seo": ["error", { "minScore": 0.95 }] + } + } + } +} diff --git a/migration/flodesk-inventory.md b/migration/flodesk-inventory.md new file mode 100644 index 0000000..bd414d4 --- /dev/null +++ b/migration/flodesk-inventory.md @@ -0,0 +1,20 @@ +# Flodesk integration inventory + +The live forms were recovered from rendered public HTML. Their endpoint, form ID, success mode and interest values are preserved in `src/components/FlodeskForm.astro`. + +| Purpose | Page | Form ID | Submission endpoint | Completion behavior | +| --------------------------------------- | -------------------- | -------------------------- | ---------------------------------------------------------------- | ----------------------- | +| General newsletter and course interests | `/` | `63f9e4e2f0116a4cabfef7db` | `https://form.flodesk.com/forms/63f9e4e2f0116a4cabfef7db/submit` | Inline success message | +| Starters Academy waiting list | `/starters-academy/` | `615c9641c71e8685f183bd18` | `https://form.flodesk.com/forms/615c9641c71e8685f183bd18/submit` | Redirect to site root | +| Prep Course waiting list | `/prep-course/` | `6172e62fe625581864e3f857` | `https://form.flodesk.com/forms/6172e62fe625581864e3f857/submit` | Redirect to Prep Course | + +The general form uses these Flodesk preference values: + +| Value | Label | +| --------- | ------------------------------------ | +| `option1` | Lisbon Data Science Starters Academy | +| `option2` | Data Science Prep Course | + +The Flodesk universal script progressively enhances the native forms. The component mirrors the public embed's current module/legacy loader pair, while the native `POST` action remains present as a fallback. On 2026-10-09 both public runtime files returned HTTP 200 and all three endpoints returned HTTP 405 with `Allow: POST`, confirming that the integration resources and form handlers exist without creating test subscribers. + +A real submission must still be checked against the expected Flodesk segment and automation before production cutover; rendering and HTTP-level checks cannot prove subscriber routing inside the Flodesk account. Use separate controlled LDSA addresses for each routing case in the QA runbook. diff --git a/migration/page-inventory.md b/migration/page-inventory.md new file mode 100644 index 0000000..1e23369 --- /dev/null +++ b/migration/page-inventory.md @@ -0,0 +1,29 @@ +# Public page inventory + +Captured from the public `www.lisbondatascience.org` site on 2026-10-08 and re-audited on 2026-10-09. The suspended Unbounce account cannot be inspected, so the migration scope is based on live responses, rendered source, search results, the Internet Archive CDX index, LDSA-owned public properties, and stakeholder review. + +| Current route | Unbounce page ID | Status | Migration action | Flodesk | +| -------------------------------------------- | -------------------------------------- | ---------------------------- | ----------------------------------------------------- | ---------------------------------------- | +| `/` | `7f3394ee-2bc1-4e2a-bab8-b6f82f2f95a1` | Current | Rebuilt at `/` | General form `63f9e4e2f0116a4cabfef7db` | +| `/starters-academy/` | `e32f0ae1-c188-40d7-b076-eb2635283791` | Current | Rebuilt at same route | Starters form `615c9641c71e8685f183bd18` | +| `/prep-course/` | `7fa562f1-64ad-49b9-9496-d134d3b71af0` | Current | Rebuilt at same route | Prep form `6172e62fe625581864e3f857` | +| `/about-us/` | `26c2a69c-5711-4a20-b2af-daf4908b0628` | Current | Rebuilt at same route | None | +| `/jobs/` | `19599627-f46b-430c-84cf-75e8a08edcbf` | Current | Rebuilt at same route | None | +| `/curriculum/` | `c308f178-82db-40be-a34c-14ea378e1a33` | Legacy and materially stale | Permanent redirect to `/starters-academy/#curriculum` | None | +| `/faq/` | `8e322e94-da07-418c-86fd-2aed2693fb87` | Legacy and materially stale | Permanent redirect to `/starters-academy/#faq` | None | +| `/comms-associate-position/` | `0cc074e0-cbc4-46ec-b2e2-f21e73d32b86` | Expired 2018 vacancy | Permanent redirect to `/about-us/` | Retired Unbounce form | +| `/pythonzerotohero/` | Unknown | Historical and currently 404 | Permanent redirect to `/prep-course/` | Unknown | +| Historical `*-form_confirmation.html` routes | Associated page IDs where observable | Retired confirmation pages | Redirect to the relevant maintained route | Retired Unbounce forms | + +The snapshot HTML is stored under `migration/raw/`. It is evidence and source material, not production code. It includes generated Unbounce markup and should never be copied into the final application. See `public-evidence-audit.md` for sources, response status and limitations. + +## Content decisions + +- Current course claims, open learning materials, external resources, video and contact links were retained. +- Dates, pricing and year-specific admission language that had already become stale were replaced with links to the LDSA wiki, the operational source of truth. +- The published volunteer roster was retained without mutable employer/title claims. +- Legacy Curriculum and FAQ content was consolidated into the maintained course pages so old URLs continue to resolve through 301 redirects. + +## Account-access limitation + +The account reportedly contained fourteen published pages, but its suspension prevents an account inventory, `.unbounce` backups, inactive-variant inspection, Script Manager inspection, and lead export. These items cannot be reconstructed from public responses and are no longer migration gates. LDSA accepts this limitation and will validate the publicly recovered URL set before production cutover. diff --git a/migration/public-evidence-audit.md b/migration/public-evidence-audit.md new file mode 100644 index 0000000..aad2e5b --- /dev/null +++ b/migration/public-evidence-audit.md @@ -0,0 +1,61 @@ +# Public website evidence audit + +## Scope and method + +The public LDSA site was re-audited on 2026-10-09 after the Unbounce account became unavailable. The audit used only free, public sources: + +- direct HTTPS requests to the production hostname; +- rendered HTML, scripts, forms, links, metadata and response headers; +- `robots.txt` and sitemap checks; +- public search results; +- the Internet Archive CDX URL index; +- LDSA Wiki, Portal, GitHub and social links referenced by the site; +- the existing source snapshots in `migration/raw/`. + +The live site does not expose a sitemap: `/sitemap.xml` returns 404. Its `robots.txt` only blocks Unbounce click-tracking paths. Search and archive discovery were therefore used in addition to navigation crawling. + +## Recovered route set + +| Route | Live status on 2026-10-09 | Decision | +| ---------------------------------------------------- | ------------------------------------ | ----------------------------------------- | +| `/` | 200 | Rebuilt | +| `/starters-academy/` | 200 | Rebuilt | +| `/prep-course/` | 200 | Rebuilt | +| `/about-us/` | 200 | Rebuilt | +| `/jobs/` | 200 | Rebuilt | +| `/curriculum/` | 200, materially stale | Redirect to maintained curriculum section | +| `/faq/` | 200, materially stale | Redirect to maintained FAQ section | +| `/comms-associate-position/` | 200, expired 2018 vacancy | Redirect to About/Get involved | +| `/pythonzerotohero/` | 404, historically archived | Redirect to Prep Course | +| `/d-form_confirmation.html` | 200, retired homepage confirmation | Redirect to homepage | +| `/comms-associate-position/a-form_confirmation.html` | 200, retired vacancy confirmation | Redirect to About | +| `/curriculum/a-form_confirmation.html` | 200, retired curriculum confirmation | Redirect to Starters Academy | +| `/a-form_confirmation.html` | 404, historically archived | Redirect to homepage | +| `/b-form_confirmation.html` | 404, historically archived | Redirect to homepage | + +The Internet Archive also indexed HTTP/HTTPS and port variants of these URLs. Those are hostname/protocol variants rather than separate content routes. + +## Public integration findings + +Three Flodesk forms are present in the rendered production HTML: + +| Page | Form ID | Endpoint | +| ---------------- | -------------------------- | ---------------------------------------------------------------- | +| Homepage | `63f9e4e2f0116a4cabfef7db` | `https://form.flodesk.com/forms/63f9e4e2f0116a4cabfef7db/submit` | +| Starters Academy | `615c9641c71e8685f183bd18` | `https://form.flodesk.com/forms/615c9641c71e8685f183bd18/submit` | +| Prep Course | `6172e62fe625581864e3f857` | `https://form.flodesk.com/forms/6172e62fe625581864e3f857/submit` | + +The homepage form sends the public preference values `option1` for Starters Academy and `option2` for Prep Course. The public embed loads `https://assets.flodesk.com/universal.mjs` for modern browsers and `https://assets.flodesk.com/universal.js` as the legacy fallback. Both resources returned HTTP 200 on the audit date. Each form endpoint advertised POST support without a test submission. + +The public HTML also contains the old Google Analytics property `UA-113478482-1`, Unbounce runtime code, old jQuery code, YouTube, and normal outbound links. No Google Tag Manager, Meta Pixel, or LinkedIn Insight Tag was observed. + +## Accepted limits + +Public evidence cannot reveal unpublished pages, inactive experiment variants, disabled global scripts, lead records, historical analytics, or server-side integrations that produce no browser-visible effect. These unknown account-only items are accepted as unavailable and do not block migration. Production acceptance is based on: + +- all recovered routes and redirects; +- maintained public content and metadata; +- visual and responsive QA; +- external-link checks; +- real Flodesk subscriber-routing tests; +- stakeholder review before DNS cutover. diff --git a/migration/raw/.gitkeep b/migration/raw/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/migration/raw/.gitkeep @@ -0,0 +1 @@ + diff --git a/migration/raw/about-us.html b/migration/raw/about-us.html new file mode 100644 index 0000000..32ac677 --- /dev/null +++ b/migration/raw/about-us.html @@ -0,0 +1,3812 @@ + + + + Lisbon Data Science Academy + + + + + + + + + + + + + + + + + + + + + + + + + +

WHO'S BEHIND IT?

We are a group of Data Scientists and volunteers, from all kind of organisations, who absolutely love to teach and spread the knowledge of Data Science and its amazing uses!

The Academy is not for profit and all fees are re-invested into the program, to continuously make it better.

Machine Learning Engineer
 Spotify

Data Scientist
 DareData Engineering

Senior Research Engineer
 Unbabel

Juliane Silva

Senior Data Delivery EY (Ernst & Young)

Christine Maroti

AI Research Engineer
Unbabel

Big Data Consultant  ALTRAN

Data Analyst Seedrs


Rita Carvalho

RAN Engineer Nokia

Software Developer
Probely

Researcher MINES ParisTech University

Rodrigo Amaro e Silva

Data Scientist / Engineer Closer Consulting

Susan Wang

Data Scientist European Commission

Data Science and Analytics 
DareData Engineering

Pratiksha Jain

Data Quality Analyst CarpeData

Gonçalo Nunes

Venture Partner Olisipo Way

Carla Cotas

ORGANISATIONS THAT MAKE

OUR ACADEMIES POSSIBLE

Software Engineer
Google

Data Scientist / Engineer
DareData Engineering

 Data Scientist & Engineer
DareData Engineering

Data Scientist DareData Engineering

Data Scientist
Devscope

Data Engineer 
DareData Engineering

Data Engineer/ Scientist
DareData Engineering

Rita Sousa

Data Scientist
Jungle AI

Quant/Data Scientist
BNP Paribas

José Miguel Santos

Mariana Fernandes

Sonia Seyed Allalei

HomeAbout UsStarters AcademyPrep CourseJobs pageContacts

© 2020 Lisbon Data Science Academy. 

All rights reserved

Get in touch!

info@lisbondatascience.org

© 2022 Lisbon Data Science Academy.  All rights reserved

Inês Mendes

Phd Student

Instituto de Medicina Molecular João Lobo Antunes


Chief Operations Officer

Daredata Engineering

Helena Carvalho

Trainee 

Banco de Portugal

Daniel Sousa

Data Scientist
Tessian

Sofia Ferreira

Branding & Web Design

BySofiaDesign

Pedro Ved

Marketing

Pedro Ribeiro

Quality Assurance Engineer

Inês Pires

Data scientist Omnicom Media Group

Silvana Alves

Business Operations Associate Klarna

Ricardo Robalo

Phuc Nguyen

Search Language Specialist Qualitest

Gustavo Fonseca

Data Scientist / Data Engineer DareData

Diana Baltrusaitis

Web Developer Capgemini

Tiago Ferrão

Innovation Manager Smart Energy Lab

Miguel Dias

Data Engineer DareData Engineering

Diana Vieira

Research Fellow Instituto Superior Técnico

João Correia

Machine Learning Engineer Jungle

Sara Gomes

Project Manager Tekever

Fábio Cruz

Machine Learning Researcher Inductiva Research Labs

José Rebelo

Data Scientist Cofidis

EXECUTIVE TEAM

President

Vice-President

Vice-President

Daniel Neves

Principal
 DareData Engineering

Marketing and Growth

+ + \ No newline at end of file diff --git a/migration/raw/comms-associate-position.html b/migration/raw/comms-associate-position.html new file mode 100644 index 0000000..9263afc --- /dev/null +++ b/migration/raw/comms-associate-position.html @@ -0,0 +1,1790 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+Lisbon Data Science Academy is searching  for a Communications Associate (voluntary position) +

+
+
+

+The role: +

+

+We need help spreading the word about the Academy and managing our social presence, including some content creation. For this we need someone passionate about bringing affordable tech education to students and professionals, someone with fresh ideas and a willingness to learn, but teach too. +

+
+
+

+Who we are: +

+

+Lisbon Data Science Academy is a non-profit, volunteer-based organisation bringing affordable education to students and tech professionals. We are proud to say that amongst our volunteers we have a few of the best data scientist in Portugal, successful entrepreneurs, engineers, founders, CEOs and CTOs. +

+

+Last year we ran the first data science course in Portugal. It was a great success and an amazing experience for the whole team, where we got to work with industry leaders such as Microsoft, Farfetch, Uniplaces.  +

+

+This year we’re set on providing an even better experience for our students and expanding our network of partners even further. That’s where you come in! +

+
+
+
+
+
+
+
+
+
+
+

+What’s in it for you: +

+

+In return we offer over 200 years of our cumulative experience in the tech industry.  +

+
    +
  • +

    +You will be part of all our weekly meetings and will be able to see first hand the inner workings of an educational startup.  +

    +
  • +
  • +

    +You will have a voice in building the Lisbon Data Science Academy brand  +

    +
  • +
  • +

    +You’ll become a part of our Mastermind and make friends for life +

    +
  • +
  • +

    +You’ll increase your network with people from startups and tech companies. +

    +
  • +
  • +

    +We will add you as a collaborator in our LinkedIn organisation and write a recommendation that might just be the tipping point towards hiring for your next clients. +

    +
  • +
+

+
+

+

+Time investment: +

+
    +
  • +

    +This positions will be open from now until September 2018.  +

    +
  • +
  • +

    +It will require around 5 hours per week in the first 2 months and between 2 and 4 hours per week after that. +

    +
  • +
  • +

    +Once a month, you’ll need to attend our hackathons for  few hours (always a Sunday). Locations and dates are to be decided, but previous locations included the Lisbon offices of Microsoft, Farfetch, Uniplaces and Outsystems, to name a few. +

    +
  • +
+
+
+
+
+
+
+
+
+
+
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+
+Submit +
+
+

+To apply please fill in this form:
+

+
+
+
+Core competencies (required):  +
+
    +
  • +

    +Digital marketing +

    +
  • +
  • +

    +Social media management +

    +
  • +
  • +

    +Content creation +

    +
  • +
  • +

    +Community management +

    +
  • +
  • +

    +Excellent written and spoken English (CAE preferred) +

    +
  • +
  • +

    +Strong organizational skills +

    +
  • +
  • +

    +Command of Google Apps tools (Gmail, Calendar, Google Docs, Google Sheets) +

    +
  • +
  • +

    +Good communication skills and enthusiasm for working with people +

    +
  • +
+
+
+
+Optional:  +
+
    +
  • +

    +Enthusiasm for technology  +

    +
  • +
  • +

    +Experience in communication positions +

    +
  • +
  • +

    +Photography skills +

    +
  • +
  • +

    +Video editing skills +
    +
    +

    +
  • +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
Fix the following errors:
Hide
+ + + + + diff --git a/migration/raw/curriculum.html b/migration/raw/curriculum.html new file mode 100644 index 0000000..561cc04 --- /dev/null +++ b/migration/raw/curriculum.html @@ -0,0 +1,5167 @@ + + + + + + + +Lisbon Data Science Academy + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+

+FORMAT +
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+The Bootcamp   +
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+6th & 7th  July  +

+

+The Bootcamp +

+
+
+

+21st July +

+

+Hackathon #1
+

+
+
+

+25th August +

+

+Hackathon #2 +

+
+
+

+29th September +

+

+Hackathon #3 +

+
+
+

+27th October +

+

+Hackathon #4 +

+
+
+

+24th November +

+

+Hackathon #5 +

+
+
+

+The Starters Academy lasts approximately 21 weeks (~ 7 months). Of that time, 8 days are in-person (usually on Sundays), and the rest is done remotely via GitHub, Slack and the Academy's submission and grading system. +

+
+
+

+ Hackathon #1
+

+
+
+

+ Hackathon #2 +

+
+
+

+ +
Hackathon #3
+

+
+
+

+ Hackathon #4 +

+
+
+

+ Hackathon #5 +

+
+
+

+ +
6th & 7th  
+
+July
+

+
+Read all the details on GitHub +
+

+ +
21st 
+
+July
+

+
+
+

+
25th August
+

+
+
+

+
29th September
+

+
+
+

+
27th October
+

+
+
+

+ +
24th 
+
+November
+

+
+
+

+CURRICULUM +
+
+

+
+
+
+ +
+
+
+

+ORGANISATIONS THAT MAKE
+

+

+OUR ACADEMIES POSSIBLE
+

+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+

+BOOTCAMPS +

+
+
+

+The Bootcamp is an intensive, 2 day event, where students and teachers will be in the same building for 10 hours per day, and the students will go through their first 20 Learning Units.  +

+

+
+

+
+
+

+HACKATHONS +

+
+
+

+Hackathons are longer and less structured exercises, often done in teams, in which the students must complete a relatively advanced project. Hackathons require the students to make use of the skills they learned in the Learning Units, but also to excel at teamwork and communication. +

+
+
+
+
+
+
+
+
+
+HomeCurriculumAbout Us +
+
+ +
+
+BlogContact +
+

+© 2018 Lisbon Data Science Academy. +

+
+
+
+ +
+
+
+
+ +
+
+
+

+All rights reserved +

+
+
+

+Get in touch! +

+
+
+

+info [at] lisbondatascience.org +

+
+
+

+© 2018 Lisbon Data Science Academy.  All rights reserved +

+
+
+

+WHAT OUR STUDENTS SAY +

+
+
+

+Amazing community and very knowledgeable instructors! +

+

+ I entered the academy with some previous knowledge, but it was nowhere near to what I learned throughout the first edition. The learning materials were adequate and simple to learn in order to face the monthly hackathons. The hackathons were super challenging and in awesome places. Oh and food / drinks / coffee in the hackathons were never a problem, so the logistics were great! +

+
+
+

+“ +

+
+
+
+
+
+

+RODRIGO VERÍSSIMO
+

+
+
+

+This image shows a high level overview of the learning units.  +

+

+You can see the curriculum in more details in this document.
+

+
+Questions? Check out the FAQQuestions? Check out the FAQFAQ +
+

+Sign up for our mailing list to receive information about our next batch first-hand
+

+
+
+

+SUBSCRIBE +

+
+Apply now +
+
+ +
+ +
+
+Subscribe +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
Fix the following errors:
Hide
+ + + + + + + diff --git a/migration/raw/faq.html b/migration/raw/faq.html new file mode 100644 index 0000000..372bb6d --- /dev/null +++ b/migration/raw/faq.html @@ -0,0 +1,2686 @@ + + + + + + + +Lisbon Data Science Academy + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+HomeCurriculumAbout Us +
+
+ +
+
+BlogContact +
+

+ORGANISATIONS THAT MAKE
+

+

+OUR ACADEMIES POSSIBLE
+

+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+ +
+
+
+
+

+FAQ +

+
+
+

+1 - What do you mean by strong quantitative skills? +

+

+You should be comfortable around numbers, quickly understanding a real life problem and translate it into something measurable & workable. Usually is the kind of skills that results from having an education or working experience in the fields of (but not only) Engineering, Physics, Mathematics, Management, Economics, etc..  +

+

+  +

+

+2 - Will I have any kind of teaching support, if I need, during the months following the Bootcamp Days? +

+

+Definitely yes! This is one of the  strongest things we want to build during the Academy experience: the community effect! We are working hard to make sure you have access to a community of colleagues, teachers and staff that will be there to help you in whatever way it's possible. Even you will be able to help others!  +

+

+  +

+

+3 - Is there any financial support if I can't afford the student fees? +

+

+We are in the process of deciding this. Please  check this page soon.
+

+

+  +

+

+4 - What will be the size of the students group? +

+

+We are expecting a maximum of 50 students. The idea is to have a high teacher-student ratio, and to ensure everyone gets enough support.  +

+

+  +

+

+5 - Are meals included in the fee? +

+

+Yes, lunch and snacks during the day.  +

+

+  +

+

+6 - Will there be any kind of internship possibility at the end of the school? +

+

+We are working hard to surprise you on this topic. More news will come soon.  +

+

+  +

+

+7 - What language will the classes be held in? +

+

+English.  +

+

+  +

+

+8 - Will you teach python?  +

+

+Yes but enrolments for the Data Science Prep Course for Batch 3 are now closed.  +
+Want to enrol next time? Subscribe here to our newsletter to stay updated on all new dates! 
+

+

+  +

+

+9 - I don’t know any data science, will I be able to do the classes?  +

+

+Yes! The classes are directed at people who are giving their first steps in the data science world.  +

+

+  +

+

+10 - I can come to the Bootcamp days, but can’t make it to the hackathons. Should I apply anyway?  +

+

+Presence in the bootcamp and Hackathon #1 is compulsory, as is the capstone. +
+Of hackathons 2 through 5, you are allowed to do one remote if the need is justified, and (if you really, really have to) miss one. +

+

+
+
+11 - Will laptops be provided?  +

+

+No, you must bring your laptop with you +

+

+  +

+

+12  - When will we install the necessary software?  +

+

+Setup must be done before arrival, we will provide step by step instructions, and remote support.  +

+

+
+

+

+13 - Do you have a refund policy? +

+

+Sorry, since the payment and course start dates are so close, we're not doing refunds.
+

+

+
+

+

+14 - How do I know if my python skills are good enough? +

+

+A good option would be doing our test. In case you want to know, the main topics we cover on this test are the following:  +

+

+- if and else statements
- for loops
- handle arrays and tuples (including slicing)
- handle lists and dictionaries (including list comprehension)
- string manipulation
- data types (like int, float, string and how to convert variables between them)
- define and use functions
- lambda functions and map
- try except statements
- basics of OOP (like what's a class, how to instantiate one and use methods from the class)
+

+
+
+

+© 2018 Lisbon Data Science Academy. +

+
+
+
+ +
+
+
+
+ +
+
+
+

+All rights reserved +

+
+
+

+Get in touch! +

+
+
+

+info [at] lisbondatascience.org +

+
+
+

+© 2018 Lisbon Data Science Academy.  All rights reserved +

+
+FAQ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + + + + diff --git a/migration/raw/home.html b/migration/raw/home.html new file mode 100644 index 0000000..262c3a7 --- /dev/null +++ b/migration/raw/home.html @@ -0,0 +1,5536 @@ + + + + Lisbon Data Science Academy + + + + + + + + + + + + + + + + + + +

Data Scientists are one of the most required and highly paid professionals today. They swim in the heaps of data our society generates and make discoveries that drive business decisions. It’s been called the coolest profession of the 21st century.


WHAT IS THE LISBON DATA SCIENCE ACADEMY ABOUT?

The LDSA is a non-profit organization dedicated to Data Science, 100% run by volunteers. It is responsible for the Data Science Starters Academy which focuses on introductory material for those who want to become entry-level data scientists.


REMEMBERING OUR FIRST BATCH!

Take a look at six months of work and fun in 150 seconds.

Signup now


SIGN UP TO RECEIVE ALL NEWS FIRST-HAND


APPLICATIONS IN PROGRESS!

Apply now
HomeStarters AcademyAbout UsPrep CourseGet in touch


CREATED BY DATA SCIENTISTS FOR FUTURE DATA SCIENTISTS

If you're completely new to programming and software engineering, you're not ready for the Starters Academy yet.

But we've got you covered! In the Prep Course you can learn all the basics you need, as a first step in you data science journey.

DATA SCIENCE PREP COURSE

OUR COURSES

DATA SCIENCE STARTERS ACADEMY

An introductory course to the Data Science field where you'll learn all the skills needed to become a junior data scientist. It lasts for ~7 months and it's designed so that it doesn't interfere with your day job.

Due to the COVID-19 pandemic, this year's batch will be remote.

Data Science

Starters Academy

Data Science

Prep Course

Learn moreLearn more

ORGANISATIONS THAT MAKE

OUR ACADEMIES POSSIBLE

Data Science

Starters Academy

Data Science

Prep Course

Signups are OPEN

for the Lisbon Data Science Academy

Get inside our newsletter and we'll send you all the LDSA goodies first-hand at the right time.

 (news about Free Data Science Prep Course, Starters Academy, etc) 

Jobs page

Just LDSA news - we hate SPAM too.

Sign-ups are open until 11.59 pm on June 20th.

Before signing up, please check all information about the application process here: https://ldssa.github.io/wiki/Applicants/Application-process/

LDSSA batch #9 started in Sep 2025.

     Batch #10 is expected to start in Sep 2026.

Subscribe to our newsletter to get the latest news  

when the admissions open.



+ +
+ +
+ +
+
+
+
+
Join our mailing list.
+
+
+
+
Get inside our newsletter and we'll send you all the LDSA goodies first-hand at the right time.
+
+
+
+
+ + +
+ + +
+ + + +
+ +
+
+
What are you interested in hearing about?
+
+
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ +
+
+
Thank you for subscribing!
+
+
+
+
+
+
+ +

Signups for the Unofficial Sidecar Academy are open until Oct. 19

I want to signup!

For more information on our 100% free Sidecar Academy, please read all details here.

© 2020 Lisbon Data Science Academy. 

All rights reserved

Get in touch!

info@lisbondatascience.org

© 2022 Lisbon Data Science Academy.  All rights reserved

+ + \ No newline at end of file diff --git a/migration/raw/jobs.html b/migration/raw/jobs.html new file mode 100644 index 0000000..c46ac28 --- /dev/null +++ b/migration/raw/jobs.html @@ -0,0 +1,2753 @@ + + + + + + + + + + + + + + + + + + + + + + +

(yes, both for you and for the applicant)

Submit a job offer to reach data science talent, for free.
HomeStarters AcademyAbout UsPrep CourseContacts
Start here

ORGANISATIONS THAT MAKE

OUR ACADEMIES POSSIBLE

Jobs page

© 2020 Lisbon Data Science Academy. 

All rights reserved

Get in touch!

info@lisbondatascience.org

© 2022 Lisbon Data Science Academy.  All rights reserved

+ \ No newline at end of file diff --git a/migration/raw/prep-course.html b/migration/raw/prep-course.html new file mode 100644 index 0000000..c2a2352 --- /dev/null +++ b/migration/raw/prep-course.html @@ -0,0 +1,5536 @@ + + + + Lisbon Data Science Academy + + + + + + + + + + + + + + + + + + + + + + + + + +

FORMAT

Prep Course applications start

18th March

Prep Course applications start

26th March

Accepted students are announced

5th April

Prep Course starts

To be announced

Starters Academy applications start

18th June

Prep Course ends

The Prep Course was designed as a preparation for the Starters Academy. It is fully taught online and lasts for 12 weeks.

You'll learn how to program in Python, how to use the command line, some linear algebra basics and version control with git.

Accepted students are announced

Prep Course starts

Starters Academy applications start

Prep Course ends

2021-03-18

2021-03-26

2021-04-05


To be announced

2021-06-18

CURRICULUM

ORGANISATIONS THAT MAKE

OUR ACADEMIES POSSIBLE

© 2020 Lisbon Data Science Academy. 

All rights reserved

Get in touch!

info@lisbondatascience.org

© 2022 Lisbon Data Science Academy.  All rights reserved

WHAT OUR STUDENTS SAY

Before the preparation course I was very rusty in Python, not having programmed anything in over a year. The course gave me the foundations that allowed me to ace the entrance test and ensured that, throughout the academy, the “how should I put this idea into code?” problem never really existed. I highly recommend it!

“

RICARDO ALBERTO

Updated curriculum will be announced soon.

00

0. I want more information!

Check the Wiki: https://ldssa.github.io/wiki/ 


1. What is the LDSA Prep Course about?

The Prep Course was designed to teach you everything you need to know before joining the Data Science Starters Academy. It was created to be taken 100% remotely. This means that it's fully taught online, with new content every week, and instructors available to help you. 


2. What topics are covered?

We start by teaching you how to program in Python. We'll then go through some Linear Algebra basics and we'll show you how to translate them into coding using Numpy. We'll also cover some software development utilities like how to use the command line, using a text editor for programming,  version control with git and Python modules, and packaging. We'll wrap it up with a final project where you'll be putting everything you've learned into practice.


3. When does the course start?

The more news on this soon. Subscribe to our newsletter on our main page to receive the news firsthand.


4. How much does it cost?

The Prep Course is free for everyone!


5. What is the recommended time commitment?

If you have no prior knowledge of any of the subjects, you should be prepared to spend an absolute minimum of 10 hours per week on the course. We understand this might sound like a lot. However, this course was not designed to give you some brief ideas on Python programming. Instead, we expect that you will fully grasp all fundamental concepts and practice enough to instinctively solve programming problems. If you already have a certain degree of knowledge of some topics, we expect the time required will be less. Finally, keep in mind that this recommendation does not include the preparation time for the admission test for the Starters Academy.


6. Is there any software/app I need to be able to do the course?

Yes, there is some software you’ll need to install, but we will provide you with detailed guidelines on how to do it. This requires you to have a computer/laptop with admin rights.


7. How does the application process work?

Once you sign up, we will share with you the application form. You will need to answer all of the questions, which will be evaluated to decide whether you will be accepted. Keep in mind that we will not test any technical or prior knowledge. This is for us to assess your motivation and whether you will be to undertake an accelerated learning experience. Dates about applications will be disclosed soon. 


8. By doing the course, do I have a 100% chance to get into the Lisbon Data Science Starters Academy?

No, doing or completing the course does not guarantee you a slot in the Academy. What we are confident to say is that doing the course will increase your chance to get in. Last year, out of 50 people who got accepted into the Academy, 25 people went through the prep course, and many of them had never been exposed to programming before.


9. I’m an absolute n00b. Will I be able to do the Prep Course?

This course is designed for people without any prior knowledge or experience in the field. This is our diversity effort to enable everyone to enter the amazing world of Data Science.


10. Will I get a certificate at the end?

Not at this time. However, we are sure that, more than the certificate you could get, your biggest takeaway from this course will be the knowledge you've acquired and how it will help you navigate the world of Data Science.


We kindly remind you that - although most of our students are able to successfully find a job in Data Science - we can't guarantee it will actually land you a job and a career in tech. Additionally, the Starters Academy was designed to be taken as a sidekick, not your main activity.

Next Prep-Course starting in March 2026.


Applications will open in Jan-Feb 2026.


Subscribe to our newsletter to get the latest news.




Master the basics of Python, along with some Git and Numpy and some good old-fashioned puns!

Fill out your application today and we’ll send the results in a few days to let you know if you’re in.

This is what you’ll learn: Python Programming, NumPy, the *nix command line, Git Basics, Jupyter, Linear Algebra.

TIMELINE

LEARNING UNITS WITH EXERCISES

During the first 10 weeks, you'll develop software engineering skills using our learning materials and testing them with our auto-graded exercises.

You'll be in touch with instructors and other students attending the course, so that you can give or ask for help anytime!

FINAL PROJECT

In the final project, you'll put all those new skills in practice, and build a piece of software that you'll definitely be proud of.

It is all about interaction, growth and experience, so you can confidently build up your data science skills.

GOT QUESTIONS?

Detailed curriculum from the 2020 editionYes, I want the FREE course!HomeAbout UsStarters AcademyPrep CourseJobs pageContacts
+ +
+ +
+ +
+
+
+
Subscribe
+
+
+
+
 
+
Join our mailing list to receive all news firsthand for the next edition of the Prep Course
+
+
+
+
+ + +
+ + +
+ + + +
+ + +
+
+
Thank you for subscribing!
+
+
+
+
+
+ +

SCHEDULE

+ + \ No newline at end of file diff --git a/migration/raw/robots.txt b/migration/raw/robots.txt new file mode 100644 index 0000000..685d6cd --- /dev/null +++ b/migration/raw/robots.txt @@ -0,0 +1,4 @@ +User-agent: * + +Disallow: */clkn/* +Disallow: */clkg/* \ No newline at end of file diff --git a/migration/raw/sitemap.xml b/migration/raw/sitemap.xml new file mode 100644 index 0000000..35d97e9 --- /dev/null +++ b/migration/raw/sitemap.xml @@ -0,0 +1 @@ +The requested URL was not found on this server. \ No newline at end of file diff --git a/migration/raw/starters-academy.html b/migration/raw/starters-academy.html new file mode 100644 index 0000000..60ffc43 --- /dev/null +++ b/migration/raw/starters-academy.html @@ -0,0 +1,10904 @@ + + + + Lisbon Data Science Academy + + + + + + + + + + + + + + + + + + + + + + + + + + +

The Starters Academy lasts approximately 40 weeks of part-time work over the course of 8 months. 

Of that time, there are 9 days where we request you to be virtually present (usually on Sundays), and the rest is done remotely via GitHub, Slack, and the Academy’s grading and submission system. 

You should be prepared to spend a recommended 10 hours per week on the course and an absolute minimum of 5 hours per week in case you are having a busy week.

You do not need to quit your primary responsibility to take part in this course.

BOOTCAMP

During the Bootcamp, the students and teachers will participate virtually in a series of presentations and exercises, about 10 hours per day, and the students will go through 16 Small Learning Units.

SPECIALIZATIONS

After the bootcamp, there will be 5 remote Specializations, that are deeper dives into different areas in Data Science. Each lasts for a month and ends with an hackathon where students and instructors get together online.

This image shows a high-level overview of the learning units for Batch 5. 

This curriculum is subject to change without further notice.

Questions? Check out our wiki

LDSSA batch #9 started in Sep 2025.

     Batch #10 is expected to start in Sep 2026.

Subscribe to our newsletter to get the latest news  

when the admissions open.




WHAT IS IT ABOUT?

The Data Science Starters Academy is a series of events dedicated to helping people become entry-level data scientists, by teaching introductory material.

WHY DATA SCIENCE ?

Data Scientists are one of the most required and highly paid professionals today.

They swim in the heaps of data our society generates and make discoveries that drive business decisions.

It’s been called the coolest profession of the 21st century.
 

It's a 100% online and remote course. Meaning that is a way of learning that doesn’t interfere with your day job.

We start with a two-day intensive synchronous remote bootcamp and then we get together once a month for a synchronous remote hackathon. In between hackathons you’re provided with materials to study in your own time, with support from top-level mentors on slack.

HOW DOES IT WORK?

WHO CAN PARTICIPATE?

The academy is aimed at students or professionals with little to no knowledge of data science, but with strong quantitative skills and knowledge of software development in Python. If you are one of these, join us!

The Academy is taught by top data scientists from leading companies such as Farfetch, Feedzai, Google, Spotify, Unbabel and many others. 

They volunteer their time at the Academy to help brilliant students like you.

WHO WILL TEACH YOU?


WHAT'S IN IT FOR YOU ?

This will be your first step towards becoming a Data Scientist!

By the end of the Academy you will have skills to start a career in data science, and an amazing toolkit to learn more advanced subjects.

Sign up for the waiting list

FORMAT

HACKATHONS

Hackathons are longer and less structured exercises, done in teams, in which the students must complete a relatively advanced project. Hackathons require the students to make use of the skills they learned in the Specializations, but also to excel at teamwork and communication.

CAPSTONE PROJECT

The Capstone Project is the students' first solo flight. They'll consolidate everything they've learned in the Academy by working on a project designed to be a real-life data science project

APPLICATIONS TIMELINE

As a non-profit organisation, we're doing our best to keep our prices as low as possible. 
That means you can get a Master’s Degree Curriculum with a top-team of Data Scientists for a very affordable price.

By enrolling in the Starters Academy, at a fraction of the cost of other bootcamps or degrees, you’ll meet a highly-skilled community of fellow students as well as instructors from some of the top tech companies in Lisbon - you’ll become part of a community of excellence.

STUDENTS

250€

We're giving a 50% discount to all students who'll provide a valid student ID

REGULAR

500€

Invest in a game-changer subject for a fraction of its real cost

COMPANIES

1500€

Bring data science into your company through existing employees who already have institutional knowledge

PRICING PLAN

CURRICULUM

1. What do I need to know before applying to the Starters Academy?

You need to know how to program in Python and have basic software engineering skills, like being able to use the command line, being familiar with git and GitHub, and using a text editor to write code. You should also know some linear algebra basics and have used Numpy before.
You can check our
Data Science Prep Course Wiki Page and GitHub repo to learn or remind yourself of all these topics.


2. You also say that students should have strong quantitative skills. What does this mean?

You should be comfortable around numbers, quickly understanding a real life problem and translate it into something measurable & workable. Usually these are the kind of skills that result from having an education or working experience in the fields of (but not only) Engineering, Physics, Mathematics, Management, Economics, etc..


3. Will you teach Python at the Starters Academy? 

No. Knowing Python is actually a requirement to join the Starters Academy. But if you don’t know how to program in Python, you can check our Data Science Prep Course, that was designed to teach you everything you need to know for the Starters Academy.


4. I don’t know any data science, will I be able to do the classes?

Yes! The classes are directed at people who are giving their first steps in the data science world.


5. How does the admission process for the Starters Academy work?

The first step is to apply to the Starters Academy, during the applications phase. If you subscribe to our waiting list, you’ll receive an email with instructions for this when the time comes.
Then, during the test phase, you’ll have to solve a timed coding test and go through some introductory data science materials with exercises.


If you pass the test phase, you’ll be eligible to join the Starters Academy. We’ll take all the applicants who passed the test phase and randomly select the students for the Starters Academy. According to last year’s numbers, if you pass the test phase, you’ll have about a 50% probability of being selected.
If you’re selected, you’ll have 48 hours from the moment you received your acceptance email, to pay the admission fee and formally become a student of the Starters Academy!

Before applying, read the full details on the admission process here.


6. How do I know if my Python skills are good enough?

A good option is to apply and give the admission coding test a try. If you pass, it means that you know enough! :) In case you want a more detailed list of topics, check the Data Science Prep Course curriculum. The coding test will evaluate all these topics.


7. What will be the size of the student group?

We accept a maximum of 50 students in each edition of the Starters Academy. This year, due to the COVID-19 pandemic, we added an extra 10 seats for scholarships. If not filled, they will be converted to regular seats.

If you need a scholarship, you can learn how to apply here.


8. Will I have any kind of teaching support, if I need it, during the months following the Bootcamp Days?

Definitely yes! This is one of the strongest things we want to build during the Academy experience: the community effect! We are working hard to make sure you have access to a community of colleagues, teachers, and staff that will be there to help you in whatever way it's possible. Even you will be able to help others!


9. Is there any financial support if I can't afford the student fees?

Yes! If you want to apply to one of our scholarships, please check out our wiki page for more details.


10. Do you have a refund policy?

Please check our wiki here about this topic.


11. Will there be any kind of internship possibility at the end of the school?

We are working hard to surprise you on this topic. More news will come soon.


12. I can participate on the Bootcamp days, but can’t make it to the hackathons. Should I apply anyway?

Online presence in the Bootcamp and Hackathon #1 is compulsory.
For Hackathons #2 to #6, you are allowed to miss a hackathon (but you’ll still have to deliver it later).
Delivering the Capstone Project is also mandatory.


13. What language will the classes be held in?

English.


14. Are laptops provided?

No, you must bring a laptop with you, with at least 8GB of RAM.

If you’re planning on bringing your company’s laptop, make sure you have admin rights, because you’ll need to install some software.


15. When will we install the necessary software?

Setup must be done before arrival, we will provide step-by-step instructions and remote support.


16. Will batch 5 be 100% remote?

Yes, all teaching and mandatory events will be held online. But optional meetup, or conference, or social events will be organized in Lisbon. 


17. I’m not based in Portugal. Can I participate?

Yes! Since we’ll be going remote, this year’s batch is location-independent. However, all synchronous events will be held in the Portuguese timezone (WET). You need to attend these events at the time they are happening, regardless of where you are.


18. Are there any restrictions on the selection criteria?

Yes. Although we randomly select students that passed the coding test and practical exercises, we have a few (important) restrictions. More details on that can be found here.


19. Will I receive a certificate?

 If you successfully accomplish all requirements for graduation, then yes. Although we believe the learning is bigger than a certificate, we'll be providing one. The expected timeframe for certificate delivery is between 1 to 3 months after graduation.


20. I would like to check more information. Where can I check it?

Great question! Our absolute source of truth for the entire organization is our wiki. We strongly recommend that, before applying, that you check our wiki here.


21. Are the materials only accessible to students inside the Starters Academy?

No. We are advocates for open-source materials and you can find our materials in our GitHub repo (come back for updates on the link). You are free to follow the materials of the course independently but won't be able to attend the Bootcamp, hackathons, capstone, and have access to the instructors. And be part of an amazing community!


22. What is the required time commitment?

For that information (and other relevant ones), please refer to our wiki page.


We kindly remind you that - although most of our students are able to successfully find a job in Data Science - we can't guarantee it will actually land you a job and a career in tech. Additionally, the Starters Academy was designed to be taken as a sidekick, not your main activity.

TIMELINE

28th May

Applications start

28th June

Accepted students are announced

18th & 19th July

Bootcamp

2nd August

Hackathon #1

30th August

Hackathon #2

27th September

Hackathon #3

25th October

Hackathon #4

22nd November

Hackathon #5

10th January

Hackathon #6

Official calendar

GOT QUESTIONS?

ORGANISATIONS THAT MAKE

OUR ACADEMIES POSSIBLE

SCHOLARSHIPS

For this Batch, we are reserving 10 spots for scholarships for people that are in a no/low-income situation. If you are in this situation and interested to enroll at the Starters Academy, read about how to apply for a scholarship here

If granted, the pricing for scholarships is a symbolic fee of 20€.

SCHOLARSHIPS

For Batch 5, we are reserving 10 spots for scholarships for people that are in a no/low-income situation. 

If you are in this situation and interested to enroll at the Starters Academy, read about how to apply for a scholarship here.

If granted, the pricing for scholarships is a symbolic fee of 20€.

Join our mailing list to receive all news firsthand for the next edition of the Starters Academy

COURSE TIMELINE

Before applying, make sure that you visit our wiki to find all the updated information regarding the Starters Academy.

Susan

WHAT OUR STUDENTS SAY

LDSSA is an incredible experience if you are starting out in Data Science. I worked in the industry already and found the Academy very valuable to review foundations and meet new people with the same interests and in the same field. The course was very hands-on with real world explanations and problems and, more importantly, messy data! The final project was very interesting in the sense that we needed to solve a real problem scenario, given by an hypothetical client. We had to do everything from defining the project scope, building an acceptable solution given a series of constraints, deploying it in the cloud through an API and building a final report in order to present our solution and approach to the client. I have fine-tuned my knowledge in the area (pun intended) and at the same time I met a very friendly and determined group of instructors alongside some colleagues who are now great buddies. It's very rewarding to build something from the ground up and making it accessible for everyone to use!

“

Daniel

WHAT OUR STUDENTS SAY

I’ve been working as a software engineer and was looking for a career change for more than a year, going through LDSA (Starter Academy) was something that I was thinking a lot of. At first, I wasn’t sure that a fully online course with high requirements would be possible (even to me, a developer) to make it out, but I've learned so much and met a lot of incredible people! And now I'm getting, even more, closer to achieve my goal of becoming a Data Scientist! Thank you so much! LDSA you are Amazing!

“

WHAT OUR STUDENTS SAY

Istarted LDSA after my Master's in Data Science and Advanced Analytics. It really consolidated my knowledge in Data Science and added Recommender Systems and Time Series to my repertoire.
I loved how well structured the course was, how much depth it went into and the specialised hackathons challenged us to put the new knowledge into practice straight away. I am inspired by the quality of the material, the dedication of the teachers and the commitment of the fellow students. It was not an easy undertaking, but it's worth every ounce of effort you put in.

“

Juliane

WHAT OUR STUDENTS SAY

Istarted LDSA after my Master's in Data Science and Advanced Analytics. It really consolidated my knowledge in Data Science and added Recommender Systems and Time Series to my repertoire.
I loved how well structured the course was, how much depth it went into and the specialised hackathons challenged us to put the new knowledge into practice straight away. I am inspired by the quality of the material, the dedication of the teachers and the commitment of the fellow students. It was not an easy undertaking, but it's worth every ounce of effort you put in.

“

Juliane

Signup NowQuestions? Check out our Wiki

WHAT OUR STUDENTS SAY

LDSSA is an incredible experience if you are starting out in Data Science. I worked in the industry already and found the Academy very valuable to review foundations and meet new people with the same interests and in the same field. The course was very hands-on with real world explanations and problems and, more importantly, messy data! The final project was very interesting in the sense that we needed to solve a real problem scenario, given by an hypothetical client. We had to do everything from defining the project scope, building an acceptable solution given a series of constraints, deploying it in the cloud through an API and building a final report in order to present our solution and approach to the client. I have fine-tuned my knowledge in the area (pun intended) and at the same time I met a very friendly and determined group of instructors alongside some colleagues who are now great buddies. It's very rewarding to build something from the ground up and making it accessible for everyone to use!

“

Daniel

Susan

WHAT OUR STUDENTS SAY

I’ve been working as a software engineer and was looking for a career change for more than a year, going through LDSA (Starter Academy) was something that I was thinking a lot of. At first, I wasn’t sure that a fully online course with high requirements would be possible (even to me, a developer) to make it out, but I've learned so much and met a lot of incredible people! And now I'm getting, even more, closer to achieve my goal of becoming a Data Scientist! Thank you so much! LDSA you are Amazing!

“

WHAT OUR STUDENTS SAY

I started LDSA after my Master's in Data Science and Advanced Analytics. It really consolidated my knowledge in Data Science and added Recommender Systems and Time Series to my repertoire.
I loved how well-structured the course was, how much depth it went into and the specialised hackathons challenged us to put the new knowledge into practice straight away. I am inspired by the quality of the material, the dedication of the teachers and the commitment of the fellow students. It was not an easy undertaking, but it's worth every ounce of effort you put in.

“

Juliane

+ +
+ +
+ +
+
+
+
Subscribe
+
+
+
+
Join our mailing list to receive all news firsthand for the next edition of the Starters Academy
+
+
+
+
+ + +
+ + +
+ + + +
+ + +
+
+
Thanks! Keep an eye on your inbox for updates.
+
+
+
+
+
HomeAbout UsStarters AcademyPrep CourseJobs pageContacts

© 2020 Lisbon Data Science Academy. 

All rights reserved

Get in touch!

info@lisbondatascience.org

© 2022 Lisbon Data Science Academy.  All rights reserved

Signups

17th June to 30th June

1st July to 21st July

Test & SLU

22nd July

Candidates Selection

23rd to 27th July

Payments & scholarship interviews

Official calendar

23rd to 28th September

APPLICATIONS TIMELINE

COURSE TIMELINE

Official calendar

Hackathon #1

20th October 2024

Hackathon #3

Capstone Project

Setup and Introduction

17th November 2024

Hackathon #2

15th December 2025

2nd February 2025

Hackathon #4

2nd March 2025

Hackathon #5

30th March 2025

Hackathon #6

7th April to 15th June 2025

+ + \ No newline at end of file diff --git a/migration/redirect-map.md b/migration/redirect-map.md new file mode 100644 index 0000000..1978a98 --- /dev/null +++ b/migration/redirect-map.md @@ -0,0 +1,19 @@ +# Redirect map + +Cloudflare Pages reads these rules from `public/_redirects`. + +| Source | Destination | Status | Reason | +| ---------------------------------------------------- | ------------------------------- | ------ | ------------------------------------- | +| `/curriculum` and `/curriculum/` | `/starters-academy/#curriculum` | 301 | Consolidated, maintained curriculum | +| `/faq` and `/faq/` | `/starters-academy/#faq` | 301 | Consolidated, maintained FAQ | +| `/home` | `/` | 301 | Canonical homepage | +| `/index.html` | `/` | 301 | Canonical homepage | +| `/comms-associate-position[/]` | `/about-us/` | 301 | Expired 2018 volunteer vacancy | +| `/pythonzerotohero[/]` | `/prep-course/` | 301 | Historical predecessor course | +| `/a-form_confirmation.html` | `/` | 301 | Retired Unbounce confirmation route | +| `/b-form_confirmation.html` | `/` | 301 | Retired Unbounce confirmation route | +| `/d-form_confirmation.html` | `/` | 301 | Retired homepage confirmation route | +| `/comms-associate-position/a-form_confirmation.html` | `/about-us/` | 301 | Retired vacancy confirmation route | +| `/curriculum/a-form_confirmation.html` | `/starters-academy/` | 301 | Retired curriculum confirmation route | + +These rules cover every distinct public or archived same-domain route found during the public-evidence audit. Unknown, account-only Unbounce URLs cannot be recovered from the suspended account and are tracked as a documented limitation rather than a release blocker. diff --git a/migration/script-inventory.md b/migration/script-inventory.md new file mode 100644 index 0000000..f6d41a6 --- /dev/null +++ b/migration/script-inventory.md @@ -0,0 +1,13 @@ +# Script inventory + +| Existing script | Classification | Replacement | +| --------------------------------------------- | -------------- | --------------------------------------------------------------- | +| Unbounce runtime and conversion tracker | Remove | Cloudflare Pages hosting and Web Analytics | +| Unbounce jQuery 1.4.2 and compatibility shims | Remove | Standards-based browser JavaScript for the mobile menu | +| Unbounce form handler | Remove | Direct Flodesk forms | +| Google Analytics property `UA-113478482-1` | Replace | Cloudflare Web Analytics and Core Web Vitals | +| Flodesk universal form script | Keep | Loaded only on pages that contain a Flodesk form | +| YouTube iframe | Keep | Privacy-enhanced `youtube-nocookie.com` embed with lazy loading | +| Generated animation observer | Remove | Static CSS and reduced-motion support | + +No Google Tag Manager, Meta Pixel or LinkedIn Insight Tag was found in the public rendered pages. The suspended Unbounce account prevents inspection of disabled or unapplied Script Manager entries. Publicly executed scripts have been audited; non-public account configuration is an accepted limitation. diff --git a/migration/scripts/audit-snapshot.mjs b/migration/scripts/audit-snapshot.mjs new file mode 100644 index 0000000..6c14c6d --- /dev/null +++ b/migration/scripts/audit-snapshot.mjs @@ -0,0 +1,42 @@ +import { readdirSync, readFileSync } from "node:fs"; + +const snapshotDirectory = new URL("../raw/", import.meta.url); +const files = readdirSync(snapshotDirectory) + .filter((file) => file.endsWith(".html")) + .sort(); + +const matches = (source, expression) => + [...source.matchAll(expression)].map((match) => match[1]); + +const unique = (values) => [...new Set(values)].sort(); + +for (const file of files) { + const source = readFileSync(new URL(file, snapshotDirectory), "utf8"); + const result = { + file, + title: matches(source, /(.*?)<\/title>/gis)[0] ?? null, + description: + matches(source, /<meta name="description" content="([^"]*)"/gi)[0] ?? + null, + unbouncePageId: + matches(source, /window\.ub = \{"page":\{"id":"([^"]+)/g)[0] ?? null, + flodeskFormIds: unique( + matches(source, /https:\/\/form\.flodesk\.com\/forms\/([^/"']+)/g), + ), + flodeskSubmitTokens: unique( + matches(source, /name="Flodesk_Submit_Token"[^>]+value="([^"]+)/g), + ), + internalPaths: unique( + matches( + source, + /(?:href|data-action-url)="(?:clkn?\/)?(?:https?:)?(?:\/\/)?(?:www\.)?lisbondatascience\.org([^"?# ]*)/gi, + ).map((path) => path || "/"), + ), + imageUrls: unique( + matches(source, /data-src-(?:desktop|mobile)-[123]x="([^"]+)/g), + ), + scriptUrls: unique(matches(source, /<script[^>]+src="([^"]+)/gi)), + }; + + process.stdout.write(`${JSON.stringify(result, null, 2)}\n`); +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..5b2563e --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5080 @@ +{ + "name": "@ldssa/website", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@ldssa/website", + "version": "1.0.0", + "dependencies": { + "@astrojs/sitemap": "3.7.4", + "astro": "7.3.8" + }, + "devDependencies": { + "@astrojs/check": "0.9.10", + "prettier": "3.9.9", + "prettier-plugin-astro": "1.1.0", + "typescript": "5.9.3" + }, + "engines": { + "node": ">=24.0.0" + } + }, + "node_modules/@astrojs/astro2tsx": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@astrojs/astro2tsx/-/astro2tsx-0.1.2.tgz", + "integrity": "sha512-Uj/M9zbrhu5cIZEvWAGTbQJRKSxXWJ/E59BZwt7F+chrKhbfttW+9rkS/l+1QxoJP1KU3UnpM62M7yszfAI0WA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@emnapi/core": "1.11.3", + "@napi-rs/wasm-runtime": "1.2.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/check": { + "version": "0.9.10", + "resolved": "https://registry.npmjs.org/@astrojs/check/-/check-0.9.10.tgz", + "integrity": "sha512-zgx/UQMozdjOa3bOxjgeCFdtpE3c9rRX6xHwa+2QXvy8z8Akifu2AtubHyv/zzC2znO8dl8fFWL4K+Ba9kS8HQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@astrojs/language-server": "^2.16.7", + "chokidar": "^4.0.3", + "kleur": "^4.1.5", + "yargs": "^18.0.0" + }, + "bin": { + "astro-check": "bin/astro-check.js" + }, + "peerDependencies": { + "typescript": "^5.0.0 || ^6.0.0" + } + }, + "node_modules/@astrojs/compiler-binding": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding/-/compiler-binding-0.5.1.tgz", + "integrity": "sha512-oczyolLUl9mNrtuBuNjuvn5thpHrFLXAHAWGTRg0JDHQ8cGVsVeFekOu2uSoQGgainJIi+A3/9m0DyYwz3Nlnw==", + "license": "MIT", + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@astrojs/compiler-binding-android-arm64": "0.5.1", + "@astrojs/compiler-binding-darwin-arm64": "0.5.1", + "@astrojs/compiler-binding-darwin-x64": "0.5.1", + "@astrojs/compiler-binding-linux-arm64-gnu": "0.5.1", + "@astrojs/compiler-binding-linux-arm64-musl": "0.5.1", + "@astrojs/compiler-binding-linux-x64-gnu": "0.5.1", + "@astrojs/compiler-binding-linux-x64-musl": "0.5.1", + "@astrojs/compiler-binding-wasm32-wasi": "0.5.1", + "@astrojs/compiler-binding-win32-arm64-msvc": "0.5.1", + "@astrojs/compiler-binding-win32-x64-msvc": "0.5.1" + } + }, + "node_modules/@astrojs/compiler-binding-android-arm64": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-android-arm64/-/compiler-binding-android-arm64-0.5.1.tgz", + "integrity": "sha512-nMb2nMIJtcWLNH+fV75Enmi+jp4tIOYOfsv4XtpgdOWkqc9P4uSSdK5u1xb340AzrXo5V18SxLl/nc3vcqtamQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-darwin-arm64": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-arm64/-/compiler-binding-darwin-arm64-0.5.1.tgz", + "integrity": "sha512-5k4+nZ/WRt+dcVow4z7rBuI7iv9A58h/uQ91YJe1PrTivbcCkq3u2Ynx/AZJwII4CsZawBCeGpojkFjutZBhBQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-darwin-x64": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-x64/-/compiler-binding-darwin-x64-0.5.1.tgz", + "integrity": "sha512-0Buw+YST9Ff2EqQyhRMQujuFq/dijLw3zXWhsvI0dJ2pGrT38iVoxTEca2eBX/PfhlwkVWSRJEKO5gMqKHyeHg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-arm64-gnu": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-gnu/-/compiler-binding-linux-arm64-gnu-0.5.1.tgz", + "integrity": "sha512-x5LRpyCoMKuu3INr18cn42YhwSOaRpp9b5Ny3eJJ/sWVeejC16Zeshu1JlvlSmEZACLeWxU5JM0RX6T6nI+YyA==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-arm64-musl": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-musl/-/compiler-binding-linux-arm64-musl-0.5.1.tgz", + "integrity": "sha512-mL7tQEuDk3rGfaKmrO63nw6WBy1TPVioQGmVyjcAr+C+a/k3SVMYBytNSMqH9mZPT/Swj30JaZ2N842Z1NnOEQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-x64-gnu": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-gnu/-/compiler-binding-linux-x64-gnu-0.5.1.tgz", + "integrity": "sha512-uLGc9jtxwiDe4cQyhNPQim4ECiB/YzbWlXssfLAv8r374kArfTwBHjwR2+j+XVyF71yCx5mF9YNRclRqdNBgeg==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-x64-musl": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-musl/-/compiler-binding-linux-x64-musl-0.5.1.tgz", + "integrity": "sha512-G0cywinaQUc8v1MrytvYKUX7svTDMmYtOCZfxjwKPbJi59zIqM+Ll0rWgO0GQWLZH3XoPYo19ZwlP3Xt9ep9YA==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-wasm32-wasi": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-wasm32-wasi/-/compiler-binding-wasm32-wasi-0.5.1.tgz", + "integrity": "sha512-yMhLL8asmLydoAdnEZxAw6eLfNDs3YJl6jqn0pjPt6zrb2U5qIDLnii4/twThpIFODKkQ6YDpBRTOz/vSfZCxw==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.3", + "@emnapi/runtime": "1.11.3", + "@napi-rs/wasm-runtime": "^1.2.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-win32-arm64-msvc": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-arm64-msvc/-/compiler-binding-win32-arm64-msvc-0.5.1.tgz", + "integrity": "sha512-aClIG6poFt3Wf/bZykrEAX2nAnKvBHW6RXiKUa69QjBtWH0/FOlOAjnFcxRNlhej2AMWNYFx0o+LGjwMcH8P7Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-win32-x64-msvc": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-x64-msvc/-/compiler-binding-win32-x64-msvc-0.5.1.tgz", + "integrity": "sha512-/E6uBCagY70YpWqssBC+/FuniNSloeQPRgj7l3LDWiqjxIyTLnIgX1sTt18FU7jrigetpSnC9cHAM6b6IxSgPw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-rs": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-rs/-/compiler-rs-0.5.1.tgz", + "integrity": "sha512-1k1acnX7DepIiea9U9/Y+d1hdAFfylu1ZQQkkYgRSwBOBAsXInolyZuh/2tax+83vu/A+hOYP6P3+265RvMTvw==", + "license": "MIT", + "dependencies": { + "@astrojs/compiler-binding": "0.5.1" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@astrojs/internal-helpers": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.12.0.tgz", + "integrity": "sha512-pKPuwCDtO0L7zgmt1JGd9X4Aes1+0SX3IEBSqBNPHsScYdPMlfmed3Y7DTbXyF7E+8ylnb563o/0OzGQPOFehw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.4", + "@types/mdast": "^4.0.4", + "picomatch": "^4.0.4", + "retext-smartypants": "^6.2.0", + "shiki": "^4.0.2", + "smol-toml": "^1.8.0", + "unified": "^11.0.5", + "yaml": "^2.9.1" + } + }, + "node_modules/@astrojs/language-server": { + "version": "2.17.2", + "resolved": "https://registry.npmjs.org/@astrojs/language-server/-/language-server-2.17.2.tgz", + "integrity": "sha512-WzqCBYo0RtKrkPLPMnTz2+UOQPosaFhoG/PvoSyPIgBU0BKeS6qeaYv7Rscq7P1LxJdoN0VosQF2h2TZTsIGHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@astrojs/astro2tsx": "^0.1.2", + "@astrojs/yaml2ts": "^0.2.4", + "@volar/kit": "~2.4.28", + "@volar/language-core": "~2.4.28", + "@volar/language-server": "~2.4.28", + "@volar/language-service": "~2.4.28", + "muggle-string": "^0.4.1", + "tinyglobby": "^0.2.17", + "volar-service-css": "0.0.71", + "volar-service-emmet": "0.0.71", + "volar-service-html": "0.0.71", + "volar-service-prettier": "0.0.71", + "volar-service-typescript": "0.0.71", + "volar-service-typescript-twoslash-queries": "0.0.71", + "volar-service-yaml": "0.0.71", + "vscode-html-languageservice": "^5.6.2", + "vscode-uri": "^3.2.0" + }, + "bin": { + "astro-ls": "bin/nodeServer.js" + }, + "peerDependencies": { + "prettier": "^3.0.0", + "prettier-plugin-astro": ">=0.11.0" + }, + "peerDependenciesMeta": { + "prettier": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + } + } + }, + "node_modules/@astrojs/markdown-satteri": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-satteri/-/markdown-satteri-0.4.3.tgz", + "integrity": "sha512-PFbEHPnwUlLvpXd7RXrrKM+j12akF4bG+DSuNuoN+3eiwjOwp4HaPi+QQdbkBV7QEYXmzyKca50OJsmGxhNIUA==", + "license": "MIT", + "dependencies": { + "@astrojs/internal-helpers": "0.12.0", + "@astrojs/prism": "4.0.2", + "github-slugger": "^2.0.0", + "satteri": "^0.10.3" + } + }, + "node_modules/@astrojs/prism": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-4.0.2.tgz", + "integrity": "sha512-KTivpmnz6lDsC6o9H4+DNm2SrE/GHzw8cNAvEJwAvUT+eoaEnn/4NtbDNfRRaxaJHdp15gf+tfHAWiXR4wB3BA==", + "license": "MIT", + "dependencies": { + "prismjs": "^1.30.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@astrojs/sitemap": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.7.4.tgz", + "integrity": "sha512-LbKNC24bdUWcQf/pThB6qLlSqHojxGjZDURIzFocY8rlWnAn2t74nnhnK6S5x0NHriHoAduLEpVjRykmeGiVvA==", + "license": "MIT", + "dependencies": { + "sitemap": "^9.0.0", + "zod": "^4.3.6" + } + }, + "node_modules/@astrojs/telemetry": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.3.tgz", + "integrity": "sha512-C1TLn5sPJr0x4vk56piHWKbnqlEB8BKyte5Y45V02U+D7BGO5eMqZDH5aPjnkXQWJggvmsTXxH03QMZ9NgWLzQ==", + "license": "MIT", + "dependencies": { + "ci-info": "^4.4.0", + "dset": "^3.1.4", + "is-docker": "^4.0.0", + "package-manager-detector": "^1.6.0" + }, + "engines": { + "node": "18.20.8 || ^20.3.0 || >=22.0.0" + } + }, + "node_modules/@astrojs/yaml2ts": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@astrojs/yaml2ts/-/yaml2ts-0.2.4.tgz", + "integrity": "sha512-8oddpOae35pJsXPQXhTkM0ypfKPskVsh2bCxRtbf7e+/Epw2nReakFYpLKjZMEr75CsoF203PMnCocpfz0s69A==", + "dev": true, + "license": "MIT", + "dependencies": { + "yaml": "^2.8.3" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz", + "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bruits/satteri-darwin-arm64": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-arm64/-/satteri-darwin-arm64-0.10.5.tgz", + "integrity": "sha512-27KTVl4TJkVahMy/ohyA7qd4938G5UNneFUz/PsScYfpIhj0IVAS23mpcJXdPF44sa6nva198lmV/cKIb2YPyA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@bruits/satteri-darwin-x64": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-x64/-/satteri-darwin-x64-0.10.5.tgz", + "integrity": "sha512-IjnLe3nKspq6qaeqGgjT7MT8VrTV74yWRlaag7ZdNsI8TDAYZ0iPxMCo+9KQZHUk5EyVB+reBI/PFWL5KuFw9Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@bruits/satteri-linux-arm64-gnu": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-gnu/-/satteri-linux-arm64-gnu-0.10.5.tgz", + "integrity": "sha512-glkYXZCJywjP13v67eAyAMSJdF+ncvEbYvgi/wOtffL9tQ27lr/zsyzUfgs+ovjJ9d8JNQKiXeiArJcX8PJL9w==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-arm64-musl": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-musl/-/satteri-linux-arm64-musl-0.10.5.tgz", + "integrity": "sha512-yWdgG1g17Nh2QyGVlFUxGRa3FEFwiMcpZEyMNWkbM3deC94cmVc+/i9OuyFpdKuWo3GkgoCtYVOoxk1uCnCZIA==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-x64-gnu": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-gnu/-/satteri-linux-x64-gnu-0.10.5.tgz", + "integrity": "sha512-FVaLoPT1fBgGl0J+AYebyyXJYBachGl8Oyyrf1lye4RTqCB4S0Gwkj1uM9RJyThUOvx5VUmAT1CnNh1SFHA+kw==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-x64-musl": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-musl/-/satteri-linux-x64-musl-0.10.5.tgz", + "integrity": "sha512-EHpVAx2bqW3GINHTKkljtxVfQmVDGWIuwOYOP5YghTj+0PkBa2o8oKPRtQ9Kbsr1Fye8jtUcDjhwj2jMNugZKg==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-wasm32-wasi": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-wasm32-wasi/-/satteri-wasm32-wasi-0.10.5.tgz", + "integrity": "sha512-ypz8c/Zmipxp4IoeDa228Gstv6TLzVmNs3yC6wKCoNSOjx1iwpgzu87Y3hTkXFdwChVGU85qeUDuOIarGUZQLw==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.2.3" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@bruits/satteri-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@bruits/satteri-win32-arm64-msvc": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-arm64-msvc/-/satteri-win32-arm64-msvc-0.10.5.tgz", + "integrity": "sha512-siTV88nb0LRqNpkL2gXboqCwVdq95sLtzMHS1/3eONV2gLbB3NAK46wmSMvCO/yquBvI2lvaFIfd8P12ecsxBw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@bruits/satteri-win32-x64-msvc": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-x64-msvc/-/satteri-win32-x64-msvc-0.10.5.tgz", + "integrity": "sha512-C3IfPvfvMXmlzBxaMPKFS1XiuV9pu2mC7YqkPk7PSvTgPZ8gbdASIpHpztDLvTTQjqZ0z1Ol8tK5X+V6XXC0wQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@capsizecss/unpack": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-4.0.1.tgz", + "integrity": "sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ==", + "license": "MIT", + "dependencies": { + "fontkitten": "^1.0.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@clack/core": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.5.1.tgz", + "integrity": "sha512-iHTrHA8MtVuLl2TfZySmcKv1qO2PoyC9Z7pfSDozEuV5vtY3/wcOPKJXlqJ5Oq2Cx5DDGQGAMVx6HZfRRoVEbQ==", + "license": "MIT", + "dependencies": { + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/@clack/prompts": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.8.1.tgz", + "integrity": "sha512-dlT1m5e/0yUL0kRNcQn7yGLVThkgbB0Ga/1AmfDDC/8ik6AIiSf2QLQO2zPYvefsHP0aFgxO93cVLCCfDp7kzQ==", + "license": "MIT", + "dependencies": { + "@clack/core": "1.5.1", + "fast-string-width": "^3.0.2", + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/@emmetio/abbreviation": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@emmetio/abbreviation/-/abbreviation-2.3.3.tgz", + "integrity": "sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@emmetio/scanner": "^1.0.4" + } + }, + "node_modules/@emmetio/css-abbreviation": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@emmetio/css-abbreviation/-/css-abbreviation-2.1.8.tgz", + "integrity": "sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@emmetio/scanner": "^1.0.4" + } + }, + "node_modules/@emmetio/css-parser": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@emmetio/css-parser/-/css-parser-0.4.1.tgz", + "integrity": "sha512-2bC6m0MV/voF4CTZiAbG5MWKbq5EBmDPKu9Sb7s7nVcEzNQlrZP6mFFFlIaISM8X6514H9shWMme1fCm8cWAfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@emmetio/stream-reader": "^2.2.0", + "@emmetio/stream-reader-utils": "^0.1.0" + } + }, + "node_modules/@emmetio/html-matcher": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@emmetio/html-matcher/-/html-matcher-1.3.0.tgz", + "integrity": "sha512-NTbsvppE5eVyBMuyGfVu2CRrLvo7J4YHb6t9sBFLyY03WYhXET37qA4zOYUjBWFCRHO7pS1B9khERtY0f5JXPQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@emmetio/scanner": "^1.0.0" + } + }, + "node_modules/@emmetio/scanner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@emmetio/scanner/-/scanner-1.0.4.tgz", + "integrity": "sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@emmetio/stream-reader": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@emmetio/stream-reader/-/stream-reader-2.2.0.tgz", + "integrity": "sha512-fXVXEyFA5Yv3M3n8sUGT7+fvecGrZP4k6FnWWMSZVQf69kAq0LLpaBQLGcPR30m3zMmKYhECP4k/ZkzvhEW5kw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@emmetio/stream-reader-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@emmetio/stream-reader-utils/-/stream-reader-utils-0.1.0.tgz", + "integrity": "sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@emnapi/core": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.3.tgz", + "integrity": "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@emnapi/wasi-threads": "1.2.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.3.tgz", + "integrity": "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.5.tgz", + "integrity": "sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.3.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.5.tgz", + "integrity": "sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.3.4" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.5.tgz", + "integrity": "sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw==", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.4.tgz", + "integrity": "sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.4.tgz", + "integrity": "sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.4.tgz", + "integrity": "sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.4.tgz", + "integrity": "sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.4.tgz", + "integrity": "sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.4.tgz", + "integrity": "sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.4.tgz", + "integrity": "sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.4.tgz", + "integrity": "sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.4.tgz", + "integrity": "sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.4.tgz", + "integrity": "sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.5.tgz", + "integrity": "sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.3.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.5.tgz", + "integrity": "sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.3.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.5.tgz", + "integrity": "sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.3.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.5.tgz", + "integrity": "sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.3.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.5.tgz", + "integrity": "sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.3.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.5.tgz", + "integrity": "sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.3.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.5.tgz", + "integrity": "sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.3.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.5.tgz", + "integrity": "sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.3.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.5.tgz", + "integrity": "sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ==", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.5.tgz", + "integrity": "sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/sharp-wasm32": "0.35.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.5.tgz", + "integrity": "sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.5.tgz", + "integrity": "sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.5.tgz", + "integrity": "sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "license": "MIT" + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.4.tgz", + "integrity": "sha512-AJxoUD2/15ESHbvpcyjU274nsAPLuOtPHCk0vKJM5pj//Fg/B1FXNWjPnXTT9PymCYYiHo4zPj0ZomXBKhoy7g==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" + } + }, + "node_modules/@oslojs/encoding": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", + "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==", + "license": "MIT" + }, + "node_modules/@oxc-project/types": { + "version": "0.153.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.153.0.tgz", + "integrity": "sha512-jSNrRIyvPVLL5E1z/tS5zLAprosGz/FuI1PWwEOz59j3E4TTTzSpqKb/RDGqJhhW0QHYISGXxq582wGx6T11Tw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/oxc-project" + } + }, + "node_modules/@prettier/parse-srcset": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@prettier/parse-srcset/-/parse-srcset-3.1.0.tgz", + "integrity": "sha512-FIRv2rZotO9NP/r66taYqD1zICpPiBbrECqjKV/xqNotqDxF7fLzzUeK+1RSRx9Tenk0DajR/GcRmTJ2qtHaKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.13.tgz", + "integrity": "sha512-Vu/NICrgdRvUOeaJZIyB2FP63s6fO/4ud5l8dXmxDx9yDyn+mN0y+gsvsVviSli+T361Aqo0h46EuMoobrwawA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.13.tgz", + "integrity": "sha512-6LEoIrGy5vvCd2boNnv2soPSUZj/PRvhp8R/t9KlX+mIZxThIE/ZqxrGXxmuNX0FuvQFvLdZr7S4HrYpYBVhEQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.13.tgz", + "integrity": "sha512-CaJSRzlQ6Q+zkDMVA9OkVxBjVoPRCDEE3v0FSJ9kIQBgRjFApA3Si4ijhbNVUji7ZJt+PfO0e46qy4nPInHUPA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.13.tgz", + "integrity": "sha512-e4r340I5hxSzJkD3YJBxgzq/rJ3wwxwdLLb2FPt1Zpyem20nrQPT11DDEEkazwxaHumH8wpglsp/Ozgj/wT6mw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.13.tgz", + "integrity": "sha512-hoBWDZ4/K/mJKVMbsgUd1jfpA2DiOrJ54mpegObU9TBHBBrtX4FLIXJJQRDPZ/2Dn91GvPob+Z2hwKJzf4E5SQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.13.tgz", + "integrity": "sha512-oF9fZu3vTWnTiVAkDFxU9PJLt1nh20K9RRpuHsR7ZnE2L7XookBbDBVLojCVSLGbvtSlxeUNnjAj6DbNsKyzbg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.13.tgz", + "integrity": "sha512-DTpb/+jMiqWMPgphn4ksW1zW+XLJ6dJJ9FMvnIk9I291LhCctMPhMYJrP5t9p6C9qa3KXadDdUEZHBaPh3kNxQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.13.tgz", + "integrity": "sha512-xU+Kvv9N2A3SY35zFh1FWWNjxbadE/BKeqnkXe7vScU4GZsw757BlVmDLITZEq8DPpk6m7uoDYkK7P+x/uU5Ow==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.13.tgz", + "integrity": "sha512-XMk/hnhh25oQ756Lclb1Q11h4ll0xC0Esa5w4Vzp+mAE2S3AGJgeM0SonrIgY8fHhjarb6hcoTuwoFG0VVP2ig==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.13.tgz", + "integrity": "sha512-U1BoTHYmlywE2n3HUWjzZ+8EQsKr+OwpxYuDWaubx42tUL8QSL7nOqRz/NVHanibbFP3M1Rxuprz/wNcbMKjqw==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.13.tgz", + "integrity": "sha512-J4cm80Pc6wEwV5AZ42o62sIQgDk+Y7R2s646jIwuvO+4V46QB58N4vVuMPWCBaNgursXLpmUWJV//WQAkPvHBQ==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.13.tgz", + "integrity": "sha512-W0euZS40U4V6JkmEnVCIX9mcsL6cbXQ4X8c2w+MMOIjFg68TXUW+u3RT+2e1t1pv9mg/EP3pDtK3POXdQ7Rxyw==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.13.tgz", + "integrity": "sha512-do/AF/4n9MRshia3a5aXoy8xM2c6mgfT6D7H/dY6my5z+hfCIR4ab7mk4IRj++nsEQpKy39kd0DQnDq+r692tw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.13.tgz", + "integrity": "sha512-6FtUf2cJZobutJdxnGYX8z0vtbgqU0b2v8vfBhkXb9wp2MppMFtkunE+IaOntyi01rkIQoPW2GHABtfzsC0BRQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.13.tgz", + "integrity": "sha512-UKhFaZTeTcbKrb3iU82m17jVcPEAoqDkCN5MBqgPyZSwp/cQmib21crTS0DdH7T1B7wZRWEI89YQAOJMet5yhg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "license": "MIT" + }, + "node_modules/@shikijs/core": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.5.0.tgz", + "integrity": "sha512-6CIgwq+bt2JZ3OSjb/nBkkwEf/CfpHcbam+Mg5Pllco7HAGRQ2/EFhZrrJtbyNTgKwknfVS+llsXGygiDGwJWw==", + "license": "MIT", + "dependencies": { + "@shikijs/primitive": "4.5.0", + "@shikijs/types": "4.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5", + "hast-util-to-html": "^9.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.5.0.tgz", + "integrity": "sha512-Ed6UdEj84LyYkwnxM5CvSMWAD+id9ExTYri0/+x41U9zrgNifPSS9lZm0JqBP0VfYI8yr8tY5nbNq9+p9R0kFA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^4.3.6" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.5.0.tgz", + "integrity": "sha512-dKVmbJecVdB9recl8a/CU4jdpNXyjJQrIcesIkpS70s5HS02ohpnCSpMUbotPnEUzbAcLQ+S4hwZYbkBBiTcJw==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.5.0", + "@shikijs/vscode-textmate": "^10.0.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/langs": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.5.0.tgz", + "integrity": "sha512-wwBTqwduXSz0X4v9Qa2EWeFaYJEw1clCncWjoRNQRbVDu3SPRTRWucnouYapKj3b8Q+sk/41Vbc4B5c8nuYzLA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.5.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/primitive": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.5.0.tgz", + "integrity": "sha512-UM7ShlA8oz0OWxLLO8BmO9Ip6fVqdkt8yF0EH7nOCqHfrx1eEAzF0ZUVK7L+I8NEgeu1PEWSRlxaiPPeCb4iDg==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/themes": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.5.0.tgz", + "integrity": "sha512-zGb87H3UBbpuI4yoh5UguB1XbU+2BDU3kflM7UfZ+Cy2hWo9lBtqjSVK20gmLY6ukYl040hAypGXMgud9vul8Q==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.5.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/types": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.5.0.tgz", + "integrity": "sha512-drtPrtD8N0DENyD2cyt6Ajy+IWsiCLgMxODwLTpvH1UsN6a9jf90tNTPjqwHvA+0Lzu9e0txk8DjfqH1p9UJRw==", + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.4.tgz", + "integrity": "sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "license": "MIT" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", + "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/nlcst": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz", + "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/node": { + "version": "24.19.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.19.1.tgz", + "integrity": "sha512-aS3/DG0oM05K0RIXXP+hKjinGG5IgSSVGzswZxW3O0sS3pH4/fycXundUC9XsszgKCk4gHXylTEK6hyFxVxnoQ==", + "license": "MIT", + "dependencies": { + "undici-types": ">=7.24.0 <7.24.7" + } + }, + "node_modules/@types/sax": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", + "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.4.0.tgz", + "integrity": "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==", + "license": "ISC" + }, + "node_modules/@volar/kit": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/kit/-/kit-2.4.28.tgz", + "integrity": "sha512-cKX4vK9dtZvDRaAzeoUdaAJEew6IdxHNCRrdp5Kvcl6zZOqb6jTOfk3kXkIkG3T7oTFXguEMt5+9ptyqYR84Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-service": "2.4.28", + "@volar/typescript": "2.4.28", + "typesafe-path": "^0.2.2", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.28.tgz", + "integrity": "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.28" + } + }, + "node_modules/@volar/language-server": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-server/-/language-server-2.4.28.tgz", + "integrity": "sha512-NqcLnE5gERKuS4PUFwlhMxf6vqYo7hXtbMFbViXcbVkbZ905AIVWhnSo0ZNBC2V127H1/2zP7RvVOVnyITFfBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "@volar/language-service": "2.4.28", + "@volar/typescript": "2.4.28", + "path-browserify": "^1.0.1", + "request-light": "^0.7.0", + "vscode-languageserver": "^9.0.1", + "vscode-languageserver-protocol": "^3.17.5", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@volar/language-service": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/language-service/-/language-service-2.4.28.tgz", + "integrity": "sha512-Rh/wYCZJrI5vCwMk9xyw/Z+MsWxlJY1rmMZPsxUoJKfzIRjS/NF1NmnuEcrMbEVGja00aVpCsInJfixQTMdvLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "vscode-languageserver-protocol": "^3.17.5", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz", + "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.28", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.28.tgz", + "integrity": "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.28", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vscode/emmet-helper": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@vscode/emmet-helper/-/emmet-helper-2.11.0.tgz", + "integrity": "sha512-QLxjQR3imPZPQltfbWRnHU6JecWTF1QSWhx3GAKQpslx7y3Dp6sIIXhKjiUJ/BR9FX8PVthjr9PD6pNwOJfAzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "emmet": "^2.4.3", + "jsonc-parser": "^2.3.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.15.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vscode/emmet-helper/node_modules/jsonc-parser": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", + "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vscode/l10n": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz", + "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-i18n": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ajv-i18n/-/ajv-i18n-4.2.0.tgz", + "integrity": "sha512-v/ei2UkCEeuKNXh8RToiFsUclmU+G57LO1Oo22OagNMENIw+Yb8eMwvHu7Vn9fmkjJyv6XclhJ8TbuigSglPkg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^8.0.0-beta.0" + } + }, + "node_modules/am-i-vibing": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/am-i-vibing/-/am-i-vibing-0.5.0.tgz", + "integrity": "sha512-KLcsDhqf1XRrJmJr+9I/C0EX2K+sGrAGZFgkmd84c8TJO9zJBu+/kFInjazu1KfJWFhWDKdRWkpBzEs7s35DVA==", + "license": "MIT", + "dependencies": { + "process-ancestry": "^0.1.0" + }, + "bin": { + "am-i-vibing": "dist/cli.mjs" + } + }, + "node_modules/ansi-regex": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/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", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/astro": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/astro/-/astro-7.3.8.tgz", + "integrity": "sha512-LHzDHSaV+YzZiR3EFndDYTLLeNeuMtP1qX1FLcCNUUHOCqTTIWPczy2UV3zpA5VqvJe5cWZolqM9Z8JFqR2i9w==", + "license": "MIT", + "dependencies": { + "@astrojs/compiler-rs": "^0.5.1", + "@astrojs/internal-helpers": "0.12.0", + "@astrojs/markdown-satteri": "0.4.3", + "@astrojs/telemetry": "3.3.3", + "@capsizecss/unpack": "^4.0.1", + "@clack/prompts": "^1.8.1", + "@oslojs/encoding": "^1.1.0", + "am-i-vibing": "^0.5.0", + "aria-query": "^5.3.2", + "axobject-query": "^4.1.0", + "ci-info": "^4.4.0", + "clsx": "^2.1.1", + "common-ancestor-path": "^2.0.0", + "cookie": "^2.0.1", + "devalue": "^5.9.4", + "diff": "^9.0.0", + "dset": "^3.1.4", + "es-module-lexer": "^3.0.2", + "find-proc": "0.2.0", + "flattie": "^1.1.1", + "fontace": "~0.4.1", + "get-tsconfig": "5.0.0-beta.6", + "github-slugger": "^2.0.0", + "html-escaper": "3.0.3", + "http-cache-semantics": "^4.2.0", + "jsonc-parser": "^3.3.1", + "magic-string": "^1.4.2", + "magicast": "^0.5.5", + "mrmime": "^2.0.1", + "neotraverse": "^1.0.1", + "obug": "^3.0.0", + "p-limit": "^7.3.3", + "p-queue": "^9.3.3", + "package-manager-detector": "^1.8.0", + "piccolore": "^0.1.3", + "picomatch": "^4.0.7", + "rolldown": "^1.2.11", + "shiki": "^4.4.3", + "smol-toml": "^1.9.0", + "svgo": "^4.1.0", + "tinyclip": "^1.0.3", + "tinyexec": "^1.3.1", + "tinyglobby": "^0.2.17", + "ultrahtml": "^1.7.0", + "unifont": "~0.7.5", + "unstorage": "^1.17.5", + "verkit": "^0.5.0", + "vite": "^8.3.1", + "vitefu": "^1.1.3", + "xxhash-wasm": "^1.1.0", + "yargs-parser": "^22.0.0", + "zod": "^4.6.5" + }, + "bin": { + "astro": "bin/astro.mjs" + }, + "engines": { + "node": ">=22.12.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/astrodotbuild" + }, + "optionalDependencies": { + "sharp": "^0.35.4" + }, + "peerDependencies": { + "@astrojs/markdown-remark": "^7.3.0" + }, + "peerDependenciesMeta": { + "@astrojs/markdown-remark": { + "optional": true + } + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ci-info": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/common-ancestor-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-2.0.0.tgz", + "integrity": "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">= 18" + } + }, + "node_modules/cookie": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-2.0.1.tgz", + "integrity": "sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w==", + "license": "MIT", + "engines": { + "node": ">=22" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cookie-es": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.3.tgz", + "integrity": "sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==", + "license": "MIT" + }, + "node_modules/crossws": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz", + "integrity": "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==", + "license": "MIT", + "dependencies": { + "uncrypto": "^0.1.3" + } + }, + "node_modules/css-select": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-6.0.0.tgz", + "integrity": "sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^7.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "nth-check": "^2.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-7.0.0.tgz", + "integrity": "sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "license": "MIT", + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "license": "CC0-1.0" + }, + "node_modules/defu": { + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", + "license": "MIT" + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/devalue": { + "version": "5.9.4", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.9.4.tgz", + "integrity": "sha512-sPAT4pztbu6586/hrhOnMKS17IJrvg12mXiSPSS3W5qDeN2RGgvZ0diZCm31dBbnevfVmujNO3IM2wrS4Y2Rhg==", + "license": "MIT" + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/diff": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz", + "integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dset": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", + "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/emmet": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/emmet/-/emmet-2.4.11.tgz", + "integrity": "sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==", + "dev": true, + "license": "MIT", + "workspaces": [ + "./packages/scanner", + "./packages/abbreviation", + "./packages/css-abbreviation", + "./" + ], + "dependencies": { + "@emmetio/abbreviation": "^2.3.3", + "@emmetio/css-abbreviation": "^2.1.8" + } + }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-module-lexer": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-3.0.3.tgz", + "integrity": "sha512-yWGe1O/j+almnV4hOYrmZo8u58JdnmXEMneRQAYkWtqSiT86yoMIEKF4rpqEDj52tbYl3xceBD3DQIBwcsC9Pw==", + "license": "MIT" + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "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", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", + "license": "MIT" + }, + "node_modules/fast-string-width": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", + "license": "MIT", + "dependencies": { + "fast-string-truncated-width": "^3.0.2" + } + }, + "node_modules/fast-uri": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-wrap-ansi": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", + "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", + "license": "MIT", + "dependencies": { + "fast-string-width": "^3.0.2" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/find-proc": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/find-proc/-/find-proc-0.2.0.tgz", + "integrity": "sha512-a6h2nTrgB3g5Wrn4au9Ux4kMk7IJtyF6oA8uGn+mk3weLZssUtc1cp6meHIzzuW2o1srrBAxzkmXLbqjRkgB6w==", + "license": "MIT", + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/flattie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz", + "integrity": "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/fontace": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/fontace/-/fontace-0.4.1.tgz", + "integrity": "sha512-lDMvbAzSnHmbYMTEld5qdtvNH2/pWpICOqpean9IgC7vUbUJc3k+k5Dokp85CegamqQpFbXf0rAVkbzpyTA8aw==", + "license": "MIT", + "dependencies": { + "fontkitten": "^1.0.2" + } + }, + "node_modules/fontkitten": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/fontkitten/-/fontkitten-1.0.3.tgz", + "integrity": "sha512-Wp1zXWPVUPBmfoa3Cqc9ctaKuzKAV6uLstRqlR56kSjplf5uAce+qeyYym7F+PHbGTk+tCEdkCW6RD7DX/gBZw==", + "license": "MIT", + "dependencies": { + "tiny-inflate": "^1.0.3" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz", + "integrity": "sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-tsconfig": { + "version": "5.0.0-beta.6", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-5.0.0-beta.6.tgz", + "integrity": "sha512-X6fBC0pmImC70gvX2zm56go9hx0MyoGVdG0tUCkg/D+Xnh5TJsOZ7iDbOdI3PvmtrDxnu1YdDufpK2QJX1Meqw==", + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "engines": { + "node": ">=20.20.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/github-slugger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", + "license": "ISC" + }, + "node_modules/h3": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.11.tgz", + "integrity": "sha512-L3THSe2MPeBwgIZVSH5zLdBBU90TOxarvhK9d04IDY2AmVS8j2Jz2LIWtwsGOU3lu2I5jCN7FNvVfY2+XyF+mg==", + "license": "MIT", + "dependencies": { + "cookie-es": "^1.2.3", + "crossws": "^0.3.5", + "defu": "^6.1.6", + "destr": "^2.0.5", + "iron-webcrypto": "^1.2.1", + "node-mock-http": "^1.0.4", + "radix3": "^1.1.2", + "ufo": "^1.6.3", + "uncrypto": "^0.1.3" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", + "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-escaper": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", + "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", + "license": "MIT" + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.3.0.tgz", + "integrity": "sha512-M5t5LlJpS1UHMjvwRQVdFHvPISGeLAxNcrWuJkeGh0KxsqCHZ1O3NXZU/8x7cD0BDcGW8kapxMKTvwlqrNkHkA==", + "license": "BSD-2-Clause" + }, + "node_modules/iron-webcrypto": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", + "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/brc-dd" + } + }, + "node_modules/is-docker": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-4.0.0.tgz", + "integrity": "sha512-LHE+wROyG/Y/0ZnbktRCoTix2c1RhgWaZraMZ8o1Q7zCh0VSrICJQO5oqIIISrcSBtrXv0o233w1IYwsWCjTzA==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "license": "MIT" + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lru-cache": { + "version": "11.5.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz", + "integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/magic-string": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.3.tgz", + "integrity": "sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.6.0" + } + }, + "node_modules/magicast": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.5.tgz", + "integrity": "sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "source-map-js": "^1.2.1" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "license": "CC0-1.0" + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.3.tgz", + "integrity": "sha512-oxB2Ik03hI0gv+VNn9tnh1t1YEe9MDPptViAEgfdf3YQHsn0pzGTgCdlSCJXcwhqm8phaEuM7zeEu3QQzVBrPg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/neotraverse": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-1.0.1.tgz", + "integrity": "sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/nlcst-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz", + "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "license": "MIT" + }, + "node_modules/node-mock-http": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.5.tgz", + "integrity": "sha512-KQyt/wLjG3TAc7DOUhpqWzgd4ERxR80JOlTK5VE5R1S12IaPVN5qkj4klBce9HPG1Njuup4Sb5bljaT34lIyjw==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/obug": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/obug/-/obug-3.0.0.tgz", + "integrity": "sha512-5vvB5+W7ePv+p3uqxi+RcW1XAzLW0/hxt3/4X4Lc4qHudzOhmBiBwOY6DRob4WnanAEGvNcLjF+KNOufrUoEQw==", + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/ofetch": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.5.1.tgz", + "integrity": "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==", + "license": "MIT", + "dependencies": { + "destr": "^2.0.5", + "node-fetch-native": "^1.6.7", + "ufo": "^1.6.1" + } + }, + "node_modules/ohash": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.12.tgz", + "integrity": "sha512-65S/5gk9YSsaRjcyf7Nfa6h/d3E8/1gslpXfI4W7Dxn/oap8IKRuNT5VXkLQ1YFKIEg4apRY4Pj6aiwFzrDdmw==", + "license": "MIT" + }, + "node_modules/oniguruma-parser": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz", + "integrity": "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==", + "license": "MIT" + }, + "node_modules/oniguruma-to-es": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.6.tgz", + "integrity": "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==", + "license": "MIT", + "dependencies": { + "oniguruma-parser": "^0.12.2", + "regex": "^6.1.0", + "regex-recursion": "^6.0.2" + } + }, + "node_modules/p-limit": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-7.3.3.tgz", + "integrity": "sha512-SKgVvDU5TNIxQ4r30U75BNLC6a+6GOEjngrk/ysM5+Zu3oJuk1SC5ApzIzY/7PjCEjoVPMWJ0zho/4QUiEB7TQ==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.2.1" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-queue": { + "version": "9.3.3", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.3.3.tgz", + "integrity": "sha512-NXAOdnEe5FsZJfT4oK84lE1Y5cFFdWlRuOo5tww8DyNMxyRXwn39fIkUtNLKppcPC+UYU/bXujNCUGDv01y7CA==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.4", + "p-timeout": "^7.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.2.tgz", + "integrity": "sha512-prbX4Z3YszrFNgH+MW5Zoeq3baXrMtP/MQnFeET90UB/GtGcGDQ5Usg9OCy6ETjTTntOw1SL2z9fMPUppN3Guw==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-manager-detector": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.9.0.tgz", + "integrity": "sha512-zAMfbta9nPA8AKkA4WdmNAT8R8s6Dk0/xwPsqNnRidMFcLo1YkLuPDTgs2f64XdzS8vbLNnoJ+uUIkgbz7oRHA==", + "license": "MIT" + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/piccolore": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/piccolore/-/piccolore-0.1.3.tgz", + "integrity": "sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==", + "license": "ISC" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.29.tgz", + "integrity": "sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.19", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prettier": { + "version": "3.9.9", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.9.tgz", + "integrity": "sha512-Z/CJHIkdujO/OtN7nXUii0Rf3VT5SRuhjBA82Xvu2XhBUgX3nhP67T0LHceBdQLex7OOFGTox+Q5Yg8Jk2Qivg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-astro": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-astro/-/prettier-plugin-astro-1.1.0.tgz", + "integrity": "sha512-ujJ1VX+iin4eYJPHVCEbmXHzrEVsi1SaVehHtbAteTZv8VTrZyBiZPmYc6Ud6+fkr/UBMXuEu3N3+d49h7YfKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@astrojs/compiler-rs": "^0.4.0", + "@prettier/parse-srcset": "^3.1.0", + "sass-formatter": "^0.8.0" + }, + "engines": { + "node": ">=22.12.0" + }, + "peerDependencies": { + "prettier": "^3.5.3" + } + }, + "node_modules/prettier-plugin-astro/node_modules/@astrojs/compiler-binding": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding/-/compiler-binding-0.4.1.tgz", + "integrity": "sha512-ZYVDW1P58OXyxqZbOMp+/2U9KA7y2esIx9r4pWdpZ/l6fCZhaw4ul+D7EgpRVxYLkyTWqzANbTTUFGPFP8zDHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@astrojs/compiler-binding-android-arm64": "0.4.1", + "@astrojs/compiler-binding-darwin-arm64": "0.4.1", + "@astrojs/compiler-binding-darwin-x64": "0.4.1", + "@astrojs/compiler-binding-linux-arm64-gnu": "0.4.1", + "@astrojs/compiler-binding-linux-arm64-musl": "0.4.1", + "@astrojs/compiler-binding-linux-x64-gnu": "0.4.1", + "@astrojs/compiler-binding-linux-x64-musl": "0.4.1", + "@astrojs/compiler-binding-wasm32-wasi": "0.4.1", + "@astrojs/compiler-binding-win32-arm64-msvc": "0.4.1", + "@astrojs/compiler-binding-win32-x64-msvc": "0.4.1" + } + }, + "node_modules/prettier-plugin-astro/node_modules/@astrojs/compiler-binding-android-arm64": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-android-arm64/-/compiler-binding-android-arm64-0.4.1.tgz", + "integrity": "sha512-Wtx2DZORuNTLASLLZ8hlrjFaJe1LUloatcCn9EPq7knBhRpjMGXeJANIJMeBZG4igvz7x+3nyAJM2iwCSW9xog==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/prettier-plugin-astro/node_modules/@astrojs/compiler-binding-darwin-arm64": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-arm64/-/compiler-binding-darwin-arm64-0.4.1.tgz", + "integrity": "sha512-7iwXcU+hB60Y1SvOfnWHNioT72gDsDQKaDipXh2Onwp/TvQMozhbvhy13wJR8Af940a0h6PbddCeJFpj791UDA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/prettier-plugin-astro/node_modules/@astrojs/compiler-binding-darwin-x64": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-x64/-/compiler-binding-darwin-x64-0.4.1.tgz", + "integrity": "sha512-pifjICos49RXhSVKSDu5gDB0/8j0O7DmxSp1DaWW2PyLG5ekwSkcrfOZto5JqhdI7QYMZB3XOiXpsdOXpS4npA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/prettier-plugin-astro/node_modules/@astrojs/compiler-binding-linux-arm64-gnu": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-gnu/-/compiler-binding-linux-arm64-gnu-0.4.1.tgz", + "integrity": "sha512-hsZMlNWc6LqWHh9LlSwTq1/XCY7pxA5rkkKIdZRV8mgbOz9OwLvjEoe5Jry13eIZjMUQO8XnQyzk8o/Pjw2YFA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/prettier-plugin-astro/node_modules/@astrojs/compiler-binding-linux-arm64-musl": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-musl/-/compiler-binding-linux-arm64-musl-0.4.1.tgz", + "integrity": "sha512-a3EW27f+Z9uf57vYmpLgUriA1y9wDhKz57PT8D8XlFZt2HZEWhUxKuxI1jvKVI9p8okYee11WXEm/FHY1G9yoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/prettier-plugin-astro/node_modules/@astrojs/compiler-binding-linux-x64-gnu": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-gnu/-/compiler-binding-linux-x64-gnu-0.4.1.tgz", + "integrity": "sha512-XruPTKB/SS4DFzeAjQ1ZyM0ieHDjapHkOSnnzRApZ4FXY2YAI7UY3mETjfJiIS/acyZyrnPH5jObv1gATTdUJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/prettier-plugin-astro/node_modules/@astrojs/compiler-binding-linux-x64-musl": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-musl/-/compiler-binding-linux-x64-musl-0.4.1.tgz", + "integrity": "sha512-E8j7lGanqNpudtkUbsFaOLJhmwFe4Ecqux7K9hkj4wULIPNZRM2bYZNcMSj8r1oWvaUiwZl8LaaNwv3+14jqhw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/prettier-plugin-astro/node_modules/@astrojs/compiler-binding-wasm32-wasi": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-wasm32-wasi/-/compiler-binding-wasm32-wasi-0.4.1.tgz", + "integrity": "sha512-vNLxIv41ZE+nmPFtw24cRW5ZXG+ZPcAGtgigtjCP8lvcxKI7u2GrXoMsCgEH5fMw8WCEJpwLvsVBg15rJKAkyA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.2.4" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/prettier-plugin-astro/node_modules/@astrojs/compiler-binding-win32-arm64-msvc": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-arm64-msvc/-/compiler-binding-win32-arm64-msvc-0.4.1.tgz", + "integrity": "sha512-/xTDgRsFDoTh0/+KFXJ74DY4Jqn8CoSHwa8UP5RAblHfnRHkenwfSl4zYDET61O9vXKLk09UzHjjq09yP20/AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/prettier-plugin-astro/node_modules/@astrojs/compiler-binding-win32-x64-msvc": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-x64-msvc/-/compiler-binding-win32-x64-msvc-0.4.1.tgz", + "integrity": "sha512-0x5J6iHZO0Fjo/CVzoIbXKzWnGESftDbEUQ/oXQ61HhUAFoR7+T1jfToZSNAS+gYyBXHSTpMWYC7lzarbhClcg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/prettier-plugin-astro/node_modules/@astrojs/compiler-rs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-rs/-/compiler-rs-0.4.1.tgz", + "integrity": "sha512-//NmAuRhy7eU9iP0ceI5Pymy55M+nUZ//kS6XeOyhBWdDib2KbJOyFaVJ52k7sDkJwTKVox7oPu3SpYiVnGm5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@astrojs/compiler-binding": "0.4.1" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/process-ancestry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/process-ancestry/-/process-ancestry-0.1.0.tgz", + "integrity": "sha512-tGqJW/UnclpYASFcM6Xh8D8l/BMtaQ9+CSG0vlJSJTcdMM4lDRv4c6H0Pdcsfted+bVczdYSfk2fdukg2gQkZg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/property-information": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", + "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/radix3": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", + "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", + "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-recursion": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", + "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "license": "MIT", + "dependencies": { + "regex-utilities": "^2.3.0" + } + }, + "node_modules/regex-utilities": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", + "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "license": "MIT" + }, + "node_modules/request-light": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.7.0.tgz", + "integrity": "sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/retext-smartypants": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.2.0.tgz", + "integrity": "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==", + "license": "MIT", + "dependencies": { + "@types/nlcst": "^2.0.0", + "nlcst-to-string": "^4.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rolldown": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.13.tgz", + "integrity": "sha512-/5apOkwWxtACcQtZ5WlrAsKS4JwXVThQPJZ3RqIruRY9UG9M8e0VlXN068xum/JRKA4aIUoJ13ghe/kGCvidiQ==", + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.153.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm-eabi": "1.2.13", + "@rolldown/binding-android-arm64": "1.2.13", + "@rolldown/binding-darwin-arm64": "1.2.13", + "@rolldown/binding-darwin-x64": "1.2.13", + "@rolldown/binding-freebsd-x64": "1.2.13", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.13", + "@rolldown/binding-linux-arm64-gnu": "1.2.13", + "@rolldown/binding-linux-arm64-musl": "1.2.13", + "@rolldown/binding-linux-ppc64-gnu": "1.2.13", + "@rolldown/binding-linux-s390x-gnu": "1.2.13", + "@rolldown/binding-linux-x64-gnu": "1.2.13", + "@rolldown/binding-linux-x64-musl": "1.2.13", + "@rolldown/binding-openharmony-arm64": "1.2.13", + "@rolldown/binding-win32-arm64-msvc": "1.2.13", + "@rolldown/binding-win32-x64-msvc": "1.2.13" + } + }, + "node_modules/s.color": { + "version": "0.0.15", + "resolved": "https://registry.npmjs.org/s.color/-/s.color-0.0.15.tgz", + "integrity": "sha512-AUNrbEUHeKY8XsYr/DYpl+qk5+aM+DChopnWOPEzn8YKzOhv4l2zH6LzZms3tOZP3wwdOyc0RmTciyi46HLIuA==", + "dev": true, + "license": "MIT" + }, + "node_modules/sass-formatter": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/sass-formatter/-/sass-formatter-0.8.0.tgz", + "integrity": "sha512-Nf4eMGE2O5vnJG7KPCRyksWS9gVKSYoUWtPeMXRRxwZSPVQhSGtL602xVYS65LvMkHmkvKa7xpauF0+/5pPwrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "suf-log": "^2.5.3" + }, + "bin": { + "sass-formatter": "dist/cli.js" + } + }, + "node_modules/satteri": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/satteri/-/satteri-0.10.5.tgz", + "integrity": "sha512-Ao1LKpAEa9Wdg0otgbVKViZHEq9ebdXe4DMrp3s9vQAU0HNIuHnFEuMuOcm0ZIXyV0Yzxj91NvhLpvXZJO/5ZQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.5", + "@types/hast": "^3.0.5", + "@types/mdast": "^4.0.4", + "@types/unist": "^3.0.3" + }, + "optionalDependencies": { + "@bruits/satteri-darwin-arm64": "0.10.5", + "@bruits/satteri-darwin-x64": "0.10.5", + "@bruits/satteri-linux-arm64-gnu": "0.10.5", + "@bruits/satteri-linux-arm64-musl": "0.10.5", + "@bruits/satteri-linux-x64-gnu": "0.10.5", + "@bruits/satteri-linux-x64-musl": "0.10.5", + "@bruits/satteri-wasm32-wasi": "0.10.5", + "@bruits/satteri-win32-arm64-msvc": "0.10.5", + "@bruits/satteri-win32-x64-msvc": "0.10.5" + } + }, + "node_modules/sax": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", + "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "devOptional": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.5.tgz", + "integrity": "sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.1.0", + "detect-libc": "^2.1.2", + "semver": "^7.8.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.35.5", + "@img/sharp-darwin-x64": "0.35.5", + "@img/sharp-freebsd-wasm32": "0.35.5", + "@img/sharp-libvips-darwin-arm64": "1.3.4", + "@img/sharp-libvips-darwin-x64": "1.3.4", + "@img/sharp-libvips-linux-arm": "1.3.4", + "@img/sharp-libvips-linux-arm64": "1.3.4", + "@img/sharp-libvips-linux-ppc64": "1.3.4", + "@img/sharp-libvips-linux-riscv64": "1.3.4", + "@img/sharp-libvips-linux-s390x": "1.3.4", + "@img/sharp-libvips-linux-x64": "1.3.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.4", + "@img/sharp-libvips-linuxmusl-x64": "1.3.4", + "@img/sharp-linux-arm": "0.35.5", + "@img/sharp-linux-arm64": "0.35.5", + "@img/sharp-linux-ppc64": "0.35.5", + "@img/sharp-linux-riscv64": "0.35.5", + "@img/sharp-linux-s390x": "0.35.5", + "@img/sharp-linux-x64": "0.35.5", + "@img/sharp-linuxmusl-arm64": "0.35.5", + "@img/sharp-linuxmusl-x64": "0.35.5", + "@img/sharp-webcontainers-wasm32": "0.35.5", + "@img/sharp-win32-arm64": "0.35.5", + "@img/sharp-win32-ia32": "0.35.5", + "@img/sharp-win32-x64": "0.35.5" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/shiki": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-4.5.0.tgz", + "integrity": "sha512-OcRW72ngQvc+MSVWBRIovC/BTW589iUE/Vxav7VeM4RcY9MUjqB+IR6ILG1k9nuW6LiG/nct9uma4/MBtnZbmQ==", + "license": "MIT", + "dependencies": { + "@shikijs/core": "4.5.0", + "@shikijs/engine-javascript": "4.5.0", + "@shikijs/engine-oniguruma": "4.5.0", + "@shikijs/langs": "4.5.0", + "@shikijs/themes": "4.5.0", + "@shikijs/types": "4.5.0", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, + "node_modules/sitemap": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-9.0.1.tgz", + "integrity": "sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ==", + "license": "MIT", + "dependencies": { + "@types/node": "^24.9.2", + "@types/sax": "^1.2.1", + "arg": "^5.0.0", + "sax": "^1.4.1" + }, + "bin": { + "sitemap": "dist/esm/cli.js" + }, + "engines": { + "node": ">=20.19.5", + "npm": ">=10.8.2" + } + }, + "node_modules/smol-toml": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.9.0.tgz", + "integrity": "sha512-hpd+HLON7HdZXqYchMM/+LaTTbdK0AU3NngIJ4KVyWbY9bfQqdL9cD+4yf6dUoU2Ap4VsU0JkQi6FxAI1B2mXQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, + "node_modules/source-map-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/string-width": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.3.0.tgz", + "integrity": "sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/suf-log": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/suf-log/-/suf-log-2.5.3.tgz", + "integrity": "sha512-KvC8OPjzdNOe+xQ4XWJV2whQA0aM1kGVczMQ8+dStAO6KfEB140JEVQ9dE76ONZ0/Ylf67ni4tILPJB41U0eow==", + "dev": true, + "license": "MIT", + "dependencies": { + "s.color": "0.0.15" + } + }, + "node_modules/svgo": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.1.0.tgz", + "integrity": "sha512-bkxnTg1kSU0guhIBmibA6UUhrQmPVA1XsQLN+ylCd+UWzbnLkySOcXpyk1mrl05f+pcaCx2eHb+sp6BgMZWX+Q==", + "license": "MIT", + "dependencies": { + "commander": "^11.1.0", + "css-select": "^6.0.0", + "css-tree": "^3.0.1", + "css-what": "^7.0.0", + "csso": "^5.0.5", + "picocolors": "^1.1.1", + "sax": "1.6.1" + }, + "bin": { + "svgo": "bin/svgo.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "license": "MIT" + }, + "node_modules/tinyclip": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tinyclip/-/tinyclip-1.0.3.tgz", + "integrity": "sha512-nTv2raKFMzGG6JJp0lpNDsRPdfFE6UHlu2rVEjXo8vkx1ZS3LW2CVXqIaLu8xZlu+6tbFs9V58Qc/Jmk/Ii7wA==", + "license": "MIT", + "engines": { + "node": "^16.14.0 || >= 17.3.0" + } + }, + "node_modules/tinyexec": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "devOptional": true, + "license": "0BSD" + }, + "node_modules/typesafe-path": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/typesafe-path/-/typesafe-path-0.2.2.tgz", + "integrity": "sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA==", + "dev": true, + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-auto-import-cache": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/typescript-auto-import-cache/-/typescript-auto-import-cache-0.3.6.tgz", + "integrity": "sha512-RpuHXrknHdVdK7wv/8ug3Fr0WNsNi5l5aB8MYYuXhq2UH5lnEB1htJ1smhtD5VeCsGr2p8mUDtd83LCQDFVgjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.8" + } + }, + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "license": "MIT" + }, + "node_modules/ultrahtml": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.7.0.tgz", + "integrity": "sha512-2xRd0VHoAQE4M+vF/DvFFB7pUV0ZxTW1TLi7lHQWnF/Sb5TPeEUV/l+hxcNnGO00ZXGnR0voCMmYRKQf+rvJ2g==", + "license": "MIT" + }, + "node_modules/uncrypto": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", + "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", + "license": "MIT" + }, + "node_modules/undici": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.11.2.tgz", + "integrity": "sha512-u4UB2/IrKdU6lFxumHmmo1a3fCQO5tzQllRorfoRS63txhrB7xTpSn1PftwC4qEHkOaqP95fCWW4lJzwErwzhQ==", + "license": "MIT", + "engines": { + "node": ">=22.19.0" + } + }, + "node_modules/undici-types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "license": "MIT" + }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unifont": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.7.5.tgz", + "integrity": "sha512-ULe/Cs+ZIsq+dcFofNkhqielCrUJnb5mr+Yc4EBM2VlL+6OZR6+cjtI2mT1bJvRBrVncqHAbLURxmPLcCXzWMg==", + "license": "MIT", + "dependencies": { + "css-tree": "^3.1.0", + "ohash": "^2.0.11", + "undici": "^8.0.0" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unstorage": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.5.tgz", + "integrity": "sha512-0i3iqvRfx29hkNntHyQvJTpf5W9dQ9ZadSoRU8+xVlhVtT7jAX57fazYO9EHvcRCfBCyi5YRya7XCDOsbTgkPg==", + "license": "MIT", + "dependencies": { + "anymatch": "^3.1.3", + "chokidar": "^5.0.0", + "destr": "^2.0.5", + "h3": "^1.15.10", + "lru-cache": "^11.2.7", + "node-fetch-native": "^1.6.7", + "ofetch": "^1.5.1", + "ufo": "^1.6.3" + }, + "peerDependencies": { + "@azure/app-configuration": "^1.8.0", + "@azure/cosmos": "^4.2.0", + "@azure/data-tables": "^13.3.0", + "@azure/identity": "^4.6.0", + "@azure/keyvault-secrets": "^4.9.0", + "@azure/storage-blob": "^12.26.0", + "@capacitor/preferences": "^6 || ^7 || ^8", + "@deno/kv": ">=0.9.0", + "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", + "@planetscale/database": "^1.19.0", + "@upstash/redis": "^1.34.3", + "@vercel/blob": ">=0.27.1", + "@vercel/functions": "^2.2.12 || ^3.0.0", + "@vercel/kv": "^1 || ^2 || ^3", + "aws4fetch": "^1.0.20", + "db0": ">=0.2.1", + "idb-keyval": "^6.2.1", + "ioredis": "^5.4.2", + "uploadthing": "^7.4.4" + }, + "peerDependenciesMeta": { + "@azure/app-configuration": { + "optional": true + }, + "@azure/cosmos": { + "optional": true + }, + "@azure/data-tables": { + "optional": true + }, + "@azure/identity": { + "optional": true + }, + "@azure/keyvault-secrets": { + "optional": true + }, + "@azure/storage-blob": { + "optional": true + }, + "@capacitor/preferences": { + "optional": true + }, + "@deno/kv": { + "optional": true + }, + "@netlify/blobs": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/blob": { + "optional": true + }, + "@vercel/functions": { + "optional": true + }, + "@vercel/kv": { + "optional": true + }, + "aws4fetch": { + "optional": true + }, + "db0": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "uploadthing": { + "optional": true + } + } + }, + "node_modules/unstorage/node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/unstorage/node_modules/readdirp": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", + "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/verkit": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/verkit/-/verkit-0.5.0.tgz", + "integrity": "sha512-u4J1hr6Y+C/hit34GwL3eIcUf3wngT8TlYUjK7tk0jxydKLAq3sYvifjdTDA6U3KCKRcQCQDGf/srjxfZF3l7g==", + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.4.tgz", + "integrity": "sha512-IftLE8phJhdNRIGNEV4JCCvfNScoZQVfcXaN8TQropWGJtOHcu+iAB9pHcuGvmEb/kddw0LBe/jS8ztdC6xdiQ==", + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.29", + "rolldown": "~1.2.12", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.7.1", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", + "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/volar-service-css": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-css/-/volar-service-css-0.0.71.tgz", + "integrity": "sha512-wRRFt9BpjMKCazcgOh67MSjUjiWUCAh99DyYSDIOTuxaRjEtDC7PpB0k1Y1wbJIW/pVtMUSVbpPo3UGSm0Byxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-css-languageservice": "^6.3.0", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-emmet": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-emmet/-/volar-service-emmet-0.0.71.tgz", + "integrity": "sha512-zqjzt6bN95e3CUstBm0PBFAJnrfz0ZAARka87fart46/gNCLLuP3Vujy8V/J8HEziTFLnfkgIASLFYPUhonJcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@emmetio/css-parser": "^0.4.1", + "@emmetio/html-matcher": "^1.3.0", + "@vscode/emmet-helper": "^2.9.3", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-html": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-html/-/volar-service-html-0.0.71.tgz", + "integrity": "sha512-e8tHPhgQ7ooLfudAEIku+kgd9pWkq3SSz8RbnQDI1+Eb8wbenkLGHqoirLqz5ORLV6wIMr2Iv08RWBG5eOcgpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-html-languageservice": "^5.3.0", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-prettier": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-prettier/-/volar-service-prettier-0.0.71.tgz", + "integrity": "sha512-Rz7JVH3qD108UCdmIEiZvOBNljMt2nLFdbN8AXcDfn7xD9F5I2aCIsDVqBbXw21PsnxG0b7MfwtNF+zPS/NKUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0", + "prettier": "^2.2 || ^3.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + }, + "prettier": { + "optional": true + } + } + }, + "node_modules/volar-service-typescript": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-typescript/-/volar-service-typescript-0.0.71.tgz", + "integrity": "sha512-yTtM/BVT6hoyEYnDtaCyAtNhdNeS/mhTTABlBOdw3NNiRBUin3IznFJpgfjer4c6RYopiPjjQjc9VFhxVl1mLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-browserify": "^1.0.1", + "semver": "^7.6.2", + "typescript-auto-import-cache": "^0.3.5", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-nls": "^5.2.0", + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-typescript-twoslash-queries": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-typescript-twoslash-queries/-/volar-service-typescript-twoslash-queries-0.0.71.tgz", + "integrity": "sha512-9K2k72s4n7rV9s4bX0MyjbX9iBribvKZbBJKuEmTCZfeWJXs6Yh7bGpY4eoc7UufAjvpheBqwyZCOIPBvxCv0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-uri": "^3.0.8" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/volar-service-yaml": { + "version": "0.0.71", + "resolved": "https://registry.npmjs.org/volar-service-yaml/-/volar-service-yaml-0.0.71.tgz", + "integrity": "sha512-qYGWGuVpUTnZGu5P/CR4KLK4aIR8RrcVnmfZ2eRcj9q/I8VZCoC5yy9FtEvfNvnDp4MU17yhdJcvpQPIqhJS2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-uri": "^3.0.8", + "yaml-language-server": "~1.23.0" + }, + "peerDependencies": { + "@volar/language-service": "~2.4.0" + }, + "peerDependenciesMeta": { + "@volar/language-service": { + "optional": true + } + } + }, + "node_modules/vscode-css-languageservice": { + "version": "6.3.10", + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.10.tgz", + "integrity": "sha512-eq5N9Er3fC4vA9zd9EFhyBG90wtCCuXgRSpAndaOgXMh1Wgep5lBgRIeDgjZBW9pa+332yC9+49cZMW8jcL3MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vscode/l10n": "^0.0.18", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-languageserver-types": "3.17.5", + "vscode-uri": "^3.1.0" + } + }, + "node_modules/vscode-css-languageservice/node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-html-languageservice": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.6.2.tgz", + "integrity": "sha512-ulCrSnFnfQ16YzvwnYUgEbUEl/ZG7u2eV27YhvLObSHKkb8fw1Z9cgsnUwjTEeDIdJDoTDTDpxuhQwoenoLNMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vscode/l10n": "^0.0.18", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-languageserver-types": "^3.17.5", + "vscode-uri": "^3.1.0" + } + }, + "node_modules/vscode-json-languageservice": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-4.1.8.tgz", + "integrity": "sha512-0vSpg6Xd9hfV+eZAaYN63xVVMOTmJ4GgHxXnkLCh+9RsQBkWKIghzLhW2B9ebfG+LQQg8uLtsQ2aUKjTgE+QOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsonc-parser": "^3.0.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.16.0", + "vscode-nls": "^5.0.0", + "vscode-uri": "^3.0.2" + }, + "engines": { + "npm": ">=7.0.0" + } + }, + "node_modules/vscode-jsonrpc": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.3.tgz", + "integrity": "sha512-qETYf9PCl1lVfwR+oBmMfc4vP2nqLNA35ABxorw3j06+xlKRgDVxbLgKm4Y/xPL0YWdeDjLoI4ImICIT3nB0zQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.18.4.tgz", + "integrity": "sha512-CCXbyr99dafRJzJH/0Ryk7ly5T8XPmSR0//OuF2w4m+9JiOin7zcAT5iET8GRE7Z9taFBejb3GbM0a0mbjdbrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "9.0.3", + "vscode-languageserver-types": "3.18.4" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.15.tgz", + "integrity": "sha512-Gsfr9S8LU3S6885cS+NaleasJ0n3V5y/SOTqHCifQiXWFw3G9w9/YItDfwHrlCz+dSZWWZ8ilYVMcJe7Sz7Sxw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-languageserver-types": { + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.18.4.tgz", + "integrity": "sha512-nQsacoiijvl0fRf2q28uFDrAVwRhdCa3QveiF7/CX0r18mLR5FU/1WskSsCKOjmEbtteKUnY8x8Yzxop+k0zwg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-languageserver/node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver/node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver/node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-nls": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz", + "integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-uri": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.2.0.tgz", + "integrity": "sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xxhash-wasm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", + "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==", + "license": "MIT" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yaml": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz", + "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yaml-language-server": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/yaml-language-server/-/yaml-language-server-1.23.0.tgz", + "integrity": "sha512-3qVyCOexLCWw06PQa5kRPwvMWMZ/eZeCRWUvgD6a0OkqL/4iCnxy2WumbWifa937Uo5xhyWJ0uxlU39ljhNh7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vscode/l10n": "^0.0.18", + "ajv": "^8.17.1", + "ajv-draft-04": "^1.0.0", + "ajv-i18n": "^4.2.0", + "prettier": "^3.8.1", + "request-light": "^0.5.7", + "vscode-json-languageservice": "4.1.8", + "vscode-languageserver": "^9.0.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.16.0", + "vscode-uri": "^3.0.2", + "yaml": "2.8.3" + }, + "bin": { + "yaml-language-server": "bin/yaml-language-server" + } + }, + "node_modules/yaml-language-server/node_modules/request-light": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.5.8.tgz", + "integrity": "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg==", + "dev": true, + "license": "MIT" + }, + "node_modules/yaml-language-server/node_modules/yaml": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yargs": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.2.0.tgz", + "integrity": "sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^8.2.1", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.6.5", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.5.tgz", + "integrity": "sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..d8b65b4 --- /dev/null +++ b/package.json @@ -0,0 +1,30 @@ +{ + "name": "@ldssa/website", + "version": "1.0.0", + "private": true, + "type": "module", + "engines": { + "node": ">=24.0.0" + }, + "scripts": { + "dev": "ASTRO_TELEMETRY_DISABLED=1 astro dev", + "build": "ASTRO_TELEMETRY_DISABLED=1 astro build && node scripts/configure-analytics-output.mjs", + "preview": "ASTRO_TELEMETRY_DISABLED=1 astro preview", + "typecheck": "ASTRO_TELEMETRY_DISABLED=1 astro check", + "test": "npm run build && node scripts/check-site.mjs", + "check": "npm run typecheck && npm test", + "check:deployment": "node scripts/check-deployment.mjs", + "format": "prettier --write .", + "format:check": "prettier --check ." + }, + "dependencies": { + "@astrojs/sitemap": "3.7.4", + "astro": "7.3.8" + }, + "devDependencies": { + "@astrojs/check": "0.9.10", + "prettier": "3.9.9", + "prettier-plugin-astro": "1.1.0", + "typescript": "5.9.3" + } +} diff --git a/public/_headers b/public/_headers new file mode 100644 index 0000000..5e897c1 --- /dev/null +++ b/public/_headers @@ -0,0 +1,15 @@ +/* + Content-Security-Policy: default-src 'self'; base-uri 'self'; connect-src 'self' https://*.flodesk.com https://assets.flodesk.com; form-action https://form.flodesk.com; frame-ancestors 'none'; frame-src https://www.youtube-nocookie.com; img-src 'self' data: https://*.flodesk.com https://assets.flodesk.com; object-src 'none'; script-src 'self' 'unsafe-inline' https://assets.flodesk.com; style-src 'self' 'unsafe-inline' https://assets.flodesk.com; upgrade-insecure-requests + Permissions-Policy: camera=(), geolocation=(), microphone=() + Referrer-Policy: strict-origin-when-cross-origin + X-Content-Type-Options: nosniff + X-Frame-Options: DENY + +/assets/* + Cache-Control: public, max-age=31536000, immutable + +/images/* + Cache-Control: public, max-age=604800, stale-while-revalidate=86400 + +/deployment.json + Cache-Control: no-store diff --git a/public/_redirects b/public/_redirects new file mode 100644 index 0000000..f43e1ab --- /dev/null +++ b/public/_redirects @@ -0,0 +1,15 @@ +/curriculum /starters-academy/#curriculum 301 +/curriculum/ /starters-academy/#curriculum 301 +/faq /starters-academy/#faq 301 +/faq/ /starters-academy/#faq 301 +/home / 301 +/index.html / 301 +/comms-associate-position /about-us/ 301 +/comms-associate-position/ /about-us/ 301 +/pythonzerotohero /prep-course/ 301 +/pythonzerotohero/ /prep-course/ 301 +/a-form_confirmation.html / 301 +/b-form_confirmation.html / 301 +/d-form_confirmation.html / 301 +/comms-associate-position/a-form_confirmation.html /about-us/ 301 +/curriculum/a-form_confirmation.html /starters-academy/ 301 diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..09246c7 --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1,7 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"> + <rect width="64" height="64" rx="14" fill="#171b3a"/> + <circle cx="21" cy="21" r="7" fill="#2dd4bf"/> + <circle cx="43" cy="21" r="7" fill="#ffb547"/> + <circle cx="32" cy="43" r="7" fill="#f16464"/> + <path d="M26 22h12M24 27l5 11m11-11-5 11" stroke="#fff" stroke-width="4" stroke-linecap="round"/> +</svg> diff --git a/public/images/.gitkeep b/public/images/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/public/images/.gitkeep @@ -0,0 +1 @@ + diff --git a/public/images/academy.JPG b/public/images/academy.JPG new file mode 100644 index 0000000..7c82f59 Binary files /dev/null and b/public/images/academy.JPG differ diff --git a/public/images/bootcamp.JPG b/public/images/bootcamp.JPG new file mode 100644 index 0000000..fbc46f7 Binary files /dev/null and b/public/images/bootcamp.JPG differ diff --git a/public/images/community.jpeg b/public/images/community.jpeg new file mode 100644 index 0000000..10e6c98 Binary files /dev/null and b/public/images/community.jpeg differ diff --git a/public/images/curriculum.png b/public/images/curriculum.png new file mode 100644 index 0000000..dfed7e7 Binary files /dev/null and b/public/images/curriculum.png differ diff --git a/public/images/data-science.png b/public/images/data-science.png new file mode 100644 index 0000000..4b1252a Binary files /dev/null and b/public/images/data-science.png differ diff --git a/public/images/hackathon.JPG b/public/images/hackathon.JPG new file mode 100644 index 0000000..f2a6438 Binary files /dev/null and b/public/images/hackathon.JPG differ diff --git a/public/images/ldsa-logo.png b/public/images/ldsa-logo.png new file mode 100644 index 0000000..1485cfa Binary files /dev/null and b/public/images/ldsa-logo.png differ diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..ab00acc --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Allow: / + +Sitemap: https://www.lisbondatascience.org/sitemap-index.xml diff --git a/scripts/check-deployment.mjs b/scripts/check-deployment.mjs new file mode 100644 index 0000000..6e38164 --- /dev/null +++ b/scripts/check-deployment.mjs @@ -0,0 +1,223 @@ +const baseArgument = process.argv[2] ?? process.env.DEPLOYMENT_URL; +const aliasArgument = process.env.DEPLOYMENT_ALIAS_URL; +const expectedCommit = process.env.EXPECTED_COMMIT?.toLowerCase(); +const expectedBranch = process.env.EXPECTED_BRANCH; +const expectNoindex = process.env.EXPECT_NOINDEX === "true"; +const expectAnalytics = process.env.EXPECT_ANALYTICS === "true"; +const errors = []; + +if (!baseArgument) { + console.error("Provide the deployed URL as an argument or DEPLOYMENT_URL."); + process.exit(1); +} + +const normalizeBaseUrl = (value, label) => { + let url; + try { + url = new URL(value); + } catch { + throw new Error(`${label} is not a valid URL: ${value}`); + } + if (url.protocol !== "https:") { + throw new Error(`${label} must use HTTPS: ${value}`); + } + url.pathname = "/"; + url.search = ""; + url.hash = ""; + return url; +}; + +let baseUrl; +let aliasUrl; +try { + baseUrl = normalizeBaseUrl(baseArgument, "Deployment URL"); + aliasUrl = aliasArgument + ? normalizeBaseUrl(aliasArgument, "Deployment alias URL") + : undefined; +} catch (error) { + console.error(error.message); + process.exit(1); +} + +const accessClientId = process.env.CF_ACCESS_CLIENT_ID; +const accessClientSecret = process.env.CF_ACCESS_CLIENT_SECRET; +if (Boolean(accessClientId) !== Boolean(accessClientSecret)) { + console.error( + "CF_ACCESS_CLIENT_ID and CF_ACCESS_CLIENT_SECRET must be supplied together.", + ); + process.exit(1); +} + +const headers = accessClientId + ? { + "CF-Access-Client-Id": accessClientId, + "CF-Access-Client-Secret": accessClientSecret, + } + : {}; + +const request = (path, options = {}) => + fetch(new URL(path, baseUrl), { + redirect: "manual", + headers, + signal: AbortSignal.timeout(15_000), + ...options, + }); + +const delay = (milliseconds) => + new Promise((resolve) => setTimeout(resolve, milliseconds)); + +let homeResponse; +for (let attempt = 1; attempt <= 6; attempt += 1) { + try { + homeResponse = await request("/"); + if (homeResponse.status === 200) break; + } catch (error) { + if (attempt === 6) errors.push(`Homepage request failed: ${error.message}`); + } + if (attempt < 6) await delay(5_000); +} + +if (!homeResponse || homeResponse.status !== 200) { + errors.push(`Homepage returned ${homeResponse?.status ?? "no response"}.`); +} else { + const requiredHeaders = { + "content-security-policy": "default-src 'self'", + "permissions-policy": "camera=()", + "referrer-policy": "strict-origin-when-cross-origin", + "x-content-type-options": "nosniff", + "x-frame-options": "DENY", + }; + for (const [name, expectedValue] of Object.entries(requiredHeaders)) { + const actualValue = homeResponse.headers.get(name) ?? ""; + if (!actualValue.includes(expectedValue)) { + errors.push(`${name} does not contain ${JSON.stringify(expectedValue)}.`); + } + } + + const contentSecurityPolicy = + homeResponse.headers.get("content-security-policy") ?? ""; + for (const origin of [ + "https://static.cloudflareinsights.com", + "https://cloudflareinsights.com", + ]) { + if (expectAnalytics && !contentSecurityPolicy.includes(origin)) { + errors.push(`Production analytics CSP is missing ${origin}.`); + } + if (!expectAnalytics && contentSecurityPolicy.includes(origin)) { + errors.push(`Analytics-free deployment unexpectedly permits ${origin}.`); + } + } + + const homeHtml = await homeResponse.clone().text(); + const analyticsScript = "https://static.cloudflareinsights.com/beacon.min.js"; + if (expectAnalytics && !homeHtml.includes(analyticsScript)) { + errors.push("Production deployment is missing the analytics beacon."); + } + if (!expectAnalytics && homeHtml.includes(analyticsScript)) { + errors.push("Analytics-free deployment unexpectedly contains the beacon."); + } + + const robotsHeader = homeResponse.headers.get("x-robots-tag") ?? ""; + if (expectNoindex && !/\bnoindex\b/i.test(robotsHeader)) { + errors.push("Preview deployment is missing X-Robots-Tag: noindex."); + } + if (!expectNoindex && /\bnoindex\b/i.test(robotsHeader)) { + errors.push("Production deployment unexpectedly sends noindex."); + } +} + +for (const path of [ + "/starters-academy/", + "/prep-course/", + "/about-us/", + "/jobs/", + "/robots.txt", + "/sitemap-index.xml", +]) { + try { + const response = await request(path); + if (response.status !== 200) + errors.push(`${path} returned ${response.status}.`); + } catch (error) { + errors.push(`${path} request failed: ${error.message}`); + } +} + +for (const [path, destination] of [ + ["/curriculum/", "/starters-academy/#curriculum"], + ["/faq/", "/starters-academy/#faq"], + ["/home", "/"], + ["/index.html", "/"], + ["/comms-associate-position/", "/about-us/"], + ["/pythonzerotohero/", "/prep-course/"], + ["/a-form_confirmation.html", "/"], + ["/b-form_confirmation.html", "/"], + ["/d-form_confirmation.html", "/"], + ["/comms-associate-position/a-form_confirmation.html", "/about-us/"], + ["/curriculum/a-form_confirmation.html", "/starters-academy/"], +]) { + try { + const response = await request(path); + const location = response.headers.get("location"); + const actualDestination = location + ? `${new URL(location, baseUrl).pathname}${new URL(location, baseUrl).hash}` + : ""; + if (response.status !== 301 || actualDestination !== destination) { + errors.push( + `${path} expected 301 to ${destination}, received ${response.status} to ${location ?? "no location"}.`, + ); + } + } catch (error) { + errors.push(`${path} redirect check failed: ${error.message}`); + } +} + +try { + const response = await request("/__deployment-contract-missing-route__/"); + if (response.status !== 404) { + errors.push(`Unknown route returned ${response.status} instead of 404.`); + } else if (!(await response.text()).includes("Page not found")) { + errors.push("Unknown route did not render the custom 404 page."); + } +} catch (error) { + errors.push(`404 check failed: ${error.message}`); +} + +try { + const response = await request("/deployment.json"); + if (response.status !== 200) { + errors.push(`Deployment metadata returned ${response.status}.`); + } else { + const metadata = await response.json(); + if (expectedCommit && metadata.commit !== expectedCommit) { + errors.push( + `Expected commit ${expectedCommit}, deployment reports ${metadata.commit}.`, + ); + } + if (expectedBranch && metadata.branch !== expectedBranch) { + errors.push( + `Expected branch ${expectedBranch}, deployment reports ${metadata.branch}.`, + ); + } + } +} catch (error) { + errors.push(`Deployment metadata check failed: ${error.message}`); +} + +if (aliasUrl && expectedBranch && expectedBranch !== "main") { + const expectedAlias = `${expectedBranch + .toLowerCase() + .replace(/[^a-z0-9]+/g, "-")}.`; + if (!aliasUrl.hostname.startsWith(expectedAlias)) { + errors.push( + `Branch alias ${aliasUrl.hostname} does not start with ${expectedAlias}`, + ); + } +} + +if (errors.length > 0) { + console.error(`Deployment contract checks failed:\n- ${errors.join("\n- ")}`); + process.exit(1); +} + +console.log(`Deployment contract checks passed for ${baseUrl.origin}.`); diff --git a/scripts/check-site.mjs b/scripts/check-site.mjs new file mode 100644 index 0000000..1a1d4b4 --- /dev/null +++ b/scripts/check-site.mjs @@ -0,0 +1,159 @@ +import { existsSync, readFileSync, readdirSync } from "node:fs"; +import { join, relative } from "node:path"; + +const root = new URL("../", import.meta.url).pathname; +const output = join(root, "dist"); +const expectAnalytics = Boolean( + process.env.CLOUDFLARE_WEB_ANALYTICS_TOKEN?.trim(), +); +const errors = []; + +const walk = (directory) => + readdirSync(directory, { withFileTypes: true }).flatMap((entry) => { + const path = join(directory, entry.name); + return entry.isDirectory() ? walk(path) : [path]; + }); + +const requiredPages = [ + "index.html", + "starters-academy/index.html", + "prep-course/index.html", + "about-us/index.html", + "jobs/index.html", + "404.html", +]; + +for (const page of requiredPages) { + if (!existsSync(join(output, page))) + errors.push(`Missing generated page: ${page}`); +} + +const redirects = readFileSync(join(output, "_redirects"), "utf8"); +for (const legacyPath of [ + "/curriculum/", + "/faq/", + "/comms-associate-position/", + "/pythonzerotohero/", + "/d-form_confirmation.html", +]) { + if (!redirects.includes(legacyPath)) + errors.push(`Missing redirect for ${legacyPath}`); +} + +const htmlFiles = walk(output).filter((file) => file.endsWith(".html")); +const knownRedirects = new Set( + redirects + .split("\n") + .map((line) => line.trim().split(/\s+/)[0]) + .filter(Boolean), +); + +for (const file of htmlFiles) { + const html = readFileSync(file, "utf8"); + const name = relative(output, file); + const h1Count = (html.match(/<h1(?:\s|>)/g) ?? []).length; + if (h1Count !== 1) errors.push(`${name}: expected one h1, found ${h1Count}`); + if (!/<meta name="description" content="[^"]+"/.test(html)) + errors.push(`${name}: missing meta description`); + if ( + !/<link rel="canonical" href="https:\/\/www\.lisbondatascience\.org/.test( + html, + ) + ) + errors.push(`${name}: missing production canonical URL`); + if (/unbounce|builder-assets|\bclkn?\//i.test(html)) + errors.push(`${name}: contains an Unbounce dependency`); + + for (const [, href] of html.matchAll(/href="([^"]+)"/g)) { + if (!href.startsWith("/") || href.startsWith("//")) continue; + const path = href.split(/[?#]/)[0] || "/"; + if (/\.[a-z0-9]+$/i.test(path)) { + if (!existsSync(join(output, path))) + errors.push(`${name}: missing local asset ${path}`); + continue; + } + const target = + path === "/" + ? join(output, "index.html") + : join(output, path, "index.html"); + if ( + !existsSync(target) && + !knownRedirects.has(path) && + !knownRedirects.has(`${path}/`) + ) { + errors.push(`${name}: broken internal link ${href}`); + } + } + + for (const [, src] of html.matchAll( + /(?:src|content)="(\/[^"?#]+\.(?:png|jpe?g|svg|webp|avif))"/gi, + )) { + if (!existsSync(join(output, src))) + errors.push(`${name}: missing image ${src}`); + } +} + +const allHtml = htmlFiles.map((file) => readFileSync(file, "utf8")).join("\n"); +for (const formId of [ + "63f9e4e2f0116a4cabfef7db", + "615c9641c71e8685f183bd18", + "6172e62fe625581864e3f857", +]) { + const endpoint = `https://form.flodesk.com/forms/${formId}/submit`; + if (!allHtml.includes(endpoint)) + errors.push(`Missing preserved Flodesk endpoint: ${endpoint}`); +} + +if (!allHtml.includes("https://assets.flodesk.com/universal.mjs")) + errors.push("Missing modern Flodesk form runtime"); +if (!allHtml.includes("https://assets.flodesk.com/universal.js")) + errors.push("Missing legacy Flodesk form runtime fallback"); + +if (!existsSync(join(output, "sitemap-index.xml"))) + errors.push("Missing generated sitemap index"); +if (!existsSync(join(output, "_headers"))) { + errors.push("Missing Cloudflare headers configuration"); +} else { + const headers = readFileSync(join(output, "_headers"), "utf8"); + for (const origin of [ + "https://static.cloudflareinsights.com", + "https://cloudflareinsights.com", + ]) { + if (expectAnalytics && !headers.includes(origin)) + errors.push(`Production analytics CSP is missing ${origin}`); + if (!expectAnalytics && headers.includes(origin)) + errors.push(`Analytics-free build unexpectedly permits ${origin}`); + } +} + +const analyticsScript = "https://static.cloudflareinsights.com/beacon.min.js"; +if (expectAnalytics && !allHtml.includes(analyticsScript)) + errors.push( + "Production build is missing the Cloudflare Web Analytics beacon", + ); +if (!expectAnalytics && allHtml.includes(analyticsScript)) + errors.push( + "Analytics-free build unexpectedly contains the analytics beacon", + ); + +const deployWorkflow = readFileSync( + join(root, ".github/workflows/deploy.yml"), + "utf8", +); +const ciWorkflow = readFileSync(join(root, ".github/workflows/ci.yml"), "utf8"); + +if (!deployWorkflow.includes("branches: [dev, main]")) + errors.push("Deployment workflow must deploy only dev and main"); +if (!ciWorkflow.includes("branches: [dev]")) + errors.push("Push CI workflow must run on dev"); +if (/core_development/.test(`${deployWorkflow}\n${ciWorkflow}`)) + errors.push("Temporary feature branches must not be workflow triggers"); + +if (errors.length > 0) { + console.error(`Site contract checks failed:\n- ${errors.join("\n- ")}`); + process.exit(1); +} + +console.log( + `Site contract checks passed for ${htmlFiles.length} generated pages.`, +); diff --git a/scripts/configure-analytics-output.mjs b/scripts/configure-analytics-output.mjs new file mode 100644 index 0000000..d560eef --- /dev/null +++ b/scripts/configure-analytics-output.mjs @@ -0,0 +1,23 @@ +import { readFile, writeFile } from "node:fs/promises"; + +const analyticsToken = process.env.CLOUDFLARE_WEB_ANALYTICS_TOKEN?.trim(); + +if (analyticsToken) { + const headersUrl = new URL("../dist/_headers", import.meta.url); + const headers = await readFile(headersUrl, "utf8"); + const configuredHeaders = headers + .replace( + "connect-src 'self'", + "connect-src 'self' https://cloudflareinsights.com", + ) + .replace( + "script-src 'self'", + "script-src 'self' https://static.cloudflareinsights.com", + ); + + if (configuredHeaders === headers) { + throw new Error("Unable to add Cloudflare Web Analytics to the CSP."); + } + + await writeFile(headersUrl, configuredHeaders); +} diff --git a/scripts/write-deployment-metadata.mjs b/scripts/write-deployment-metadata.mjs new file mode 100644 index 0000000..471a320 --- /dev/null +++ b/scripts/write-deployment-metadata.mjs @@ -0,0 +1,26 @@ +import { mkdirSync, writeFileSync } from "node:fs"; +import { join, resolve } from "node:path"; + +const [outputArgument = "dist", commit, branch] = process.argv.slice(2); + +if (!/^[0-9a-f]{40}$/i.test(commit ?? "")) { + console.error("Deployment metadata requires a 40-character Git commit SHA."); + process.exit(1); +} + +if (!branch || !/^[A-Za-z0-9._/-]+$/.test(branch)) { + console.error("Deployment metadata requires a valid Git branch name."); + process.exit(1); +} + +const output = resolve(outputArgument); +mkdirSync(output, { recursive: true }); +writeFileSync( + join(output, "deployment.json"), + `${JSON.stringify({ commit: commit.toLowerCase(), branch }, null, 2)}\n`, + "utf8", +); + +console.log( + `Wrote deployment metadata for ${branch} at ${commit.slice(0, 8)}.`, +); diff --git a/src/components/FaqList.astro b/src/components/FaqList.astro new file mode 100644 index 0000000..355d407 --- /dev/null +++ b/src/components/FaqList.astro @@ -0,0 +1,16 @@ +--- +interface Props { + items: { question: string; answer: string }[]; +} + +const { items } = Astro.props; +--- + +<div class="faq-list"> + {items.map((item) => ( + <details> + <summary>{item.question}</summary> + <p>{item.answer}</p> + </details> + ))} +</div> diff --git a/src/components/FlodeskForm.astro b/src/components/FlodeskForm.astro new file mode 100644 index 0000000..a2c59b1 --- /dev/null +++ b/src/components/FlodeskForm.astro @@ -0,0 +1,77 @@ +--- +type FormKind = 'general' | 'starters' | 'prep'; + +interface Props { + kind: FormKind; + heading?: string; + description?: string; +} + +const forms = { + general: { + id: '63f9e4e2f0116a4cabfef7db', + config: 'eyJ0cmlnZ2VyIjp7Im1vZGUiOiJpbW1lZGlhdGVseSIsInZhbHVlIjowfSwib25TdWNjZXNzIjp7Im1vZGUiOiJtZXNzYWdlIiwibWVzc2FnZSI6IiIsInJlZGlyZWN0VXJsIjoiaHR0cHM6Ly9saXNib25kYXRhc2NpZW5jZS5vcmcvIn0sImNvaSI6ZmFsc2UsInNob3dGb3JSZXR1cm5WaXNpdG9ycyI6dHJ1ZSwibm90aWZpY2F0aW9uIjpmYWxzZX0=', + }, + starters: { + id: '615c9641c71e8685f183bd18', + config: 'eyJ0cmlnZ2VyIjp7Im1vZGUiOiJpbW1lZGlhdGVseSIsInZhbHVlIjowfSwib25TdWNjZXNzIjp7Im1vZGUiOiJyZWRpcmVjdCIsIm1lc3NhZ2UiOiIiLCJyZWRpcmVjdFVybCI6Imh0dHBzOi8vbGlzYm9uZGF0YXNjaWVuY2Uub3JnIn0sImNvaSI6ZmFsc2UsInNob3dGb3JSZXR1cm5WaXNpdG9ycyI6dHJ1ZSwibm90aWZpY2F0aW9uIjpmYWxzZX0=', + }, + prep: { + id: '6172e62fe625581864e3f857', + config: 'eyJ0cmlnZ2VyIjp7Im1vZGUiOiJpbW1lZGlhdGVseSIsInZhbHVlIjowfSwib25TdWNjZXNzIjp7Im1vZGUiOiJyZWRpcmVjdCIsIm1lc3NhZ2UiOiIiLCJyZWRpcmVjdFVybCI6Imh0dHBzOi8vd3d3Lmxpc2JvbmRhdGFzY2llbmNlLm9yZy9wcmVwLWNvdXJzZS8ifSwiY29pIjpmYWxzZSwic2hvd0ZvclJldHVyblZpc2l0b3JzIjp0cnVlLCJub3RpZmljYXRpb24iOmZhbHNlfQ==', + }, +} as const; + +const { kind, heading = 'Get the next announcement', description = 'Join the LDSA mailing list. We only send useful Academy news.' } = Astro.props; +const form = forms[kind]; +const rootSelector = `.flodesk-${form.id}`; +--- + +<section class={`newsletter flodesk-${form.id}`} data-ff-el="root" data-ff-version="3" data-ff-type="inline" data-ff-name="inlineNoImage" aria-labelledby={`newsletter-${form.id}`}> + <div data-ff-el="config" data-ff-config={form.config} hidden></div> + <div class="newsletter-copy"> + <p class="eyebrow">Stay in the loop</p> + <h2 id={`newsletter-${form.id}`}>{heading}</h2> + <p>{description}</p> + </div> + <form action={`https://form.flodesk.com/forms/${form.id}/submit`} method="post" data-ff-el="form" class="newsletter-form"> + <div class="newsletter-fields" data-ff-el="content"> + <div data-ff-el="fields"> + <label for={`email-${form.id}`}>Email address</label> + <input id={`email-${form.id}`} type="email" maxlength="255" name="email" autocomplete="email" placeholder="you@example.com" required data-ff-tab="email::submit" /> + <input class="honeypot" type="text" name="confirm_email_address" tabindex="-1" autocomplete="off" aria-hidden="true" /> + </div> + {kind === 'general' && ( + <fieldset class="preferences"> + <legend>What are you interested in?</legend> + <label><input type="checkbox" name="preferences" value="option1" /> Starters Academy</label> + <label><input type="checkbox" name="preferences" value="option2" /> Prep Course</label> + </fieldset> + )} + <button class="button button-light" type="submit" data-ff-el="submit" data-ff-tab="submit">Subscribe</button> + </div> + <div class="form-success" data-ff-el="success" role="status">Thank you for subscribing. Keep an eye on your inbox.</div> + <div class="form-error" data-ff-el="error" role="alert"></div> + </form> +</section> + +<script is:inline define:vars={{ formId: form.id, rootSelector }}> + (function (w, d, t, n) { + w.FlodeskObject = n; + var fn = function () { (w[n].q = w[n].q || []).push(arguments); }; + w[n] = w[n] || fn; + var first = d.getElementsByTagName(t)[0]; + var version = '?v=' + Math.floor(Date.now() / 120000) * 60; + var modern = d.createElement(t); + modern.async = true; + modern.type = 'module'; + modern.src = 'https://assets.flodesk.com/universal.mjs' + version; + first.parentNode.insertBefore(modern, first); + var legacy = d.createElement(t); + legacy.async = true; + legacy.noModule = true; + legacy.src = 'https://assets.flodesk.com/universal.js' + version; + first.parentNode.insertBefore(legacy, first); + })(window, document, 'script', 'fd'); + window.fd('form:handle', { formId: formId, rootEl: rootSelector }); +</script> diff --git a/src/components/Footer.astro b/src/components/Footer.astro new file mode 100644 index 0000000..84f9343 --- /dev/null +++ b/src/components/Footer.astro @@ -0,0 +1,29 @@ +--- +import { navigation, socialLinks } from '../data/site'; +--- + +<footer class="site-footer"> + <div class="shell footer-grid"> + <div> + <img class="footer-logo" src="/images/ldsa-logo.png" width="178" height="70" alt="" /> + <p>A volunteer-run, non-profit learning community helping people enter data science.</p> + </div> + <div> + <h2>Explore</h2> + <nav aria-label="Footer"> + {navigation.map((item) => <a href={item.href}>{item.label}</a>)} + </nav> + </div> + <div> + <h2>Connect</h2> + <a href="mailto:info@lisbondatascience.org">info@lisbondatascience.org</a> + <div class="social-links"> + {socialLinks.map((item) => <a href={item.href} target="_blank" rel="noreferrer">{item.label}<span class="sr-only"> (opens in a new tab)</span></a>)} + </div> + </div> + </div> + <div class="shell footer-bottom"> + <span>© {new Date().getFullYear()} Lisbon Data Science Academy</span> + <span>Built in the open by the LDSA community.</span> + </div> +</footer> diff --git a/src/components/Header.astro b/src/components/Header.astro new file mode 100644 index 0000000..45bd73f --- /dev/null +++ b/src/components/Header.astro @@ -0,0 +1,41 @@ +--- +import { navigation } from '../data/site'; + +const pathname = Astro.url.pathname; +--- + +<header class="site-header" data-site-header> + <a class="skip-link" href="#main-content">Skip to content</a> + <div class="shell header-inner"> + <a class="brand" href="/" aria-label="Lisbon Data Science Academy home"> + <img src="/images/ldsa-logo.png" width="178" height="70" alt="Lisbon Data Science Academy" /> + </a> + <button class="menu-button" type="button" aria-expanded="false" aria-controls="primary-navigation" data-menu-button> + <span class="sr-only">Open menu</span> + <span aria-hidden="true"></span><span aria-hidden="true"></span><span aria-hidden="true"></span> + </button> + <nav id="primary-navigation" class="primary-navigation" aria-label="Primary" data-menu> + {navigation.map((item) => ( + <a href={item.href} aria-current={pathname === item.href ? 'page' : undefined}>{item.label}</a> + ))} + </nav> + </div> +</header> + +<script> + const button = document.querySelector<HTMLButtonElement>('[data-menu-button]'); + const menu = document.querySelector<HTMLElement>('[data-menu]'); + + button?.addEventListener('click', () => { + const open = button.getAttribute('aria-expanded') === 'true'; + button.setAttribute('aria-expanded', String(!open)); + menu?.toggleAttribute('data-open', !open); + }); + + menu?.addEventListener('click', (event) => { + if ((event.target as HTMLElement).closest('a')) { + button?.setAttribute('aria-expanded', 'false'); + menu.removeAttribute('data-open'); + } + }); +</script> diff --git a/src/data/site.ts b/src/data/site.ts new file mode 100644 index 0000000..e337c24 --- /dev/null +++ b/src/data/site.ts @@ -0,0 +1,170 @@ +export const navigation = [ + { href: "/", label: "Home" }, + { href: "/starters-academy/", label: "Starters Academy" }, + { href: "/prep-course/", label: "Prep Course" }, + { href: "/about-us/", label: "About us" }, + { href: "/jobs/", label: "Jobs" }, +]; + +export const socialLinks = [ + { + href: "https://www.linkedin.com/company/lisbondatascience/", + label: "LinkedIn", + }, + { href: "https://www.instagram.com/lisbon_datascience/", label: "Instagram" }, + { href: "https://www.facebook.com/LisbonDataScience/", label: "Facebook" }, + { href: "https://github.com/LDSSA", label: "GitHub" }, +]; + +export const externalLinks = { + portal: "https://portal.lisbondatascience.org/", + wiki: "https://ldssa.github.io/wiki/", + applicationProcess: + "https://ldssa.github.io/wiki/Applicants/Application-process/", + startersCourse: + "https://ldssa.github.io/wiki/Starters%20Academy%20(LDSSA)/01-Starters-Academy-(Course)/", + prepCourse: + "https://ldssa.github.io/wiki/DS%20Prep%20Course/Data-Science-Prep-Course/", + prepRepository: "https://github.com/LDSSA/ds-prep-course", + jobs: "https://ldssa.github.io/wiki/Jobs/How-to-offer-a-job-on-the-Lisbon-Data-Science-Academy-website/", +}; + +export const team = [ + "Maria Dominguez", + "Minh Hoang", + "Catarina Silva", + "Juliane Silva", + "Carol Martins", + "Christine Maroti", + "Filipa Rodrigues", + "Rita Carvalho", + "Hugo Castilho", + "Rodrigo Amaro e Silva", + "Inês Pessoa", + "Susan Wang", + "Ivo Bernardo", + "Pratiksha Jain", + "Juliana Rodrigues", + "Gonçalo Nunes", + "Carla Cotas", + "Nuno Brás", + "Pedro Fonseca", + "Rita Sousa", + "Ricardo Alberto", + "Wilson Ramos", + "José Miguel Santos", + "Pavel Nikalayeu", + "Rui Figueiredo", + "Sam Hopkins", + "Telmo Felgueira", + "Mariana Fernandes", + "Sonia Seyed Allalei", + "Inês Mendes", + "Helena Carvalho", + "Daniel Sousa", + "Sofia Ferreira", + "Pedro Ved", + "Pedro Ribeiro", + "Inês Pires", + "Silvana Alves", + "Ricardo Robalo", + "Phuc Nguyen", + "Gustavo Fonseca", + "Diana Baltrusaitis", + "Tiago Ferrão", + "Miguel Dias", + "Diana Vieira", + "João Correia", + "Sara Gomes", + "Fábio Cruz", + "José Rebelo", +]; + +export const startersFaq = [ + { + question: "What do I need to know before applying?", + answer: + "You should be comfortable programming in Python, using the command line, Git and GitHub, and working with basic linear algebra and NumPy. The free Prep Course covers these foundations.", + }, + { + question: "Is prior data science experience required?", + answer: + "No. The Academy is designed for people taking their first steps in data science who already have the required Python and quantitative foundations.", + }, + { + question: "How does admission work?", + answer: + "Applicants complete a timed coding test and introductory data science exercises. Candidates who pass the technical phase become eligible for selection. The official wiki contains the current process and restrictions.", + }, + { + question: "How much time should I reserve?", + answer: + "Plan for about 10 hours each week, with an absolute minimum of 5 hours during unusually busy weeks. Attendance at the synchronous opening and selected hackathons is required.", + }, + { + question: "Is the course remote?", + answer: + "The course is designed to run online through GitHub, Slack, synchronous sessions, mentoring and the Academy submission system. Check the wiki for the current batch format.", + }, + { + question: "Will I receive support?", + answer: + "Yes. Instructors, mentors, staff and fellow students form an active learning community throughout the programme.", + }, + { + question: "Are scholarships available?", + answer: + "LDSA has historically reserved places for people in low-income situations. Availability and application details for each batch are published in the official application guide.", + }, + { + question: "What language is used?", + answer: "Teaching and course materials are in English.", + }, + { + question: "Do I need my own laptop?", + answer: + "Yes. You need a laptop with at least 8 GB of RAM and permission to install the required software.", + }, + { + question: "Are the learning materials public?", + answer: + "Yes. LDSA publishes its learning materials openly. Independent learners can follow them, while enrolled students also receive live events, grading, mentoring and community access.", + }, +]; + +export const prepFaq = [ + { + question: "Who is the Prep Course for?", + answer: + "It is designed for people without prior programming experience who want the foundations needed for the Starters Academy.", + }, + { + question: "What topics are covered?", + answer: + "Python programming, NumPy, basic linear algebra, the Unix command line, Git, text editors, Python modules, packaging and a final project.", + }, + { + question: "How much does it cost?", + answer: "The Prep Course is free.", + }, + { + question: "How much time should I reserve?", + answer: + "Beginners should plan for at least 10 hours each week. Learners who already know some topics may need less time.", + }, + { + question: "Does completion guarantee a Starters Academy place?", + answer: + "No. It prepares you for the admission process and improves your foundations, but does not guarantee admission.", + }, + { + question: "Is special software required?", + answer: + "Yes. Setup instructions are provided, and you need a computer on which you can install software.", + }, + { + question: "Will I receive a certificate?", + answer: + "The current public course information does not promise a certificate. The focus is on practical knowledge and preparation.", + }, +]; diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro new file mode 100644 index 0000000..2360ffc --- /dev/null +++ b/src/layouts/BaseLayout.astro @@ -0,0 +1,62 @@ +--- +import Header from '../components/Header.astro'; +import Footer from '../components/Footer.astro'; +import '../styles/global.css'; + +interface Props { + title: string; + description: string; + image?: string; + noindex?: boolean; +} + +const { + title, + description, + image = '/images/community.jpeg', + noindex = false, +} = Astro.props; +const canonical = new URL(Astro.url.pathname, Astro.site); +const pageTitle = title === 'Lisbon Data Science Academy' ? title : `${title} · Lisbon Data Science Academy`; +const socialImage = new URL(image, Astro.site); +const analyticsToken = import.meta.env.CLOUDFLARE_WEB_ANALYTICS_TOKEN?.trim(); +--- + +<!doctype html> +<html lang="en"> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width" /> + <meta name="generator" content={Astro.generator} /> + <title>{pageTitle} + + + + + {noindex && } + + + + + + + + + + + + +
+
+ +
+