From 523e822b39e5c5cf22da4a75bc510abebd454b58 Mon Sep 17 00:00:00 2001 From: Jad wauthier Date: Sat, 8 Aug 2026 20:39:36 -0500 Subject: [PATCH 1/2] Add project scaffold: Astro, TypeScript, CI pipeline Astro (server output, Cloudflare Workers adapter with static assets), TypeScript, ESLint/markdownlint/html-validate, Vitest, and Playwright + axe-core for WCAG 2.2 AA accessibility testing. GitHub Actions runs lint, typecheck, unit tests, build, HTML lint, and accessibility/e2e tests on every pull request. Corrects earlier "Cloudflare Pages" references in PROJECT.md and docs to Cloudflare Workers with static assets, reflecting what the current Astro Cloudflare adapter actually targets. --- .github/workflows/ci.yml | 60 + .gitignore | 10 + .htmlvalidate.json | 3 + .markdownlint-cli2.jsonc | 9 + PROJECT.md | 46 +- astro.config.mjs | 9 + docs/ACCESSIBILITY.md | 14 +- docs/CI_TESTING.md | 49 +- docs/PRD.md | 8 +- docs/SECURITY.md | 6 +- eslint.config.js | 12 + package-lock.json | 9358 ++++++++++++++++++++++++++++++++++ package.json | 43 + playwright.config.ts | 21 + src/layouts/BaseLayout.astro | 35 + src/lib/health.ts | 6 + src/pages/api/health.ts | 10 + src/pages/index.astro | 12 + tests/e2e/home.a11y.spec.ts | 19 + tests/unit/health.test.ts | 14 + tsconfig.json | 9 + vitest.config.ts | 13 + wrangler.toml | 18 + 23 files changed, 9734 insertions(+), 50 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 .htmlvalidate.json create mode 100644 .markdownlint-cli2.jsonc create mode 100644 astro.config.mjs create mode 100644 eslint.config.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 playwright.config.ts create mode 100644 src/layouts/BaseLayout.astro create mode 100644 src/lib/health.ts create mode 100644 src/pages/api/health.ts create mode 100644 src/pages/index.astro create mode 100644 tests/e2e/home.a11y.spec.ts create mode 100644 tests/unit/health.test.ts create mode 100644 tsconfig.json create mode 100644 vitest.config.ts create mode 100644 wrangler.toml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c2b5d7c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,60 @@ +name: CI + +on: + pull_request: + branches: [main] + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + - run: npm ci + - run: npm run lint:js + - run: npm run lint:md + - run: npm run typecheck + + unit-tests: + name: Unit Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + - run: npm ci + - run: npm run test:unit + + build-and-html-lint: + name: Build and HTML Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + - run: npm ci + - run: npm run build + - run: npm run lint:html + + accessibility-e2e: + name: Accessibility and E2E Tests + runs-on: ubuntu-latest + needs: build-and-html-lint + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + - run: npm ci + - run: npx playwright install --with-deps chromium + - run: npm run build + - run: npm run test:e2e diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5d21875 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +node_modules/ +dist/ +.astro/ +.wrangler/ +.env +.env.* +!.env.example +playwright-report/ +test-results/ +coverage/ diff --git a/.htmlvalidate.json b/.htmlvalidate.json new file mode 100644 index 0000000..8f35b44 --- /dev/null +++ b/.htmlvalidate.json @@ -0,0 +1,3 @@ +{ + "extends": ["html-validate:recommended", "html-validate:a11y"] +} diff --git a/.markdownlint-cli2.jsonc b/.markdownlint-cli2.jsonc new file mode 100644 index 0000000..a59b408 --- /dev/null +++ b/.markdownlint-cli2.jsonc @@ -0,0 +1,9 @@ +{ + "config": { + "default": true, + "MD013": false, + "MD033": false + }, + "globs": ["**/*.md"], + "ignores": ["node_modules/**", "dist/**"] +} diff --git a/PROJECT.md b/PROJECT.md index 8896ff1..6a67c82 100644 --- a/PROJECT.md +++ b/PROJECT.md @@ -13,7 +13,7 @@ team-maintained commercial product. Tech-Clusive Solutions, LLC. * Primary contributor: Jad Wauthier (the Blind Tech Mage). * GitHub account: BlindTechMage. -* Contact email: info@BlindTechMage.com. +* Contact email: . * SSH key for GitHub operations: `C:/Users/jwauthier/.ssh/btm_cynthus_ed25519`. ## Working Agreement @@ -68,17 +68,21 @@ below) rather than following a full formal field checklist. requests. Merge instead requires an explicit verbal "proceed" from the primary contributor in conversation, given each time, regardless of CI status. * No semantic-versioning or release-branch machinery. This is a live site, not a - versioned distributed product. Deploys happen on merge to `main` via Cloudflare - Pages' automatic deployment. + versioned distributed product. Deploys happen on merge to `main` via a + dedicated deploy workflow that runs `wrangler deploy` (see Environmental + Conditions — this project deploys to Cloudflare Workers with static assets, + not classic Cloudflare Pages, which does not have the same git-integration + auto-deploy Pages offered; deploy automation is a separate, not-yet-built CI + workflow). ## CI Policy * A GitHub Actions workflow runs on every pull request targeting `main`, executing: - - Linting for Markdown, JavaScript/TypeScript, and HTML. - - Unit tests for all executable/functional code, regardless of language + * Linting for Markdown, JavaScript/TypeScript, and HTML. + * Unit tests for all executable/functional code, regardless of language (TypeScript, Python, or anything else introduced later). Every function or module implementing real behavior must have a corresponding unit test that - verifies its functionality. This applies to Pages Functions/Worker logic (e.g. + verifies its functionality. This applies to Cloudflare Worker route logic (e.g. the contact form handler, resource search/filter API) as much as to frontend code. A pull request introducing functional code with no corresponding test is considered incomplete. @@ -126,7 +130,7 @@ below) rather than following a full formal field checklist. * A linter is configured from the start for each language in use (ESLint for TypeScript/JavaScript, markdownlint for Markdown, an HTML linter/validator), run locally and enforced in CI. -* TypeScript is used for all functional/executable code — Pages Functions/Worker +* TypeScript is used for all functional/executable code — Cloudflare Worker route logic, interactive frontend components — not plain JavaScript. * Every first-order module/file has exactly one primary responsibility, unless additional code is a directly related helper or configuration type. @@ -146,15 +150,25 @@ below) rather than following a full formal field checklist. ## Environmental Conditions -* Target platform: Cloudflare Pages (static frontend) plus Cloudflare Pages - Functions/Workers (TypeScript) plus D1 (SQLite-compatible database). +* Target platform: Cloudflare Workers with static assets (via + `@astrojs/cloudflare`, which targets Workers rather than classic Cloudflare + Pages), plus D1 (SQLite-compatible database). This is a correction from an + earlier assumption that the platform would be classic Cloudflare Pages — + the Astro adapter's current majors generate a Workers-style deployment + (`wrangler.toml` with an `[assets]` binding), not a Pages-style one. The + practical effect is the same (Cloudflare-hosted, TypeScript route handlers, + D1-backed), but deployment is via `wrangler deploy` in CI rather than Pages' + built-in git-integration auto-deploy. * Frontend framework: Astro, chosen for its island-based interactivity model, which fits a mostly-static site with a small number of interactive pages (resource - search/filter). + search/filter). The site builds in Astro's `server` output mode (required for + Worker route/API handling), with individual static content pages opted into + prerendering (`export const prerender = true`) so they build to plain HTML + rather than being server-rendered per request. * Language: TypeScript for all functional/executable code. * No self-managed server, no OS/runtime matrix beyond what Cloudflare's platform dictates. The previously used server (`fornax.techclusivesolutions.com`) is - retired from serving this site once cutover to Cloudflare Pages is complete. + retired from serving this site once cutover to Cloudflare Workers is complete. * Credential storage uses Cloudflare's native secrets/bindings mechanism. Never plaintext, never committed. * Any dependency that is unmaintained, deprecated, or otherwise at risk is flagged @@ -201,7 +215,9 @@ Plan → Design → Track → Implement → Test process before work begins in t blindness-related resources, project details) in D1. * Contact form implementation details (delivery mechanism, spam/abuse mitigation). * Cloudflare Access configuration for any admin/write-gated routes. -* DNS cutover from `fornax.techclusivesolutions.com` to Cloudflare Pages — decided - in principle, but not yet executed pending a working deployment to point to. This - requires explicit confirmation before being carried out, as a live change to a - working domain. +* A deploy workflow (GitHub Actions running `wrangler deploy` on merge to `main`, + using a Cloudflare API token stored as a repository secret) — not yet built. +* DNS cutover from `fornax.techclusivesolutions.com` to the deployed Cloudflare + Worker — decided in principle, but not yet executed pending a working deployment + to point to. This requires explicit confirmation before being carried out, as a + live change to a working domain. diff --git a/astro.config.mjs b/astro.config.mjs new file mode 100644 index 0000000..b8de51e --- /dev/null +++ b/astro.config.mjs @@ -0,0 +1,9 @@ +import { defineConfig } from 'astro/config'; +import cloudflare from '@astrojs/cloudflare'; + +export default defineConfig({ + output: 'server', + adapter: cloudflare({ + imageService: 'cloudflare', + }), +}); diff --git a/docs/ACCESSIBILITY.md b/docs/ACCESSIBILITY.md index f90de2d..63b36ea 100644 --- a/docs/ACCESSIBILITY.md +++ b/docs/ACCESSIBILITY.md @@ -32,21 +32,21 @@ that no feature bypasses. ## Specific Requirements Carried From Project Discussion -* No custom-styled interactive control (dropdowns, filter panels, etc.) built as +- No custom-styled interactive control (dropdowns, filter panels, etc.) built as divs pretending to be native form controls. Real semantic elements (`