diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..77ce572 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,61 @@ +name: Deploy FELG Dashboard + +on: + push: + branches: [main] + workflow_dispatch: + +env: + PUBLIC_SUPABASE_URL: ${{ secrets.PUBLIC_SUPABASE_URL }} + PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.PUBLIC_SUPABASE_ANON_KEY }} + SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }} + +jobs: + validate-sync-deploy: + runs-on: ubuntu-latest + permissions: + contents: read + pages: write + id-token: write + + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + + - name: Install dependencies + run: npm ci + + # Step 1: Validate the data file + - name: Validate dashboard.json + run: node scripts/validate-data.js + + # Step 2: Sync validated data to Supabase + - name: Sync to Supabase + run: node scripts/sync-to-supabase.js + + # Step 3: Build the Astro site + - name: Build Astro + run: npm run build + + # Step 4: Upload and deploy to GitHub Pages + - name: Setup Pages + uses: actions/configure-pages@v4 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: dist + + - name: Deploy to Pages + id: deployment + uses: actions/deploy-pages@v4 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..016b59e --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# build output +dist/ + +# generated types +.astro/ + +# dependencies +node_modules/ + +# logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# environment variables +.env +.env.production + +# macOS-specific files +.DS_Store + +# jetbrains setting folder +.idea/ diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..22a1505 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,4 @@ +{ + "recommendations": ["astro-build.astro-vscode"], + "unwantedRecommendations": [] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..d642209 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "command": "./node_modules/.bin/astro dev", + "name": "Development server", + "request": "launch", + "type": "node-terminal" + } + ] +} diff --git a/AGENT-PROMPT.md b/AGENT-PROMPT.md new file mode 100644 index 0000000..95cc124 --- /dev/null +++ b/AGENT-PROMPT.md @@ -0,0 +1,111 @@ +# π€ FELG Dashboard β Agent Build Prompt + +Copy this document into a coding agent's context (Claude Code, or equivalent) to execute the MVP build. It is self-contained but assumes the agent also has read access to [`PRJ-FELG.md`](./PRJ-FELG.md) (source of truth, **v1.3.0-r1 / Amendment A3**) and [`IMPLEMENTATION-GUIDE.md`](./IMPLEMENTATION-GUIDE.md) (architecture) in the same repo. + +> **Amendment A3:** the frontend is **Astro + Tailwind CSS**, not vanilla HTML/CSS/JS. Brand detail and "all brands" views are real Astro routes, not hash-based fake routes. The backend (Supabase + Realtime, Amendment A2) is unchanged. If you've seen an earlier version of this prompt describing a hand-written `router.js` and raw ES module render functions, that version is stale β this one supersedes it. + +--- + +## Your Mission + +Build the MVP (Phase 1) of the FELG Ecosystem Dashboard: a public, open web app β **no signup, ever, for viewing** β dark-themed, built with Astro + Tailwind CSS, displaying four FELG sections β π Fun, π° Earning, π Learning, π«Ά Giving β in that exact order, backed by Supabase for storage and realtime push delivery, deployable to GitHub Pages. + +Before writing any code, read `PRJ-FELG.md` in full and `IMPLEMENTATION-GUIDE.md` Β§1β7. Every requirement below cites an FR/NFR number from the PRD β if you're ever unsure what a requirement means, that number is where the definitive answer lives, not this prompt. + +--- + +## Non-Negotiables (violating any of these is a failed build, not a style choice) + +| Rule | Detail | Source | +|:---|:---|:---| +| **Section order is fixed** | Fun β Earning β Learning β Giving, in the DOM, always. Never data-driven. | FR-40, π IMMUTABLE | +| **No authentication, anywhere** | No login form, no session, no auth-gated route, no admin panel. This is an open web app β nothing gates viewing. | FR-44, π IMMUTABLE, NFR-09 | +| **Dark mode is the only mode** | No light-mode toggle, no `darkMode` config in Tailwind. One theme. | PRD Out of Scope table | +| **No calculated score field** | Never add a `felg_score`, `contribution_score`, or similar aggregate. | PRD Out of Scope table | +| **Astro + Tailwind, static output** | `output: 'static'` in `astro.config.mjs`. No SSR, no API routes β Supabase is the only backend this product needs. Don't reach for React/Vue/Svelte components; plain Astro components + small client-side TypeScript scripts are sufficient for everything this build requires. | PRD Amendment A3, Implementation Guide Β§2 | +| **Brand navigation uses real routes, not a hand-written router** | `src/pages/brand/[id].astro` with `getStaticPaths()`. Do not build a hash-based router or a click-handler-driven modal system β plain `` links plus the browser's native back button satisfy FR-49β52 for free. | FR-67, Implementation Guide Β§5 | +| **Realtime delivery IS required β this is MVP, not a stretch goal** | The dashboard must push updates to open browsers via Supabase Realtime, with no client polling loop. | FR-65, PRD Amendment A2 | +| **Data ingestion is still manual for MVP** | Do not build Postiz/SendGrid/GitHub API integrations. Content still comes from a human editing `data/dashboard.json` in a PR. | PRD Phasing Β§, Phase 2 unchanged | +| **The service-role Supabase key must never reach the client** | It exists only as a GitHub Actions secret, used exclusively by `scripts/sync-to-supabase.js`. The browser and the Astro build-time frontmatter both use only the public anon key, safe specifically because RLS restricts it to SELECT-only. | FR-66, π IMMUTABLE | +| **Live-patch scripts never own page structure** | `src/scripts/live/patch/*.ts` update specific DOM nodes (tagged `data-live="..."`) or rebuild a small, scoped sub-tree. They do not duplicate whole-page layout logic that belongs in the `.astro` components. | Implementation Guide Β§4.4 | + +--- + +## What You're Building + +Follow the repository structure in `IMPLEMENTATION-GUIDE.md` Β§3 exactly. In short: + +``` +astro.config.mjs, tailwind.config.mjs, package.json +src/lib/supabase.ts (public client β used at build time AND client-side) +src/layouts/Layout.astro, src/components/*.astro +src/pages/index.astro, src/pages/brands.astro, src/pages/brand/[id].astro +src/scripts/live/client.ts, src/scripts/live/patch/*.ts +data/dashboard.json (human-edited source β not fetched at runtime) +supabase/schema.sql +scripts/{validate-data,sync-to-supabase}.js +.github/workflows/deploy.yml (validate β sync β astro build β deploy) +``` + +The data shape must match PRD Appendix B **exactly** β same keys, same nesting, same enum values (`status` β `live | in_progress | not_started | paused`). Do not invent fields. This shape lives as a single JSONB column on one Supabase row (`id = 'current'`) β see Implementation Guide Β§4.3 for the exact schema and query pattern. + +**Critical architectural point:** the browser and the Astro build process both read from Supabase, never from `data/dashboard.json` directly. That file is the human-editable source; CI copies its contents into Supabase on merge, and every subsequent read β at build time or in the browser β goes through Supabase. + +--- + +## Build Order + +Work through these in sequence. Steps 2β5 prove the entire data pipeline (file β CI β Supabase β build-time render β browser realtime) end-to-end before any route/page work gets built on top of it. + +1. **Scaffold.** `npm create astro@latest`, `astro add tailwind`, configure `astro.config.mjs` (`output: 'static'`). Build `Layout.astro`, `Header.astro`, `Footer.astro`, and an `index.astro` with hardcoded sample data for all four sections, in FELG order, per FR-36β41. Get this looking right before touching Supabase. +2. **Provision Supabase.** Apply `supabase/schema.sql`, enable Realtime replication, seed one row with sample data. Confirm in the Supabase dashboard that RLS is on and anon has SELECT-only access. +3. **Wire the build-time read.** Add `src/lib/supabase.ts`; update `index.astro`'s frontmatter to fetch from Supabase instead of using hardcoded data. Run `astro build` and confirm real data appears in the output HTML. +4. **Wire the realtime subscription.** Build `src/scripts/live/client.ts` and one patch module (start with `patch/social.ts`). Manually edit the row in the Supabase dashboard UI and confirm an already-open browser tab updates with zero reload. **Do not proceed past this step until you've actually watched this happen.** +5. **Wire the CI pipeline.** Build `scripts/validate-data.js` and `scripts/sync-to-supabase.js`, then `.github/workflows/deploy.yml` (validate β sync β `astro build` β deploy). Test for real: edit `data/dashboard.json`, open a PR, merge it, and confirm the change appears live in an open tab within seconds **and** survives a fresh page reload. +6. **Brand detail pages.** Build `src/pages/brand/[id].astro` with `getStaticPaths()` sourced from the same Supabase read used in step 3. Style per PRD Β§6.2. Add the ESC-key listener (`history.back()` on Escape) for FR-51. Do not build a router β `BrandCard.astro` just links to the page with a plain ``. +7. **All Brands route.** Build `src/pages/brands.astro`, grouped by status per FR-54β57, each card linking to its `brand/[id]` page. +8. **Remaining live-patch coverage.** `patch/earning.ts`, `patch/learning.ts`, `patch/giving.ts`. Add relative "last updated"/"last synced" timestamps (FR-14, FR-39) and the manual refresh button as an explicit fallback (FR-45). +9. **Responsive + accessibility pass.** Tailwind breakpoints (`sm:`, `lg:`) for the brand grid. Full keyboard operability. Contrast-check the pillar colors against `bg`. +10. **Security verification.** From the browser console, using only the public anon key already present in the deployed bundle, attempt an `insert` or `update` against the Supabase table. It must fail. This is a launch blocker β do not skip it. +11. **Performance check.** Run Lighthouse against the built site. If LCP exceeds 3s or TTI exceeds 2s, investigate before moving on β Astro's default output should make this easier to hit than a hand-rolled site, not harder, so a miss here is worth digging into. + +Stop after step 11. Postiz/SendGrid/GitHub API integration, historical trend charts, and an admin data-entry UI are Phase 2/3 and out of scope for this pass β see Implementation Guide Β§9 if asked to scope that work later. + +--- + +## Definition of Done + +Before reporting this build complete, verify β don't assume β every item: + +- [ ] `astro build` completes with zero errors and `dist/` contains one static page per brand in `all_brands`, each with a distinct `` and OG tags (FR-67) +- [ ] Opening the deployed site renders all four sections in FunβEarningβLearningβGiving order with real data β not hardcoded β sourced from Supabase at build time +- [ ] Every FR-01 through FR-67 in the PRD has a corresponding, working piece of UI β cross-check against the FR table in Implementation Guide Β§5 module-by-module +- [ ] **You have personally watched a merged PR to `data/dashboard.json` update an already-open browser tab within seconds, with no manual refresh, and confirmed a subsequent fresh page load also reflects it.** Describe what you observed in your report. +- [ ] Attempting a write against Supabase from the browser console, using the public anon key, fails (RLS confirmed) +- [ ] The service-role key does not appear anywhere in `dist/`, `src/`, or any browser network request β grep for it before you report done +- [ ] `scripts/validate-data.js` runs clean against the shipped `dashboard.json` and fails loudly on a deliberately broken copy (test this) +- [ ] No auth code, no signup flow, no light-mode code, no score field, no React/Vue/Svelte dependency, no hand-written router exists anywhere in the diff +- [ ] Clicking a brand card navigates to `/brand//`; the browser back button returns to the prior scroll position on `/` with zero custom code involved +- [ ] ESC key closes the brand detail page (FR-51) +- [ ] Keyboard-only pass: Tab reaches every interactive element +- [ ] Lighthouse numbers meet NFR-01/NFR-02 targets β paste the actual numbers into your final report, not "should be fine" +- [ ] `.github/workflows/deploy.yml` successfully validates, syncs to Supabase, runs `astro build`, and deploys on a test push + +If any box can't be checked, say so explicitly in your summary rather than reporting the build as done β an unchecked box you didn't mention is worse than one you flagged. + +--- + +## When to Stop and Ask + +Don't guess on these β surface them instead of picking silently: + +- Whether `/brands` should be a dedicated route or an inline expansion on `/` (PRD Β§7.1 leaves this open; Implementation Guide Β§10 leans toward a route but defers to @PAT) +- Whether `sync-to-supabase.js` should run automatically on every merge or require a manual trigger β a trust/safety tradeoff for the human team to set +- Anything in `data/dashboard.json` that seems to need a field not present in PRD Appendix B +- Final hosting target if GitHub Pages direct isn't confirmed by the time you reach the deploy step β this affects `astro.config.mjs`'s `site`/`base` values +- Any FR that seems to conflict with another FR β flag the numbers, don't resolve it by picking one +- If you don't have a Supabase project to provision against β don't fabricate credentials or stub around it silently; ask for a project to be created first + +--- + +*Operating brief for PRJ-FELG.md v1.3.0-r1 (Amendment A3) / IMPLEMENTATION-GUIDE.md. Read both in full before starting step 1.* diff --git a/IMPLEMENTATION-GUIDE.md b/IMPLEMENTATION-GUIDE.md new file mode 100644 index 0000000..1ef3ae0 --- /dev/null +++ b/IMPLEMENTATION-GUIDE.md @@ -0,0 +1,334 @@ +# π οΈ FELG Dashboard β Implementation Guide + +Companion to [`PRJ-FELG.md`](./PRJ-FELG.md) **v1.3.0-r1** (Amendment A3). This document translates the PRD's 67 functional requirements into a concrete build: file structure, data contract, component boundaries, and a delivery sequence for the MVP (Phase 1, W29βW30). + +> **π Amendment A3 note:** This guide previously specified a hand-authored vanilla HTML/CSS/JS frontend with hash-based routing. Per PRD Amendment A3, the frontend is now **Astro + Tailwind CSS**, and the product is explicitly framed as an **open, public web app β no signup, ever**. The Supabase + Realtime backend from Amendment A2 is **unchanged** β this revision only touches how the frontend is authored and routed. + +Every decision below is either **locked** (taken directly from the PRD β do not relitigate) or **open** (flagged for @PAT / Core TEAM). Where a requirement is cited, the FR/NFR number is the source of truth β if this guide and the PRD ever disagree, the PRD wins and this file needs a patch. + +--- + +## 1. Architecture at a Glance + +``` +Human editor + β + ββ edits data/dashboard.json in a PR + ββ scripts/validate-data.js runs in CI + ββ PR reviewed + merged to main + β + βΌ +GitHub Actions (.github/workflows/deploy.yml) + β + ββ scripts/sync-to-supabase.js (service-role key, secret β WRITE) + β upserts the merged JSON into ONE Supabase table row + β + ββ astro build + β each page's frontmatter does a build-time READ from Supabase + β (public anon key β same key the browser will use) + β β produces static HTML with a good, real, indexable snapshot + β + ββ deploys dist/ to GitHub Pages + β + βΌ +Supabase (Postgres + Realtime) β unchanged from Amendment A2 + β + ββ one table, one row, one JSONB column (PRD Appendix B shape) + ββ RLS: anon role = SELECT only (FR-66) + ββ Realtime broadcasts UPDATE events on that row + β + βΌ +Browser (static Astro output + one small client-side "island") + β + ββ src/scripts/live/client.ts + β 1. On load: supabase.select() β catches anything newer than the build snapshot + β 2. Subscribes to Realtime β patches specific DOM nodes on every push (FR-65) + β 3. Manual refresh button re-runs step 1, as an explicit fallback (FR-45) + β + ββ Real per-page routes: /, /brands, /brand// β no client-side router (FR-67) +``` + +`β Insight βββββββββββββββββββββββββββββββββββββ` +**One Supabase client config, two runtimes.** `src/lib/supabase.ts` uses the *public* anon key and is imported in two completely different places: inside an Astro page's frontmatter (runs in Node, at build time, to produce the static snapshot) and inside the client-side island script (runs in the browser, to subscribe to live updates). Same file, same credentials, same RLS boundary β Astro's architecture makes "fetch this data" trivially reusable across build-time and runtime without duplicating the query logic. The *only* place that ever holds the service-role write key is `scripts/sync-to-supabase.js`, which never ships to a browser at all. + +**Real routes retire an entire module.** The previous vanilla plan needed a hand-written `router.js` to fake shareable brand URLs via `location.hash`. Astro's file-based routing (`src/pages/brand/[id].astro`) produces real URLs at build time β which means the browser's native back button and scroll restoration satisfy FR-50 for free, and a plain `` link (working even with JavaScript disabled) replaces what used to be a click handler + hash parser + history management. This isn't a wash β it's a net deletion of a whole category of code the framework switch buys us. +`βββββββββββββββββββββββββββββββββββββββββββββββββ` + +--- + +## 2. Tech Stack β Locked for MVP + +| Decision | Choice | Why | +|:---|:---|:---| +| Frontend framework | **Astro**, `output: 'static'` | PRD Amendment A3. Static build output (still deployable anywhere static files go), file-based routing gives real per-brand URLs (FR-67), component model maps cleanly onto the four FELG sections. | +| Styling | **Tailwind CSS** via `@astrojs/tailwind` | PRD Amendment A3. Utility classes express the existing design tokens (Β§6 below) faster than hand-rolled CSS; Astro's official integration needs zero extra build config. | +| Backend | **Supabase (Postgres + Realtime)** β unchanged | Amendment A2. Not affected by the frontend tooling change. | +| Data storage shape | **One table, one row, one JSONB column** β unchanged | PRD Appendix B shape stored wholesale; still no per-domain table normalization for MVP. | +| Routing | **Real Astro file-based routes** β `/`, `/brands`, `/brand/[id]/` | Supersedes hash routing. Satisfies FR-52 and FR-67 (shareable, indexable, real OG tags per brand) more directly than a client-side router could. | +| Data source (content) | **Manual JSON entry, PR-reviewed** β unchanged | Amendment A3 doesn't touch data sourcing. Postiz/SendGrid/GitHub ingestion is still Phase 2. | +| Hosting (frontend) | **GitHub Pages** | Astro has first-party GitHub Pages deploy support (`site`/`base` config + official deploy action). | +| Auth | **None for viewing β open web app.** Supabase RLS: anon role SELECT-only. | FR-44, FR-66, NFR-09. No signup exists anywhere in this product. | + +**Not decided yet (flag to @PAT / @SHD / @MWK):** +- Final hosting target β GitHub Pages direct vs. behind Cloudflare once `FELG.WeOwn.Net` DNS is live (PRD Risk #1); this determines the `site`/`base` values in `astro.config.mjs` (PRD Risk #9). +- Whether `sync-to-supabase.js` runs automatically on every merge or requires a manual workflow dispatch (PRD Risk #8). +- Whether `/brands` (All Brands) is its own route or an inline expansion on `/` β PRD Β§7.1 leaves this open. **Recommendation given the Astro switch:** make it a real route, consistent with brand detail pages now also being real routes β but this is still @PAT's call, not locked. + +--- + +## 3. Repository Structure + +``` +FELG-dashboard/ +βββ astro.config.mjs # output: 'static', site/base config +βββ tailwind.config.mjs # theme.extend.colors sourced from PRD's pillar-color table (Β§6 below) +βββ package.json +βββ README.md +βββ LICENSE +βββ PRJ-FELG.md +βββ IMPLEMENTATION-GUIDE.md +βββ AGENT-PROMPT.md +βββ data/ +β βββ dashboard.json # human-edited, PR-reviewed source β feeds Supabase via CI; never fetched directly by Astro or the browser +βββ supabase/ +β βββ schema.sql # table + RLS policy (unchanged from Amendment A2) +β βββ README.md +βββ scripts/ +β βββ validate-data.js # unchanged in purpose β schema + referential checks, runs before sync +β βββ sync-to-supabase.js # unchanged in purpose β service-role key, CI-only, WRITE +βββ src/ +β βββ env.d.ts +β βββ lib/ +β β βββ supabase.ts # public anon-key client β imported at build time (Node) AND client-side +β βββ layouts/ +β β βββ Layout.astro # shell, dark theme, default meta/OG tags, imports global.css +β βββ styles/ +β β βββ global.css # @tailwind base/components/utilities +β βββ components/ +β β βββ Header.astro # FR-36 +β β βββ Footer.astro # FR-38 +β β βββ BrandCard.astro # plain link β no click handler needed +β β βββ BrandGrid.astro # Top 5 + "Show All" link to /brands +β β βββ SocialSection.astro +β β βββ EarningSection.astro +β β βββ LearningSection.astro +β β βββ GivingSection.astro +β βββ pages/ +β β βββ index.astro # home β all 4 FELG sections, hardcoded order +β β βββ brands.astro # All Brands, grouped by status β real route (FR-54β60) +β β βββ brand/ +β β βββ [id].astro # per-brand detail page β getStaticPaths, real URL + OG tags (FR-67) +β βββ scripts/ +β βββ live/ +β βββ client.ts # supabase client init, initial select, realtime subscription +β βββ patch/ +β βββ brands.ts # patches Top 5 card values; full brand-list changes trigger a small rebuild +β βββ social.ts +β βββ earning.ts +β βββ learning.ts +β βββ giving.ts +βββ .github/ + βββ workflows/ + βββ deploy.yml # validate β sync-to-supabase β astro build β deploy dist/ to Pages +``` + +**What disappeared vs. the pre-Amendment-A3 plan:** `router.js` (real routes replace it entirely) and the raw `assets/js/render/*.js` module set (split into Astro components for structure + `src/scripts/live/patch/*.ts` for live updates β see Β§5). + +--- + +## 4. Data Layer + +Unchanged from Amendment A2 in every respect except *who calls it and when*. + +### 4.1 Contract + +Still PRD Appendix B, verbatim, stored as one JSONB column on one Supabase row. + +### 4.2 Validation & Sync β unchanged in purpose + +`scripts/validate-data.js` and `scripts/sync-to-supabase.js` keep doing exactly what they did in the Amendment A2 plan (see PRD Β§4.2/Β§4.3 there for the full spec) β nothing about the frontend framework changes what happens on the write side. + +### 4.3 Build-Time Read (`src/lib/supabase.ts`, used in page frontmatter) + +```ts +// src/lib/supabase.ts +import { createClient } from '@supabase/supabase-js'; + +export const supabase = createClient( + import.meta.env.PUBLIC_SUPABASE_URL, + import.meta.env.PUBLIC_SUPABASE_ANON_KEY +); +``` + +```astro +--- +// src/pages/index.astro +import { supabase } from '../lib/supabase'; +const { data } = await supabase.from('dashboard_state').select('payload').eq('id', 'current').single(); +const dashboard = data.payload; +--- +``` + +This runs during `astro build` (Node context) β the resulting HTML already has real numbers in it before a single line of client JS executes. This is what makes the static snapshot both fast (no client-side fetch needed to see *something*) and honest (it's real data, not a placeholder). + +### 4.4 Client-Side Live Patch (`src/scripts/live/client.ts`) + +```ts +import { supabase } from '../../lib/supabase'; +import { patchSocial } from './patch/social'; +import { patchEarning } from './patch/earning'; +// ...one import per section + +async function refresh() { + const { data } = await supabase.from('dashboard_state').select('payload').eq('id', 'current').single(); + applyAll(data.payload); +} + +function applyAll(payload) { + patchSocial(payload.fun.social_media); + patchEarning(payload.earning); + // ... +} + +refresh(); // catches anything newer than the build snapshot, on page load + +supabase + .channel('dashboard-changes') + .on('postgres_changes', { event: 'UPDATE', schema: 'public', table: 'dashboard_state' }, + (msg) => applyAll(msg.new.payload)) + .subscribe(); + +document.getElementById('refresh-btn')?.addEventListener('click', refresh); // FR-45 fallback +``` + +Each `patch/*.ts` function targets specific DOM nodes tagged with a `data-live="..."` attribute in the corresponding Astro component's markup β e.g. `1200` gets its `textContent` updated directly. For values that are whole collections (the brand grid, a milestone list), the patch function rebuilds that one sub-tree's `innerHTML` from a small template string rather than trying to diff individual nodes. + +`β Insight βββββββββββββββββββββββββββββββββββββ` +**This is the one place where Astro's islands model has a real cost, not just a benefit β be honest about it with the team.** The initial HTML structure lives in an `.astro` component; the live-update logic that reproduces that same structure for realtime patches lives in a separate `.ts` file. Two representations of "what a brand card looks like" now exist, and they can drift. Keep the *set* of fields that need live patching as small as the product actually requires β the numbers that genuinely benefit from sub-second freshness β rather than mirroring every field. A brand's tagline changing doesn't need a live patch path; its follower count arguably does. +`βββββββββββββββββββββββββββββββββββββββββββββββββ` + +--- + +## 5. Component Breakdown + +| Astro component (build-time structure) | Client patch module (live updates) | FRs covered | +|:---|:---|:---| +| `BrandGrid.astro`, `BrandCard.astro` | `patch/brands.ts` | FR-01β05, FR-46, FR-48, FR-64 | +| `pages/brands.astro` | `patch/brands.ts` (shared) | FR-54β60 | +| `pages/brand/[id].astro` | β (detail pages are low-frequency enough that a build-time snapshot + manual refresh is sufficient; live-patch here is optional polish, not required for MVP) | FR-47, FR-49β53, FR-67 | +| `SocialSection.astro` | `patch/social.ts` | FR-06β14 | +| `EarningSection.astro` | `patch/earning.ts` | FR-15β26 | +| `LearningSection.astro` | `patch/learning.ts` | FR-27β31 | +| `GivingSection.astro` | `patch/giving.ts` | FR-32β35 | +| `Header.astro`, `Footer.astro`, `Layout.astro` | `client.ts` (last-updated timestamp only) | FR-36β45 | + +**Brand detail navigation (FR-49β53), the Astro way:** +- Click β plain `` (FR-49). No click handler required. +- Close/Back β a normal link back to `/`, plus the browser's native back button. Scroll position is restored automatically by the browser (FR-50) β nothing to implement. +- ESC-to-close (FR-51) β a ~5-line script on the brand detail page: `document.addEventListener('keydown', e => { if (e.key === 'Escape') history.back(); })`. +- Shareable via URL (FR-52) β it's a real URL. Nothing else needed. +- Mobile full-screen (FR-53) β it's just a page; it's full-screen by definition on mobile. Style it to match the PRD Β§6.2 wireframe. + +--- + +## 6. Design System + +Same values as before β only their expression changes, from CSS custom properties to a Tailwind theme. + +```js +// tailwind.config.mjs +export default { + content: ['./src/**/*.{astro,html,js,ts}'], + theme: { + extend: { + colors: { + bg: '#0a0a1a', + text: '#e0e0ff', + 'pillar-fun': '#FF6B6B', + 'pillar-earning': '#FFD700', + 'pillar-learning': '#4ECDC4', + 'pillar-giving': '#95E1D3', + 'status-live': '#2ecc71', + 'status-progress': '#f1c40f', + 'status-not-started': '#7f8c8d', + 'status-paused': '#e74c3c', + }, + fontFamily: { + sans: ['-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Roboto', 'sans-serif'], + }, + }, + }, +}; +``` + +No `darkMode` config needed β dark is the only theme (PRD Out of Scope: no light-mode toggle), so these colors are just the default palette, not a variant. + +**Responsive grid** (FR-41): `class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-4"` on `BrandGrid.astro` β Tailwind's responsive prefixes replace the hand-written `auto-fit`/`minmax` CSS Grid rule from the pre-Amendment-A3 plan with an equivalent, more explicit utility chain. Either approach satisfies NFR-04 (320px floor); Tailwind's is more idiomatic given the rest of the stack. + +**Section color-coding**: each FELG section's container gets a `border-l-4 border-pillar-fun` (etc.) β what makes FR-40's invariant order visually legible on stream, not just structurally correct in the DOM. + +Full mockups remain @PAT's ownership per the PRD's explicit note; this token set is the floor a build starts from. + +--- + +## 7. Build Sequence (MVP, Phase 1) β REVISED per Amendment A3 + +| Step | Deliverable | Unblocks | Maps to | +|:---:|:---|:---|:---| +| 1 | `npm create astro@latest` scaffold + `astro add tailwind` + `astro.config.mjs` (`output: 'static'`) + `Layout.astro`/`Header.astro`/`Footer.astro` + `index.astro` with **hardcoded** sample data for all 4 sections in FELG order | Visual review with @PAT before any Supabase code exists | FR-36β41 | +| 2 | `supabase/schema.sql` provisioned on a real Supabase project (table + RLS + Realtime enabled) + seeded with sample data | Real data exists in Postgres | M1.5, M1.6 | +| 3 | `src/lib/supabase.ts` + wire `index.astro`'s frontmatter to fetch from Supabase at build time, replacing step 1's hardcoded data | Build-time read path proven | M1.6 | +| 4 | `src/scripts/live/client.ts` + one patch module (start with `patch/social.ts`) β manually edit the Supabase row in its dashboard UI, confirm an open browser tab updates with **no reload**. This is the single most important checkpoint in the build. | Realtime delivery proven end-to-end (FR-65) | M1.8 | +| 5 | `.github/workflows/deploy.yml` β validate β sync-to-supabase β `astro build` β deploy. Merge a real PR changing a number in `dashboard.json`; confirm it appears live in an open tab within seconds, **and** that a fresh page load also reflects it. | The actual Phase 1 promise (PR merge β live everywhere) works | M1.7 | +| 6 | `src/pages/brand/[id].astro` with `getStaticPaths()` sourced from the same build-time Supabase fetch β real per-brand URLs, OG tags, styled per PRD Β§6.2 | FR-47, FR-49β53, FR-67 | β | +| 7 | `src/pages/brands.astro` β grouped-by-status All Brands route | FR-46, FR-54β64 | β | +| 8 | Remaining `patch/*.ts` modules for Earning/Learning/Giving + relative "last updated"/"last synced" timestamps + manual refresh button as fallback | FR-14, FR-26, FR-31, FR-39, FR-45 | β | +| 9 | Responsive pass via Tailwind breakpoints + keyboard nav (ESC on brand detail) + color-contrast check | NFR-04, NFR-07 | FR-41, FR-51 | +| 10 | Security check: from the browser console, attempt a write against Supabase using the public anon key; confirm RLS rejects it | FR-66, NFR-09 | β | +| 11 | Lighthouse pass β confirm < 3s load, < 2s TTI. Astro's default minimal-JS output should make this easier to hit than the hand-rolled plan, but verify with real numbers. | NFR-01, NFR-02 | β | +| 12 | Domain cutover + finalize `astro.config.mjs` `site`/`base` against the chosen host | M1.4 | β | +| 13 | Dry run at a #DailySync β confirm every number is accurate and that a live edit during the stream actually appears on screen | M1.10 | β | + +Steps 1β5 remain the critical path β they prove the full pipeline (edit β merge β sync β build/push β render) before any page-level polish is layered on. + +--- + +## 8. QA Checklist (pre-launch, maps to NFRs) + +- [ ] `astro build` completes with zero errors and produces a `dist/` with real per-brand pages (one per entry in `all_brands`) +- [ ] Lighthouse: LCP < 3s, TTI < 2s, throttled "Fast 3G" (NFR-01, NFR-02) +- [ ] Renders correctly at 320px, 768px, 1024px, 1920px via Tailwind breakpoints (NFR-04) +- [ ] Manually verified on latest Chrome, Firefox, Safari, Edge (NFR-05) +- [ ] Each `/brand//` page has a correct, unique `` and Open Graph tags (FR-67, NFR-08) +- [ ] A merged PR to `dashboard.json` appears in an already-open browser tab within seconds, with no manual refresh, **and** survives a fresh page reload +- [ ] Attempting a write against Supabase from the browser console (public anon key) fails β RLS confirmed (FR-66) +- [ ] Service-role key does not appear anywhere in `dist/`, in `src/`, or in any browser network request +- [ ] Browser back button from a brand detail page returns to `/` at the prior scroll position (native behavior β verify it actually holds, don't just assume) +- [ ] ESC key closes/returns from the brand detail page (FR-51) +- [ ] Pillar accent colors pass WCAG AA contrast against `bg` (NFR-07) +- [ ] No ``, no auth prompt, no login/signup state anywhere in the DOM (FR-44, NFR-09) β this is an **open web app**, confirm nothing gates viewing +- [ ] FELG section order is hardcoded in `index.astro`, not data-driven (FR-40, immutable) +- [ ] The Supabase payload has zero score/index fields not in Appendix B +- [ ] If the Realtime channel disconnects, the UI indicates staleness rather than silently freezing (PRD Risk #8) +- [ ] Manual data audit: every number matches the real source it claims to represent, checked by Core TEAM before the first #DailySync + +--- + +## 9. Phase 2 / 3 Notes (not MVP scope, but design for it now) + +- **Phase 2** (API integration): Postiz/SendGrid/GitHub pollers write into the same Supabase table `sync-to-supabase.js` already established. Nothing about the Astro frontend or the live-patch scripts changes β they don't know or care where a value in the JSONB payload came from. +- **Phase 3** (Analytics & polish): historical/trend charts need a genuinely new, separate time-series table β that's real new schema work. The animated `#SoundOfMoney` counter is pure frontend polish on a value that's already live as of Phase 1. + +Do not build the historical-data table, an admin panel, or trend-chart rendering now β that's explicitly deferred in the PRD's Out of Scope table. + +--- + +## 10. Open Questions for @PAT / Core TEAM + +1. Final hosting target (GitHub Pages direct vs. behind Cloudflare) β determines `astro.config.mjs`'s `site`/`base` values, blocks step 12. +2. Whether `sync-to-supabase.js` runs automatically on every merge or requires a manual workflow dispatch. +3. Whether `/brands` is a dedicated route or an inline expansion on `/` (PRD Β§7.1 leaves this open) β recommendation above is "make it a route," but this is @PAT's call. + +--- + +*Companion to PRJ-FELG.md v1.3.0-r1 (Amendment A3). If this guide and the PRD diverge, the PRD is authoritative β update this file, don't build around the mismatch.* diff --git a/PRJ-FELG.md b/PRJ-FELG.md new file mode 100644 index 0000000..2027c6f --- /dev/null +++ b/PRJ-FELG.md @@ -0,0 +1,907 @@ +# π PRJ-FELG | FELG Ecosystem Dashboard β Product Requirements Document +βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ +PRJ-FELG.md | PRJ-FELG_v1.3.0-r1.md +βΎοΈ WeOwnNet π β Product Requirements β _S004_PROJECTS_/ +π PRJ-040 ELEVATED β #FELG Culture + Content Quality Standard +π BP-045 ENHANCED β Attestation Chain + Enhanced Related Documents +π BP-068 COMPLIANT β Multi-#LLMmodel Header +π Source of Truth: https://github.com/WeOwnNet/FELG +π Culture Governance: https://github.com/CCCbotNet/fedarch/blob/main/_GOVERNANCE_/D-070_FELG_Culture.md +## π STATUS: π‘ DRAFT β AWAITING R-011 APPROVAL +βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ + +| Field | Value | +|:------|:-------| +| **Document** | PRJ-FELG.md | +| **Version** | **v1.3.0-r1** (v1.2.0-r1 + Amendment A3 consolidated) | +| **Folder** | `_S004_PROJECTS_/` π | +| **Category** | π **PRODUCT REQUIREMENTS DOCUMENT** | +| **Lifecycle Stage** | π‘ **DRAFT β AWAITING R-011** | +| **Content Tier (PRJ-040)** | π‘οΈ **Tier 1 β Governance/Strategy** | +| **#masterCCC** | **PAT_2026-W29_1035** π | +| **Product Owner** | **@PAT π¨ (Osinachi Patrick β SinachPat βΎοΈ)** | +| **Champion** | **@GTM π― (yonksο½π€ποΈπͺο½Jason Younker βΎοΈ)** | +| **Season** | #WeOwnSeason004 π | +| **Created** | 2026-07-13 (W29 D1) | +| **Source of Truth** | https://github.com/WeOwnNet/FELG/README.md | + +--- + +## π Table of Contents + +1. [Product Overview](#-product-overview) +2. [#FELG Culture Alignment](#-felg-culture-alignment) +3. [Problem Statement](#-problem-statement) +4. [Product Vision & North Star](#-product-vision--north-star) +5. [Target Audience](#-target-audience) +6. [Functional Requirements β The Four Dashboard Sections](#-functional-requirements--the-four-dashboard-sections) +7. [Brand Detail View](#-brand-detail-view) +8. [All Brands View](#-all-brands-view) +9. [Non-Functional Requirements](#-non-functional-requirements) +10. [Tech Stack](#-tech-stack) +11. [Data Sources & Integration](#-data-sources--integration) +12. [User Interface Requirements](#-user-interface-requirements) +13. [Success Metrics & KPIs](#-success-metrics--kpis) +14. [Out of Scope](#-out-of-scope) +15. [Phasing & Milestones](#-phasing--milestones) +16. [Who Builds It](#-who-builds-it) +17. [Risks & Mitigations](#-risks--mitigations) +18. [Related Documents](#-related-documents) +19. [Appendix A: The 8 Immutable FELG Rules (D-070)](#-appendix-a-the-8-immutable-felg-rules-d-070) +20. [Appendix B: Data Schema Draft](#-appendix-b-data-schema-draft) +21. [BP-075: Self-Verifying Footer](#-bp-075-self-verifying-footer) + +--- + +## π― Product Overview + +The **FELG Dashboard** is the operational heartbeat of the βΎοΈ WeOwnNet ecosystem β a **public, real-time dashboard** that tracks what matters most across the Onchain Cooperative Association (.OCA). It is displayed at every **#DailySync #LiveStream** and is open to the world. + +**FELG** stands for **Fun π Β· Earning π° Β· Learning π Β· Giving π«Ά** β the four cultural pillars that define how the cooperative operates. The dashboard is the **visible expression** of this culture: not just metrics, but the story of an ecosystem that builds with joy, earns sustainably, learns openly, and gives freely. + +| Attribute | Value | +|:----------|:-------| +| **Product Name** | FELG Ecosystem Dashboard | +| **Domain** | FELG.WeOwn.Net | +| **Status** | π‘ PENDING β Domain not yet active | +| **Auth Model** | π’ **Public β NO authentication required for viewing** | +| **Primary Display** | Every #DailySync #LiveStream + always-on public URL | +| **License** | MIT β Open source. Open culture. Open cooperative. | +| **Repo** | github.com/WeOwnNet/FELG (π’ LIVE) | + +--- + +## ππ°ππ«Ά #FELG Culture Alignment + +| Pillar | Application to FELG Dashboard | +|--------|-------------------------------| +| π **Fun** | The dashboard is CELEBRATORY β it shows wins, brands, social energy, and community growth. It's not a corporate KPI board. It uses emojis, color, and personality to make metrics feel like a party. | +| π° **Earning** | Pipeline stats, #SoundOfMoney raised, co-op members, and investor pipeline show that the ecosystem generates real revenue and sustains itself. | +| π **Learning** | Lessons Counter, PRD tracker, and skills development tracking show that every mistake is codified and every member is growing. | +| π«Ά **Giving** | Open source contributions, community growth, and ecosystem support metrics track how the cooperative lifts others. | + +> **π IMMUTABLE:** #FELG is ALWAYS in this order: **π Fun β π° Earning β π Learning β π«Ά Giving**. The dashboard layout MUST follow this sequence. + +--- + +## β Problem Statement + +### The Problem + +The βΎοΈ WeOwnNet ecosystem is growing rapidly β multiple brands launching, revenue pipelines forming, co-op members onboarding, and social channels expanding. But there is **no single source of truth** for tracking ecosystem health across all four FELG dimensions. + +Currently: +- Pipeline status lives in PRJ documents and conversations +- Social media metrics are spread across platforms +- Brand launch progress is tracked ad-hoc +- Co-op member growth and investor pipeline are manually tracked +- Lesson documentation is not surfaced as a visible metric + +### Why Now + +#WeOwnSeason004 has 5 ecosystem brands launching, multiple revenue buckets (#ZeroTo100), co-op members onboarding, and investors engaging. Without a dashboard: +- #DailySync sessions lack a central visual anchor +- New members can't quickly see ecosystem health +- The cooperative story is hard to tell at a glance +- Metrics drift without visible accountability + +### The Solution + +A **single, public, real-time dashboard** at FELG.WeOwn.Net that displays all four FELG pillars. Open to the world. Shown at every livestream. Built for the fleet. + +--- + +## π― Product Vision & North Star + +### Vision Statement + +> **The FELG Dashboard is the heartbeat of βΎοΈ WeOwnNet β the place where cooperative culture meets real-time data. Anyone, anywhere, can see that we're building with joy, earning sustainably, learning openly, and giving freely.** + +### North Star Metric + +**Ecosystem Health Status** β a qualitative assessment across all 4 pillars, measured by: +- π **Fun:** Are brands live and social channels growing? +- π° **Earning:** Is revenue being generated and tracked? +- π **Learning:** Are lessons being documented and PRDs progressing? +- π«Ά **Giving:** Are contributions happening and community growing? + +> **Note:** This is NOT a calculated 0-100 score. It is a **section-level status indicator** (π΄/π‘/π’) per pillar, determined by the data in each dashboard section. + +### Key Design Principles + +| # | Principle | Why It Matters | +|:-:|:----------|:---------------| +| 1 | **Public by default** | Transparency is core to cooperative ownership | +| 2 | **No auth for viewing** | Anyone should be able to see ecosystem health | +| 3 | **Real-time or near-real-time** | Data should reflect current state | +| 4 | **Visual and celebratory** | This is not a corporate dashboard β it's a culture display | +| 5 | **Mobile and live-stream friendly** | Must render well on screenshare, mobile, and TV | +| 6 | **FELG order is INVARIANT** | Fun β Earning β Learning β Giving. Always. | + +--- + +## π₯ Target Audience + +| Audience | Need | How Dashboard Serves | +|:---------|:-----|:---------------------| +| **Core TEAM (@GTM, @THY, @CTO, etc.)** | Operational awareness at a glance | Pipeline stats, blockers, milestone progress | +| **Co-op members (.OCA)** | See ecosystem health + celebrate wins | Brand launches, revenue, community growth | +| **Investors & partners** | Due diligence + confidence | Revenue raised, member growth, pipeline health | +| **Public / prospective members** | Understand what WeOwnNet is about | Culture expression + visible activity | +| **#DailySync audience** | Visual anchor for livestream | Always-on dashboard displayed during syncs | + +--- + +## π Functional Requirements β The Four Dashboard Sections + +### Section 1: π Fun β Top 5 Ecosystem Brands + Social Media + +#### 1.1 Top 5 Ecosystem Brands + +Display the **Top 5** ecosystem brands in the cooperative. A "Show More" toggle reveals all brands. Each brand card is clickable to open a detailed view. + +| # | Brand | Status Tracked | +|:-:|:------|:---------------| +| π | **F1Visa.Net** βΎοΈ (.OCA) | Pre-launch signups, PRD completion, product milestones | +| π₯ | **Burned Out Media** π | #AttractionMarketing content output, client pipeline | +| π€ | **Exit To Client** | Client pipeline, service offerings, partnership development | +| π¬ | **βΎοΈ WeOwn.Chat** | Domains live, active communities, member growth | +| π» | **βΎοΈ WeOwn.Dev** | Repos, deployments, claw instances, infrastructure health | + +**Requirements:** + +| FR# | Requirement | Priority | +|:---:|:------------|:--------:| +| FR-01 | Display 5 brands with brand logo/emoji and name | P0 | +| FR-02 | Show status indicator per brand (π΄ Not Started / π‘ In Progress / π’ Live) | P0 | +| FR-03 | Show key metric per brand (e.g., signups, PRD %, member count) | P1 | +| FR-04 | Each brand name links to its website | P1 | +| FR-05 | Last updated timestamp per brand card | P2 | +| FR-46 | "Show More / View All" toggle to expand beyond Top 5 and see ALL ecosystem brands. Default = Top 5, expanded = full registry. | P0 π₯ | +| FR-47 | Click on any brand card (Top 5 or All Brands) opens Brand Detail View with full data | P0 π₯ | +| FR-48 | Full brand registry maintained as a single JSON source of truth. Top 5 is a filtered subset. | P1 | +| FR-61 | Top 5 is determined by a manually configured `top_5_ids` array β not algorithmic | P0 | +| FR-62 | `top_5_ids` can be updated by editing a single JSON array β no code changes | P1 | +| FR-63 | Fallback: if a brand in `top_5_ids` is archived, the next brand by status priority fills the slot | P2 | +| FR-64 | Section header shows count e.g., "Top 5 Brands (5 of 8)" | P1 | + +#### 1.2 Social Media Metrics + +| FR# | Requirement | Priority | +|:---:|:------------|:--------:| +| FR-06 | Display YouTube subscribers, views, watch time | P0 | +| FR-07 | Display X/Twitter followers, impressions, engagement rate | P0 | +| FR-08 | Display LinkedIn connections, post reach | P1 | +| FR-09 | Display Instagram followers, story views | P1 | +| FR-10 | Display Signal/Telegram community member count | P1 | +| FR-11 | Display Email List subscribers, open rate, click rate | P1 | +| FR-12 | All metrics show current value + trend (β/β/β) | P1 | +| FR-13 | Auto-refresh at configurable interval (default: 1 hour) | P2 | +| FR-14 | Social section has a "last synced" timestamp | P0 | + +--- + +### Section 2: π° Earning β Pipeline Stats + Co-Op Members + Investors + +#### 2.1 Pipeline Stats + +| FR# | Requirement | Priority | +|:---:|:------------|:--------:| +| FR-15 | Display **Ships in Flight** β count of active projects (e.g., PRJ-429 W29 #FedArch FOCUS) | P0 | +| FR-16 | Display **Sub-Projects Complete / 14 Total** β progress bar with fraction | P0 | +| FR-17 | Display **P0 Blockers** β real-time count with severity indicator | P0 | +| FR-18 | Display **PRDs Drafted** and **PRDs In Progress** β count | P1 | +| FR-19 | Display **W29 Milestone Progress** β percentage or status | P1 | +| FR-20 | Each pipeline metric should be clickable to reveal more detail (tooltip or expand) | P2 | + +#### 2.2 Co-op Members + Investors + +| FR# | Requirement | Priority | +|:---:|:------------|:--------:| +| FR-21 | Display **Active (.OCA) Members** β count | P0 | +| FR-22 | Display **Pre-Launch Signups** β count | P0 | +| FR-23 | Display **Investor Pipeline** β count of prospects in funnel | P1 | +| FR-24 | Display **#SoundOfMoney Raised** β total revenue figure ($) | P0 | +| FR-25 | Display **F1VISA-001 Pre-Orders** β count of book pre-orders | P1 | +| FR-26 | Show trend indicators (βββ) per metric | P1 | + +--- + +### Section 3: π Learning β Lessons Counter + PRD Tracker + Skills + +| FR# | Requirement | Priority | +|:---:|:------------|:--------:| +| FR-27 | Display **Lessons Counter** β total number of L-XXX lessons codified | P0 | +| FR-28 | Display **PRD Tracker** β count of PRDs completed this season | P1 | +| FR-29 | Display **Skills Development** β training modules completed, members trained | P1 | +| FR-30 | Include link/reference to #YonksPromptingAcademy | P2 | +| FR-31 | Show "New This Week" highlight for recent lessons | P2 | + +--- + +### Section 4: π«Ά Giving β Open Source + Community + Ecosystem + +| FR# | Requirement | Priority | +|:---:|:------------|:--------:| +| FR-32 | Display **Open Source Contributions** β count of repos, PRs, contributors | P1 | +| FR-33 | Display **Community Growth** β total community members across all channels | P1 | +| FR-34 | Display **Ecosystem Support** β instances deployed, members onboarded | P1 | +| FR-35 | Link to GitHub org (CCCbotNet, WeOwnNet) | P1 | + +--- + +### Section 5: Global Requirements (All Sections) + +| FR# | Requirement | Priority | +|:---:|:------------|:--------:| +| FR-36 | **Header**: "π FELG.WeOwn.Net β βΎοΈ WeOwnNet π Ecosystem Dashboard" | P0 | +| FR-37 | **Subheader**: "π Fun Β· π° Earning Β· π Learning Β· π«Ά Giving" | P0 | +| FR-38 | **Footer**: MIT License + "Open source. Open culture. Open cooperative." | P0 | +| FR-39 | **Last Updated** timestamp β visible on every section | P0 | +| FR-40 | **FELG order INVARIANT** β Fun section is ALWAYS first, Giving ALWAYS last | π IMMUTABLE | +| FR-41 | **Responsive design** β works on desktop, tablet, mobile, and live-stream screenshare | P0 | +| FR-42 | **Lightweight** β loads fast, no heavy animations that lag on stream | P1 | +| FR-43 | **Dark mode as default** (matches WeOwnNet brand aesthetic) | P1 | +| FR-44 | **No auth required** for viewing β fully public | π IMMUTABLE | +| FR-45 | **Data refresh** β manual refresh button as an explicit fallback re-sync; primary updates arrive via realtime push (see FR-65) | P1 | +| FR-65 | **Realtime data delivery** β dashboard subscribes to a Supabase Realtime channel and updates in place when underlying data changes. No client polling loop. | P0 π₯ | +| FR-66 | **Public read-only data access** β anon Supabase role has SELECT-only access via Row Level Security. All writes go through the reviewed manual-entry pipeline (edit JSON β PR β merge β sync to Supabase), never a client-exposed write path. | π IMMUTABLE | +| FR-67 | **Real, indexable per-brand URLs** β each brand has an actual route (e.g. `/brand/f1visa/`), not a hash fragment, statically generated with its own `` and Open Graph tags. Deep links (FR-52) resolve to these routes. | P1 | + +--- + +## π Brand Detail View + +### 6.1 Access Pattern + +``` +Brand Card (any) + β + βββ Click β Opens Brand Detail View + β + βββ On Desktop: Slide-out panel (right side) OR modal overlay + βββ On Tablet/Mobile: Full-screen modal or dedicated route + βββ Navigation: Back/Close returns to dashboard main view +``` + +### 6.2 Detail View Layout + +``` +ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ +β [β Back to Dashboard] [β Close] β +ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ +β β +β π F1Visa.Net π’ Live β +β βΎοΈ Pre-launch phase β .OCA cooperative validation β +β β +ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ +β β +β π Key Metrics β +β ββββββββββββββ¬βββββββββββββ¬βββββββββββββ¬βββββββββββββ β +β βPre-Launch β Waitlist β PRD β Milestone β β +β βSignups β Position β Complete β Progress β β +β β 89 β #42 β 85% β 60% β β +β ββββββββββββββ΄βββββββββββββ΄βββββββββββββ΄βββββββββββββ β +β β +β π Milestones β +β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β +β β β PRD Drafted β 2026-07-01 β β +β β β Landing Page Live β 2026-07-08 β β +β β π‘ Beta Launch β Target: 2026-08-01 β β +β β β¬ Public Launch β Target: 2026-09-01 β β +β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β +β β +β π Links β +β β π Website: f1visa.net β +β β π PRD: link-to-prd β +β β π¬ Channel: #f1visa β +β β π€ Lead: @GTM π― β +β β +ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ +β Last Updated: 2026-07-13 14:30 MDT β +ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ +``` + +### 6.3 Brand Detail β Data Requirements + +| Field | Type | Example | Required | +|:------|:-----|:--------|:--------:| +| Brand ID | string | `f1visa` | β | +| Brand Name | string | `F1Visa.Net` | β | +| Emoji / Icon | string | `π` | β | +| Status | enum | `live` / `in_progress` / `not_started` / `paused` | β | +| Tagline | string | `Pre-launch phase β .OCA cooperative validation` | β | +| Status History | array | `[{date: "2026-07-01", status: "in_progress"}]` | P2 | +| Key Metrics | object | `{signups: 89, waitlist_position: 42}` | β | +| Milestones | array | `[{label: "PRD Drafted", status: "done", date: "2026-07-01"}]` | β | +| Links | object | `{website, prd, channel, repo}` | β | +| Lead / Owner | string | `@GTM π―` | β | +| Category | string | `ecosystem_brand` / `infrastructure` / `initiative` | P1 | +| Description | string | Full text description of brand | P1 | +| Last Updated | timestamp | ISO 8601 | β | + +### 6.4 Interaction Requirements + +| FR# | Requirement | Priority | +|:---:|:------------|:--------:| +| FR-49 | Click on brand card opens detail view | P0 | +| FR-50 | Close/Back button returns to dashboard without losing scroll position | P1 | +| FR-51 | Keyboard navigation: ESC to close detail view | P1 | +| FR-52 | Detail view is shareable via URL (deep linking to brand) | P2 | +| FR-53 | Detail view renders on mobile as a full-screen overlay | P1 | + +--- + +## π All Brands View + +### 7.1 Access Pattern + +``` +Default Dashboard + β + βββ Shows: Top 5 ecosystem brands (prioritized by status/impact) + β + βββ [Show More / View All] button at bottom of brands section + β + βββ ON CLICK: + β βββ Inline expansion: Brands section expands to show full list + β βββ OR: Opens a full-page "All Brands" view + β + βββ Close/Collapse: [Show Less] or Back button +``` + +### 7.2 All Brands View β Layout + +``` +ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ +β π All Ecosystem Brands [Show Less]β +β β +β π΄ Active (3) β +β ββββββββ ββββββββ ββββββββ β +β βF1Visaβ βWeOwn β βWeOwn β β +β β.Net β β.Chat β β.Dev β β +β βπ’Liveβ βπ’Liveβ βπ’Liveβ β +β ββββββββ ββββββββ ββββββββ β +β β +β π‘ In Progress (3) β +β ββββββββ ββββββββ ββββββββ β +β βBurnedβ βExit β β[Next]β β +β βOut β βTo β βBrand β β +β βπ‘WIP β βClientβ βπ‘WIP β β +β ββββββββ ββββββββ ββββββββ β +β β +β β¬ Coming Soon (2) β +β ββββββββ ββββββββ β +β β[TBD] β β[TBD] β β +β βBrand β βBrand β β +β ββ¬ β ββ¬ β β +β ββββββββ ββββββββ β +ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ +``` + +### 7.3 All Brands Requirements + +| FR# | Requirement | Priority | +|:---:|:------------|:--------:| +| FR-54 | Brands are grouped by status (Active / In Progress / Coming Soon / Paused) | P0 | +| FR-55 | Each brand card in All Brands view is clickable β opens Brand Detail View | P0 | +| FR-56 | All Brands view shows brand name, emoji, status indicator, and primary metric | P0 | +| FR-57 | COUNT displayed per status group header (e.g., "Active (3)") | P1 | +| FR-58 | Search/filter bar in All Brands view for 10+ brands | P2 | +| FR-59 | Sort options: by status, by name, by last updated | P2 | +| FR-60 | Full brand registry is maintained in a single JSON array β Top 5 is a subset | P1 | + +--- + +## βοΈ Non-Functional Requirements + +| NFR# | Requirement | Target | Priority | +|:----:|:------------|:-------|:--------:| +| NFR-01 | **Page Load Time** | < 3 seconds on first load | P0 | +| NFR-02 | **Time to Interactive** | < 2 seconds | P0 | +| NFR-03 | **Uptime** | 99.5% (public facing) | P1 | +| NFR-04 | **Mobile Support** | Responsive down to 320px width | P0 | +| NFR-05 | **Browser Support** | Latest 2 versions of Chrome, Firefox, Safari, Edge | P1 | +| NFR-06 | **Data Freshness** | Transport is realtime for every section (FR-65) β a saved edit reaches every open browser within seconds. Underlying data freshness is bounded by entry cadence: manual entry (Phase 1) is human-paced; Postiz/SendGrid/GitHub API sourcing (Phase 2) tightens specific sections to hourly/daily. | P1 | +| NFR-07 | **Accessibility** | WCAG 2.1 AA minimum | P2 | +| NFR-08 | **SEO** | Meta tags, Open Graph, social preview card | P2 | +| NFR-09 | **Security** | No auth endpoints exposed. Read-only data layer. | P0 | +| NFR-10 | **Backup** | Data source redundancy for critical metrics | P2 | + +--- + +## π οΈ Tech Stack + +| Layer | Technology | Status | Notes | +|:------|:-----------|:------:|:------| +| **Domain** | `FELG.WeOwn.Net` | π‘ PENDING | DNS record to be configured | +| **Repo** | `github.com/WeOwnNet/FELG` | π’ LIVE | Source of truth for code | +| **Dashboard Frontend** | **Astro** (static-site framework, islands architecture) + **Tailwind CSS** | π’ **LOCKED for MVP** | See Amendment A3 β supersedes the vanilla HTML/CSS/JS decision | +| **Realtime Backend** | Supabase (Postgres + Realtime) | π’ **LOCKED for MVP** | Unchanged by Amendment A3 β see Amendment A2 | +| **Data Sources** | Manual entry (JSON β synced to Supabase). Postiz API, SendGrid API remain Phase 2. | π‘ PENDING (Phase 2 portion) | See Data Sources & Integration Β§, Phasing Β§ | +| **Auth** | None for viewing. Supabase RLS: anon role SELECT-only (FR-66). | π’ PLANNED | No login required; writes never client-exposed. The product is explicitly an **open web app β no signup, ever**, for viewing. | +| **Display** | Every #DailySync #LiveStream | π’ PLANNED | Always-on during livestreams | +| **Hosting** | TBD (GitHub Pages recommended; Astro's static output deploys there natively) | π‘ PENDING | Supabase hosts the backend regardless of frontend host choice | + +### Framework Decision β AMENDED (Amendment A3, supersedes A2's frontend guidance) + +> **π SUPERSEDES v1.2.0-r1:** Amendment A2 locked "static frontend, no framework" as the MVP frontend approach. Per Amendment A3, the frontend is now **Astro + Tailwind CSS** instead of hand-authored vanilla HTML/CSS/JS. This does **not** touch the backend decision β Supabase + Realtime (Amendment A2) is unchanged. Astro's *build output* is still static files; this is a change in **authoring tooling**, not a reversal of "no unnecessary backend server." + +| Option | Pros | Cons | Recommendation | +|:-------|:-----|:-----|:--------------| +| **Astro + Tailwind CSS (static output) + Supabase** | Real per-brand URL routes (`/brand/f1visa/`) instead of hash routing β directly strengthens FR-52 and NFR-08 (SEO/OG tags per page). Islands architecture ships minimal JS by default, which helps NFR-01/02. Component-based structure maps cleanly onto the four FELG sections. | Introduces a build step (`astro build`) and `npm`/Vite tooling where the prior plan had none. Tailwind's utility classes are a departure from the hand-rolled design-token CSS already drafted (Β§ User Interface Requirements) β token *values* carry over unchanged, only their expression changes. | π **Recommended for MVP (Amendment A3)** | +| **Vanilla HTML/CSS/JS, no framework** | Zero build tooling | Cannot give brand detail views real URLs without hand-rolling a router; SEO/OG tags would need per-page duplication by hand. This was the v1.2.0-r1 MVP scope, now superseded. | Rejected β Astro achieves the same runtime simplicity (still static output) with better routing/SEO for free | +| **Next.js / SvelteKit / other SSR meta-framework** | Full SSR, API routes | Unnecessary β nothing in this product needs server-side rendering per request; Supabase already provides the only "backend" this product needs | Not needed at any phase currently scoped | + +> **@PAT Recommendation (Amendment A3):** MVP ships as an **Astro + Tailwind CSS static site**, deployed as an **open web app β public, no signup required for viewing, ever** β backed by Supabase Postgres + Realtime per Amendment A2. Data entry stays manual (JSON β PR β merge β sync to Supabase) for MVP. Brand detail views become real Astro routes rather than hash-based fake routes. Design token *values* (colors, spacing, type scale) from the User Interface Requirements Β§ carry over unchanged into `tailwind.config` β Amendment A3 changes *how* those tokens are expressed, not what they are. + +--- + +## π Data Sources & Integration + +| Data Type | Source | Integration Method | Refresh Cadence | Priority | +|:----------|:-------|:-------------------|:---------------:|:--------:| +| **Social Media** | Postiz API | API call β data transform | Hourly | P1 | +| **Email List** | SendGrid API | API call β subscriber count + open rate | Daily | P1 | +| **Pipeline Stats** | Manual / PRJ docs | Manual update via PR or UI | Weekly (per #DailySync) | P0 | +| **Brand Status** | Manual / Core TEAM | Manual update | Weekly | P0 | +| **Co-op Members** | Manual / Onboarding system | Manual update | Weekly | P0 | +| **#SoundOfMoney Raised** | Matomo.WeOwn.app / Stripe | API or manual | Per event | P1 | +| **Lessons Counter** | GitHub / FedArch docs | GitHub API β count L-XXX files | Weekly | P1 | +| **Open Source** | GitHub API | API β repo count, PR count, contributor count | Weekly | P2 | + +### Data Integration Phasing β AMENDED (Amendment A2) + +``` +Phase 1 (MVP): Manual data entry + REALTIME DELIVERY + β Data is hand-edited in data/dashboard.json in the repo + β PR reviewed + merged (human-authored, human-reviewed β unchanged from v1.1.0-r1) + β GitHub Action syncs the merged JSON into a Supabase table row + β Supabase Realtime broadcasts the change to every open browser (FR-65) + β Dashboard frontend (static, no framework) subscribes via supabase-js β no polling + β Dashboard deploys to FELG.WeOwn.Net via GitHub Pages + +Phase 2 (Enhanced): API integration + β Postiz, SendGrid, GitHub APIs ingested automatically + β Same sync path as Phase 1 β API pollers write into the same Supabase table + β No change to the realtime delivery layer; only the data's origin changes + +Phase 3 (Enhanced): Analytics & polish + β Historical data / trend charts + β Animated #SoundOfMoney live-counter treatment + β Preference registry + CHACHING!! integration (PRJ-401 alignment) +``` + +> **π What moved and what didn't:** Supabase + Realtime (previously Phase 3, M3.1βM3.2) is now Phase 1 MVP β that's the delivery mechanism. Postiz/SendGrid/GitHub API-sourced data ingestion (previously Phase 2, M2.1βM2.3) **stays Phase 2** β that's the data's origin. Manual entry remains the MVP data source; only how fast an edit reaches a viewer's screen changed. + +--- + +## π¨ User Interface Requirements + +### Layout + +``` +ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ +β π FELG.WeOwn.Net β βΎοΈ WeOwnNet π β +β Ecosystem Dashboard β +β π Fun Β· π° Earning Β· π Learning Β· π«Ά Giving β +ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ +β β +β π Top 5 Ecosystem Brands (5 of 8) β +β ββββββββ ββββββββ ββββββββ ββββββββ ββββββββ β +β βF1Visaβ βBurnedβ βExit β βWeOwn β βWeOwn β β +β β.Net β βOut β βTo β β.Chat β β.Dev β β +β βπ’Liveβ βπ‘WIP β βπ‘WIP β βπ’Liveβ βπ’Liveβ β +β β 89 π€β β 12 πβ β 3 π€ β β120 π¬β β 14 π»β β +β ββββββββ ββββββββ ββββββββ ββββββββ ββββββββ β +β ββββββββββββββββββββββββββββββββββββββββββββββββββ β +β β πΊ Show All Brands (8) β β β +β ββββββββββββββββββββββββββββββββββββββββββββββββββ β +β β +β π± Social Media (auto-refreshed) β +β πΉ 1.2K π¦ 3.4K πΌ 890 π± 2.1K π¬ 450 π§ 1.8K β +β β +ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ +β β +β π° Section 2: Pipeline + Members + Investors β +β β +β π’ Ships: 7 β Complete: 9/14 π΄ Blocker: 2 β +β π PRDs: 5/8 π― W29: 65% β +β β +β π₯ Active: 24 π Signups: 89 β +β ποΈ Investors: 5 π° Raised: $44,200 β +β π Pre-orders: 12 β +β β +ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ +β β +β π Section 3: Learning β +β π Lessons: 156 π PRDs Complete: 8 β +β π Skills: 6 modules Β· 18 members trained β +β β +ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ +β β +β π«Ά Section 4: Giving β +β π¦ Open Source: 14 repos Β· 47 PRs Β· 9 contributors β +β π₯ Community: 380 total across all channels β +β π± Ecosystem: 12 instances Β· 24 members onboarded β +β β +ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ +β Last Updated: 2026-07-13 14:30 MDT β MIT License β +β π Refresh β +ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ +``` + +### Visual Design Principles + +| Principle | Application | +|:----------|:------------| +| **Dark theme** | Deep background (#0a0a1a), text (#e0e0ff), accents per FELG pillar | +| **Pillar colors** | π Fun: #FF6B6B (coral), π° Earning: #FFD700 (gold), π Learning: #4ECDC4 (teal), π«Ά Giving: #95E1D3 (mint) | +| **Emoji-first** | Use emoji as icons β no heavy SVG dependencies for MVP | +| **Cards** | Brand cards with rounded corners, subtle glow on hover | +| **Progress bars** | Clean, thin bars for completion metrics | +| **Status dots** | π΄ π‘ π’ for brand/pipeline status | +| **Responsive grid** | CSS Grid β 5 cards on desktop, 2-3 on tablet, 1 on mobile | +| **Font** | System font stack β no heavy font loads | + +> **π NOTE:** Full visual design (wireframes, mockups, component library) is the responsibility of **@PAT π¨** as Design & UX lead. + +--- + +## π Success Metrics & KPIs + +| KPI | Target | Measurement Method | Priority | +|:----|:------|:-------------------|:--------:| +| **Dashboard uptime** | 99.5% | Hosting provider status | P1 | +| **Data accuracy** | 100% for manual entries, Β±5% for API data | Manual audit per #DailySync | P0 | +| **Page load time** | < 3 seconds | Lighthouse / Web Vitals | P0 | +| **#DailySync usage** | Displayed at every sync | Self-reported | P1 | +| **Public visits** | TBD β baseline after launch | Postiz or Matomo analytics | P2 | +| **Brand status accuracy** | All brands reflect current state | Core TEAM validation per sync | P0 | + +--- + +## π« Out of Scope (v1.3.0) + +> **Amendment A2 change:** "Real-time WebSocket updates" is removed from this table β realtime delivery via Supabase is now MVP scope (FR-65). See Tech Stack Β§ and Phasing & Milestones Β§ for the corrected boundary. + +| Item | Reason | +|:-----|:-------| +| **User authentication / login** | Dashboard is PUBLIC by design. Auth is never needed for viewing. | +| **FELG Score / Contribution Score** | NOT in the source of truth. The dashboard tracks metrics, not calculated scores. | +| **CHACHING!! Live Feed** | #SoundOfMoney notification is a SEPARATE system (PRJ-401). Not part of FELG dashboard. | +| **Per-member contribution tracking** | Not specified in the FELG README. Out of scope for v1. | +| **Admin panel / data entry UI** | MVP data entry is manual (JSON files in repo, PR-reviewed). A UI for non-technical entry is a later phase. | +| **Mobile app** | Responsive web covers mobile needs for MVP. | +| **Dark/light mode toggle** | Dark mode is the single default. Light mode is not requested. | +| **Multi-language support** | English only for MVP. | + +--- + +## ποΈ Phasing & Milestones + +### Phase 1: MVP (W29-W30 β July 2026) β AMENDED (Amendment A2) + +| Milestone | Deliverable | Owner | Status | +|:----------|:------------|:------|:------:| +| M1.1 | Framework decision β Static frontend + Supabase backend (Amendment A2, supersedes original "Static vs Next.js vs Supabase" decision) | @PAT + @SHD + @MWK | β¬ PENDING | +| M1.2 | Design mockups for all sections + responsive layout | @PAT π¨ | β¬ PENDING | +| M1.3 | Static HTML/CSS/JS prototype (hardcoded data) | @PAT + @SHD | β¬ PENDING | +| M1.4 | FELG.WeOwn.Net domain configured | @SHD + @MWK | β¬ PENDING | +| M1.5 | Data JSON schema defined + sample data commit | @PAT | β¬ PENDING | +| M1.6 | Supabase project provisioned + table + Row Level Security (anon SELECT-only, FR-66) | @SHD + @MWK | β¬ PENDING | +| M1.7 | GitHub Action: sync merged `dashboard.json` β Supabase table on merge | @SHD + @MWK | β¬ PENDING | +| M1.8 | Frontend subscribes to Supabase Realtime channel β live push confirmed end-to-end (FR-65) | @MWK + @PLT | β¬ PENDING | +| M1.9 | MVP deployed to FELG.WeOwn.Net | @SHD + @MWK | β¬ PENDING | +| M1.10 | First #DailySync with live, realtime-updating dashboard | Core TEAM | β¬ PENDING | + +### Phase 2: API Integration (W31-W33 β August 2026) + +Unchanged from v1.1.0-r1 β this phase is about *where data comes from*, not *how fast it reaches the screen* (that's now solved in Phase 1). Each of these writes into the same Supabase table Phase 1 already established. + +| Milestone | Deliverable | Owner | +|:----------|:------------|:------| +| M2.1 | Postiz API integration β auto social metrics | @MWK + @PLT | +| M2.2 | SendGrid API integration β auto email metrics | @MWK + @PLT | +| M2.3 | GitHub API integration β auto repo/PR stats | @MWK + @PLT | +| M2.4 | Automated data refresh pipeline (GitHub Actions, scheduled pollers β Supabase) | @SHD + @MWK | + +### Phase 3: Analytics & Polish (W34+ β September 2026+) + +| Milestone | Deliverable | Owner | +|:----------|:------------|:------| +| M3.1 | Historical data / trend charts | @PAT + @MWK | +| M3.2 | #SoundOfMoney raised β animated live-counter treatment (value itself already updates live via Phase 1 realtime; this milestone is the celebratory count-up/pulse UX) | @GTM + @MWK | + +--- + +## π₯ Who Builds It + +Per the FELG repo source of truth: + +| Role | Person | Focus | +|:-----|:-------|:------| +| π― **Vision & Strategy** | βΎοΈ WeOwnNet π Core TEAM | What the dashboard shows and why | +| π¨ **Design & UX** | **@PAT π¨** | How it looks, feels, and communicates FELG | +| π» **Infrastructure** | @SHD π΅π° + @MWK πΊ + @PLT π― | Domain, hosting, data pipeline | +| π **Data** | @GTM π― + @THY π + @MWK πΊ + @PLT π― | Metrics sources, automation, refresh cadence | + +--- + +## β οΈ Risks & Mitigations + +| # | Risk | Likelihood | Impact | Mitigation | +|:-:|:-----|:----------:|:------:|:-----------| +| 1 | **Domain (FELG.WeOwn.Net) not active** | π Medium | π΄ High | Deploy to GitHub Pages or subdomain as fallback | +| 2 | **Framework decision stalls** | π‘ Low | π Medium | Default to static frontend + Supabase backend per Amendment A2 | +| 3 | **API access/keys not ready** | π Medium | π Medium | MVP data ingestion can stay manual. Postiz/SendGrid/GitHub APIs are still Phase 2 β unaffected by Amendment A2. | +| 4 | **Data freshness** | π‘ Low | π‘ Medium | Realtime transport (FR-65) removes the old polling-interval staleness window; freshness is now bounded only by how often a human edits the data. | +| 5 | **Design scope creep** | π Medium | π‘ Medium | Hold the line: MVP = static cards + manual data + realtime delivery. Do not pull Phase 2/3 items forward beyond Amendment A2's scope. | +| 6 | **No R-011 approval** | π‘ Low | π‘ Medium | Submit for review immediately. | +| 7 | **Supabase project misconfiguration exposes write access** | π‘ Low | π΄ High | RLS must be verified (anon = SELECT-only) before FELG.WeOwn.Net goes public, per FR-66. Treat this as a launch blocker, not a follow-up. | +| 8 | **Realtime channel drops mid-livestream** | π‘ Low | π Medium | Manual refresh button (FR-45) stays as a visible fallback; frontend should indicate connection state rather than silently going stale on screen. | +| 9 | **Astro build step fails or misconfigures GitHub Pages base path** | π‘ Low | π Medium | Astro has first-party GitHub Pages deploy support; verify `site`/`base` config against the final hosting decision (Β§ Tech Stack, open question) before first deploy. | +| 10 | **Tailwind theme drifts from the design tokens already specified** | π‘ Low | π‘ Medium | `tailwind.config` must source its color values directly from the User Interface Requirements Β§ pillar-color table β treat that table as the single source of truth, not the Tailwind config. | + +--- + +## π Related Documents + +| Document | Version | URL | Relationship | +|:---------|:-------:|:----|:-------------| +| **FELG README** | β | https://github.com/WeOwnNet/FELG | Source of truth for this PRD | +| **D-070 FELG Culture** | v3.2.3.1 | https://github.com/CCCbotNet/fedarch/blob/main/_GOVERNANCE_/D-070_FELG_Culture.md | Cultural governance | +| **PRJ-401** | v4.1.3.1-r14 | https://raw.githubusercontent.com/CCCbotNet/fedarch/main/_S004_PROJECTS_/PRJ-401.md | #SoundOfMoney pipeline, #ZeroTo100 context | +| **PRJ-038 (#ZeroTo100)** | β | Conceptual North Star | Revenue buckets framework | +| **PRJ-F1V** | v1.0.0-r1 | F1Visa.Net PRD | Sister project in ecosystem | +| **SharedKernel** | v3.2.2.1 | https://raw.githubusercontent.com/CCCbotNet/fedarch/main/_SYS_/SharedKernel.md | Ecosystem governance rules | +| **WS-PAT-001** | v4.2.3.1-r2 | Workspace prompt for @PAT | Design authority for this PRD | + +--- + +## π Appendix A: The 8 Immutable FELG Rules (from D-070) + +| # | Rule | Dashboard Implication | +|:-:|:-----|:----------------------| +| 1 | FELG is ALWAYS π Fun, π° Earning, π Learning, π«Ά Giving β in THAT order | Layout is INVARIANT. Fun section always first. Giving always last. | +| 2 | FELG is the **CULTURE**, not just metrics on a dashboard | The dashboard is an EXPRESSION of culture, not a replacement for it. | +| 3 | FELG applies to EVERY member, EVERY agent, EVERY interaction | Dashboard is PUBLIC. No auth. Everyone sees the full picture. | +| 4 | #FELG beats #HustleCulture β always | Celebrate wins. No burnout metrics. No "hours worked" or "grind" counters. | +| 5 | Fun is FIRST β if it's not fun, fix it | If the dashboard feels like work, redesign it. | +| 6 | Lessons are GIFTS β share them openly | Lessons Counter is visible to everyone. No gatekeeping. | +| 7 | Giving means LIFTING β open source, mentorship, ecosystem growth | Open Source Contributions section tracks how the ecosystem gives back. | +| 8 | FELG is FOREVER β codified in D-070 as the βΎοΈ Culture Standard | The dashboard is not a seasonal experiment. It's the permanent heartbeat. | + +--- + +## π Appendix B: Data Schema Draft (MVP β JSON) + +```json +{ + "dashboard": { + "name": "FELG Ecosystem Dashboard", + "domain": "FELG.WeOwn.Net", + "last_updated": "2026-07-13T14:30:00-06:00", + "version": "1.1.0" + }, + "brands": { + "top_5_ids": ["f1visa", "burnedout", "exittoclient", "weownchat", "weowndev"], + "all_brands": [ + { + "id": "f1visa", + "name": "F1Visa.Net", + "emoji": "π", + "status": "live", + "tagline": "Pre-launch phase β .OCA cooperative validation", + "category": "ecosystem_brand", + "lead": "@GTM π―", + "metric_label": "Pre-launch Signups", + "metric_value": 89, + "milestones": [ + { "label": "PRD Drafted", "status": "done", "date": "2026-07-01" }, + { "label": "Landing Page Live", "status": "done", "date": "2026-07-08" }, + { "label": "Beta Launch", "status": "in_progress", "target": "2026-08-01" }, + { "label": "Public Launch", "status": "pending", "target": "2026-09-01" } + ], + "links": { + "website": "https://f1visa.net", + "prd": "https://github.com/...", + "channel": "#f1visa", + "repo": "https://github.com/WeOwnNet/F1VISA" + }, + "last_updated": "2026-07-13T14:30:00-06:00" + }, + { + "id": "burnedout", + "name": "Burned Out Media", + "emoji": "π₯", + "status": "in_progress", + "tagline": "#AttractionMarketing content engine", + "category": "ecosystem_brand", + "lead": "@THY π", + "metric_label": "Pieces Published", + "metric_value": 12, + "milestones": [ + { "label": "Content Strategy Complete", "status": "done", "date": "2026-06-15" }, + { "label": "First 10 Pieces Published", "status": "done", "date": "2026-07-10" }, + { "label": "Client Pipeline Active", "status": "in_progress", "target": "2026-08-01" } + ], + "links": { + "website": "https://burnedout.xyz", + "channel": "#burnedout" + }, + "last_updated": "2026-07-13T14:30:00-06:00" + }, + { + "id": "exittoclient", + "name": "Exit To Client", + "emoji": "π€", + "status": "in_progress", + "tagline": "Service offerings for client acquisitions", + "category": "ecosystem_brand", + "lead": "@GTM π―", + "metric_label": "Client Pipeline", + "metric_value": 3, + "milestones": [ + { "label": "Service Menu Defined", "status": "done", "date": "2026-06-20" }, + { "label": "First 3 Clients in Pipeline", "status": "done", "date": "2026-07-05" }, + { "label": "First Client Signed", "status": "pending", "target": "2026-08-15" } + ], + "links": { "channel": "#exittoclient" }, + "last_updated": "2026-07-13T14:30:00-06:00" + }, + { + "id": "weownchat", + "name": "βΎοΈ WeOwn.Chat", + "emoji": "π¬", + "status": "live", + "tagline": "Community communication hub", + "category": "ecosystem_brand", + "lead": "@SHD π΅π°", + "metric_label": "Community Members", + "metric_value": 120, + "milestones": [ + { "label": "Domain Active", "status": "done", "date": "2026-06-01" }, + { "label": "First 100 Members", "status": "done", "date": "2026-07-01" }, + { "label": "Channels Launched", "status": "done", "date": "2026-07-01" } + ], + "links": { + "website": "https://weown.chat", + "repo": "https://github.com/WeOwnNet/WeOwnChat" + }, + "last_updated": "2026-07-13T14:30:00-06:00" + }, + { + "id": "weowndev", + "name": "βΎοΈ WeOwn.Dev", + "emoji": "π»", + "status": "live", + "tagline": "Infrastructure and development hub", + "category": "infrastructure", + "lead": "@MWK πΊ", + "metric_label": "Active Repos", + "metric_value": 14, + "milestones": [ + { "label": "Claw Instances Deployed", "status": "done", "date": "2026-06-15" }, + { "label": "CI/CD Pipeline Active", "status": "done", "date": "2026-07-01" }, + { "label": "Infra Monitoring Live", "status": "in_progress", "target": "2026-07-20" } + ], + "links": { + "website": "https://weown.dev", + "repo": "https://github.com/WeOwnNet" + }, + "last_updated": "2026-07-13T14:30:00-06:00" + } + ] + }, + "fun": { + "social_media": { + "youtube": { "subscribers": 1200, "views": 45000, "watch_time_hours": 2800 }, + "twitter": { "followers": 3400, "impressions_monthly": 125000, "engagement_rate": 4.2 }, + "linkedin": { "connections": 890, "post_reach_monthly": 45000 }, + "instagram": { "followers": 2100, "story_views_avg": 320 }, + "signal_telegram": { "members": 450 }, + "email": { "subscribers": 1800, "open_rate": 38.5, "click_rate": 12.1 } + } + }, + "earning": { + "pipeline": { + "ships_in_flight": 7, + "sub_projects_complete": 9, + "sub_projects_total": 14, + "p0_blockers": 2, + "prds_drafted": 5, + "prds_in_progress": 3, + "w29_milestone_percent": 65 + }, + "members_investors": { + "active_oca_members": 24, + "pre_launch_signups": 89, + "investor_pipeline": 5, + "sound_of_money_raised_usd": 44200, + "f1visa001_pre_orders": 12 + } + }, + "learning": { + "lessons_counter": 156, + "prds_completed": 8, + "skills_modules": 6, + "members_trained": 18, + "new_this_week": 3 + }, + "giving": { + "open_source": { + "repos": 14, + "pull_requests": 47, + "contributors": 9 + }, + "community": { + "total_members_all_channels": 380 + }, + "ecosystem": { + "instances_deployed": 12, + "members_onboarded": 24 + } + } +} +``` + +> **π NOTE:** This schema is a RECOMMENDED starting point. Final schema to be determined during implementation (Phase 1, Milestone M1.5). +> +> **π Amendment A2:** This JSON shape is unchanged by the move to realtime delivery. In the amended Phase 1 architecture, this object is stored as the value of a single JSONB column in one Supabase table row (not a normalized multi-table schema) β the GitHub Action that syncs a merged `dashboard.json` writes this exact object wholesale, and Supabase Realtime broadcasts the row's `UPDATE` event to subscribed clients. Normalizing into per-domain tables is an optimization for a later phase, not an MVP requirement. + +--- + +## π BP-075: Self-Verifying Footer + +| Field | Value | +|:------|:-------| +| **Document ID** | PRJ-FELG.md | +| **Version** | **v1.3.0-r1** (v1.2.0-r1 + A3 consolidated) | +| **Date** | 2026-07-13 β W29 D1 (Monday), amended per Amendment A2, A3 | +| **Agent Author** | AI:@PAT π¨ @ INTβP05:CCC | +| **CCC-ID** | PAT_2026-W29_1035 | +| **#masterCCC** | PAT_2026-W29_1035 π | +| **Source of Truth** | https://github.com/WeOwnNet/FELG | +| **Culture Governance** | D-070 FELG Culture | +| **Instance** | INT-P05 (PRO.WeOwn.Tools) | +| **Product Owner** | @PAT π¨ (Osinachi Patrick β SinachPat βΎοΈ) | +| **Status** | π‘ **DRAFT β AWAITING R-011 APPROVAL** | +| **Total Sections** | 21 | +| **Functional Requirements** | 67 (FR-01 through FR-67) | +| **Non-Functional Requirements** | 10 (NFR-01 through NFR-10) | +| **Phases** | 3 (MVP w/ Realtime, API Integration, Analytics & Polish) | +| **Milestones** | 16 (M1.1βM1.10, M2.1βM2.4, M3.1βM3.2) | +| **Amendment A2 Summary** | Realtime delivery (Supabase + Realtime, formerly Phase 3 M3.1βM3.2) elevated to Phase 1 MVP. API-sourced data ingestion (Postiz/SendGrid/GitHub, Phase 2) unchanged. | +| **Amendment A3 Summary** | Frontend authoring tool changed from vanilla HTML/CSS/JS to **Astro + Tailwind CSS**. Product reframed as an open, public web app (no signup, ever). Adds FR-67 (real per-brand URL routes). Backend (Supabase + Realtime, Amendment A2) unchanged. | +| **Fabrications** | **0** β | + +--- + +```text +βββ END OF PRD β PRJ-FELG v1.3.0-r1 βββ +``` + +#FlowsBros #FedArch #WeOwnSeason004 #FELG #PRD #Dashboard #ProductRequirements #v1.3.0 #AmendmentA2 #AmendmentA3 #RealtimeMVP #Supabase #Astro #TailwindCSS #OpenWebApp #BrandDetail #AllBrands #ShowMore #FR46 #FR47 #FR65 #FR66 #FR67 #Fun #Earning #Learning #Giving #OCA #PublicDashboard #DailySync #LiveStream #PRJ_FELG #PAT_Product + +βΎοΈ WeOwnNet π β π‘ Real Estate and π€ cooperative ownership for everyone β An π€ inclusive community, by π₯ invitation only. \ No newline at end of file diff --git a/astro.config.mjs b/astro.config.mjs new file mode 100644 index 0000000..81fcc39 --- /dev/null +++ b/astro.config.mjs @@ -0,0 +1,15 @@ +// @ts-check +import { defineConfig } from 'astro/config'; + +import tailwindcss from '@tailwindcss/vite'; + +// https://astro.build/config +export default defineConfig({ + output: 'static', + site: 'https://WeOwnNet.github.io', + base: '/FELG', + + vite: { + plugins: [tailwindcss()] + } +}); \ No newline at end of file diff --git a/data/dashboard.json b/data/dashboard.json new file mode 100644 index 0000000..ee33d5f --- /dev/null +++ b/data/dashboard.json @@ -0,0 +1,167 @@ +{ + "dashboard": { + "name": "FELG Ecosystem Dashboard", + "domain": "FELG.WeOwn.Net", + "last_updated": "2026-07-13T14:30:00-06:00", + "version": "1.1.0" + }, + "brands": { + "top_5_ids": ["f1visa", "burnedout", "exittoclient", "weownchat", "weowndev"], + "all_brands": [ + { + "id": "f1visa", + "name": "F1Visa.Net", + "emoji": "π", + "status": "live", + "tagline": "Pre-launch phase β .OCA cooperative validation", + "category": "ecosystem_brand", + "lead": "@GTM π―", + "metric_label": "Pre-launch Signups", + "metric_value": 89, + "milestones": [ + { "label": "PRD Drafted", "status": "done", "date": "2026-07-01" }, + { "label": "Landing Page Live", "status": "done", "date": "2026-07-08" }, + { "label": "Beta Launch", "status": "in_progress", "target": "2026-08-01" }, + { "label": "Public Launch", "status": "pending", "target": "2026-09-01" } + ], + "links": { + "website": "https://f1visa.net", + "prd": "https://github.com/...", + "channel": "#f1visa", + "repo": "https://github.com/WeOwnNet/F1VISA" + }, + "last_updated": "2026-07-13T14:30:00-06:00" + }, + { + "id": "burnedout", + "name": "Burned Out Media", + "emoji": "π₯", + "status": "in_progress", + "tagline": "#AttractionMarketing content engine", + "category": "ecosystem_brand", + "lead": "@THY π", + "metric_label": "Pieces Published", + "metric_value": 12, + "milestones": [ + { "label": "Content Strategy Complete", "status": "done", "date": "2026-06-15" }, + { "label": "First 10 Pieces Published", "status": "done", "date": "2026-07-10" }, + { "label": "Client Pipeline Active", "status": "in_progress", "target": "2026-08-01" } + ], + "links": { + "website": "https://burnedout.xyz", + "channel": "#burnedout" + }, + "last_updated": "2026-07-13T14:30:00-06:00" + }, + { + "id": "exittoclient", + "name": "Exit To Client", + "emoji": "π€", + "status": "in_progress", + "tagline": "Service offerings for client acquisitions", + "category": "ecosystem_brand", + "lead": "@GTM π―", + "metric_label": "Client Pipeline", + "metric_value": 3, + "milestones": [ + { "label": "Service Menu Defined", "status": "done", "date": "2026-06-20" }, + { "label": "First 3 Clients in Pipeline", "status": "done", "date": "2026-07-05" }, + { "label": "First Client Signed", "status": "pending", "target": "2026-08-15" } + ], + "links": { "channel": "#exittoclient" }, + "last_updated": "2026-07-13T14:30:00-06:00" + }, + { + "id": "weownchat", + "name": "βΎοΈ WeOwn.Chat", + "emoji": "π¬", + "status": "live", + "tagline": "Community communication hub", + "category": "ecosystem_brand", + "lead": "@SHD π΅π°", + "metric_label": "Community Members", + "metric_value": 120, + "milestones": [ + { "label": "Domain Active", "status": "done", "date": "2026-06-01" }, + { "label": "First 100 Members", "status": "done", "date": "2026-07-01" }, + { "label": "Channels Launched", "status": "done", "date": "2026-07-01" } + ], + "links": { + "website": "https://weown.chat", + "repo": "https://github.com/WeOwnNet/WeOwnChat" + }, + "last_updated": "2026-07-13T14:30:00-06:00" + }, + { + "id": "weowndev", + "name": "βΎοΈ WeOwn.Dev", + "emoji": "π»", + "status": "live", + "tagline": "Infrastructure and development hub", + "category": "infrastructure", + "lead": "@MWK πΊ", + "metric_label": "Active Repos", + "metric_value": 14, + "milestones": [ + { "label": "Claw Instances Deployed", "status": "done", "date": "2026-06-15" }, + { "label": "CI/CD Pipeline Active", "status": "done", "date": "2026-07-01" }, + { "label": "Infra Monitoring Live", "status": "in_progress", "target": "2026-07-20" } + ], + "links": { + "website": "https://weown.dev", + "repo": "https://github.com/WeOwnNet" + }, + "last_updated": "2026-07-13T14:30:00-06:00" + } + ] + }, + "fun": { + "social_media": { + "youtube": { "subscribers": 1200, "views": 45000, "watch_time_hours": 2800 }, + "twitter": { "followers": 3400, "impressions_monthly": 125000, "engagement_rate": 4.2 }, + "linkedin": { "connections": 890, "post_reach_monthly": 45000 }, + "instagram": { "followers": 2100, "story_views_avg": 320 }, + "signal_telegram": { "members": 450 }, + "email": { "subscribers": 1800, "open_rate": 38.5, "click_rate": 12.1 } + } + }, + "earning": { + "pipeline": { + "ships_in_flight": 7, + "sub_projects_complete": 9, + "sub_projects_total": 14, + "p0_blockers": 2, + "prds_drafted": 5, + "prds_in_progress": 3, + "w29_milestone_percent": 65 + }, + "members_investors": { + "active_oca_members": 24, + "pre_launch_signups": 89, + "investor_pipeline": 5, + "sound_of_money_raised_usd": 44200, + "f1visa001_pre_orders": 12 + } + }, + "learning": { + "lessons_counter": 156, + "prds_completed": 8, + "skills_modules": 6, + "members_trained": 18, + "new_this_week": 3 + }, + "giving": { + "open_source": { + "repos": 14, + "pull_requests": 47, + "contributors": 9 + }, + "community": { + "total_members_all_channels": 380 + }, + "ecosystem": { + "instances_deployed": 12, + "members_onboarded": 24 + } + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..7b2d86f --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4918 @@ +{ + "name": "felg-dashboard", + "version": "1.3.0-r1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "felg-dashboard", + "version": "1.3.0-r1", + "dependencies": { + "@supabase/supabase-js": "^2.49.1", + "@tailwindcss/vite": "^4.3.3", + "astro": "^7.1.3", + "tailwindcss": "^4.3.3" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding/-/compiler-binding-0.3.1.tgz", + "integrity": "sha512-DaAUj29AIBU2XdJ8uwcab8lW5O2pk9pY8AXkcMw0sw77nVa3oeTYRcO+Dvbbpoexf6ThMc0FMWYCQ/wN1/T7oQ==", + "license": "MIT", + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@astrojs/compiler-binding-darwin-arm64": "0.3.1", + "@astrojs/compiler-binding-darwin-x64": "0.3.1", + "@astrojs/compiler-binding-linux-arm64-gnu": "0.3.1", + "@astrojs/compiler-binding-linux-arm64-musl": "0.3.1", + "@astrojs/compiler-binding-linux-x64-gnu": "0.3.1", + "@astrojs/compiler-binding-linux-x64-musl": "0.3.1", + "@astrojs/compiler-binding-wasm32-wasi": "0.3.1", + "@astrojs/compiler-binding-win32-arm64-msvc": "0.3.1", + "@astrojs/compiler-binding-win32-x64-msvc": "0.3.1" + } + }, + "node_modules/@astrojs/compiler-binding-darwin-arm64": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-arm64/-/compiler-binding-darwin-arm64-0.3.1.tgz", + "integrity": "sha512-IEmEF2fUIlTHtpeE/isyEGVOB14cEyh/LZOFYt6wn3jNyVpdC8aR5OZ+RzFUR/f+8ZDM1LaMwZKvoA7eMyJeFw==", + "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.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-x64/-/compiler-binding-darwin-x64-0.3.1.tgz", + "integrity": "sha512-GF2kIxjpPDLsn94zbZNMsxEmkU828QqnmM7kiQJnaooS3jmI+I7kk6+oI6EpwOsK3femCMdcm+wmOsEqtGrmjQ==", + "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.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-gnu/-/compiler-binding-linux-arm64-gnu-0.3.1.tgz", + "integrity": "sha512-XJL3SDmOtVrqFhCirNcHwE91+IesJqlgNo23I4qW9QUYfwzm/TBZuH61fgqsb1ttgR1mMYz6ooPWs0JDhwMqpQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-arm64-musl": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-musl/-/compiler-binding-linux-arm64-musl-0.3.1.tgz", + "integrity": "sha512-xqE8BVbDoBueK/B47w30PtkVofUWJKGkwoMVE+EOMLf11rnoANxIAdA9FPqY+rng4oNI5ndHGsri1yPj2k8vZQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-x64-gnu": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-gnu/-/compiler-binding-linux-x64-gnu-0.3.1.tgz", + "integrity": "sha512-1y0StU1qiCuDFH3rmbRJXcxdfHxFPrES1Rd+RLffosvUR7I2cH5SF5SFnBN9vXpzpkmyElZm3Yr47iJBPN7vVA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-x64-musl": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-musl/-/compiler-binding-linux-x64-musl-0.3.1.tgz", + "integrity": "sha512-16q0fYf7kpbmdObZEeZJEup8hQv/whgNwVjrSvT8umrKwLDSnNIWiQpm09lQQu6bweZB0XyIvHwlPitvJhC+hg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-wasm32-wasi": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-wasm32-wasi/-/compiler-binding-wasm32-wasi-0.3.1.tgz", + "integrity": "sha512-cB456shIwDv/PrVT+2QG7LFndpHkVge5HjqADKZgGaAc9JHVktCtjSrcdkRQ+3tbkPazNKaTLRjXLIiz2NIx9g==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@astrojs/compiler-binding-win32-arm64-msvc": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-arm64-msvc/-/compiler-binding-win32-arm64-msvc-0.3.1.tgz", + "integrity": "sha512-ur/9+If/yTE69mmeX5MqSZndL0HOyx67GeNZUy3N7wVdWpLz9UTJXwyWS4UR2PUQHitghjsM5xoX0Ge56WRVQQ==", + "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.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-x64-msvc/-/compiler-binding-win32-x64-msvc-0.3.1.tgz", + "integrity": "sha512-k0W+kDBzDkNZOqu4kElDvCOIbKw5Ut9S1WZ1Krj3KTgNuBERNKXsMMsRLLcbgfdMdbe7bTekQLshZrrvmYpmwA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-rs": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-rs/-/compiler-rs-0.3.1.tgz", + "integrity": "sha512-aT7xkgsbNoS6nriY5qKpbihK43slFHO41iqgHCTdOvn1ifaQxLCc5yXy+6GzAtiafoaC1zA7OwVXCXMsvUZOkg==", + "license": "MIT", + "dependencies": { + "@astrojs/compiler-binding": "0.3.1" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/@astrojs/internal-helpers": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.10.1.tgz", + "integrity": "sha512-5phcroT/vmOOrYuuAxtkbPixy5hePtlz9i8K4OeDv3dNK6/UQRuXPOSRTxIOBbUY5Sonw2UaxjbuVc43Mcir6Q==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.4", + "@types/mdast": "^4.0.4", + "js-yaml": "^4.1.1", + "picomatch": "^4.0.4", + "retext-smartypants": "^6.2.0", + "shiki": "^4.0.2", + "smol-toml": "^1.6.0", + "unified": "^11.0.5" + } + }, + "node_modules/@astrojs/markdown-satteri": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-satteri/-/markdown-satteri-0.3.4.tgz", + "integrity": "sha512-6Lvt/bQZEBW+zzdhPblvfZEy5PGEYJaUsUqaCgwHeRPxZJL1gc9I+DRLKWJjjYTWDzVUTzXlMq4WwSK+X34CVw==", + "license": "MIT", + "dependencies": { + "@astrojs/internal-helpers": "0.10.1", + "@astrojs/prism": "4.0.2", + "github-slugger": "^2.0.0", + "hast-util-from-html": "^2.0.3", + "satteri": "^0.9.1" + } + }, + "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/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/@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.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "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.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-arm64/-/satteri-darwin-arm64-0.9.5.tgz", + "integrity": "sha512-iw4nZgx9v30lWo/MTngQqi1pI78KI0DnkSm+lVJGYdmPLgAyDNJigVhpG42/Iq55A6c1Ll8q66ljyyRiQUxwow==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@bruits/satteri-darwin-x64": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-x64/-/satteri-darwin-x64-0.9.5.tgz", + "integrity": "sha512-6T26Z5Kf3cFW2PSlk9p7zT7yVxvuBSiJvYyz9u8KjYwMTqZyIDOj2wDyNpxKV4+6yUVG7rddq2QwvG/8LJA2+Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@bruits/satteri-linux-arm64-gnu": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-gnu/-/satteri-linux-arm64-gnu-0.9.5.tgz", + "integrity": "sha512-u51id17uJwNEMK9nBlICsq6U31c+XVqQueVBkwRIzZG+gMpS8TOJctt5h5Wz33Z8xnMdTd+adtACVz0yHgGuOA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-arm64-musl": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-musl/-/satteri-linux-arm64-musl-0.9.5.tgz", + "integrity": "sha512-v39HxiwGC5Rqm01HksP6+5Y+xKLPlsuVFgIgpEAo+SiQ22c+mJVhS3u7Z6ePAKdhL5NJoK1xq70kLz3L13AhpQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-x64-gnu": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-gnu/-/satteri-linux-x64-gnu-0.9.5.tgz", + "integrity": "sha512-F3uO8uFp3pAP5ZGXttwvh57GS7s0lL953tnNdyI2gRyP4kOOkp6pyGojNJzCjkDvWI2Cvb9iNrKok3aqQPauAw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-x64-musl": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-musl/-/satteri-linux-x64-musl-0.9.5.tgz", + "integrity": "sha512-bicEqglLlz++mWyADaZoP0JY20s4vDfLjaPYgQqC+NI4zZLTOOg1T4GB8aqtc822Pqji8SQBmSrTb7CrP8i08Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-wasm32-wasi": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-wasm32-wasi/-/satteri-wasm32-wasi-0.9.5.tgz", + "integrity": "sha512-zauAuMwfPnKPUkd4AFixRFpXdgKwP2mKgxrIIo2gJzW0/ZneF9dbHnLkojSpaBnCCp7VUL1hIi5WWZvB1CqmAQ==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "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-win32-arm64-msvc": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-arm64-msvc/-/satteri-win32-arm64-msvc-0.9.5.tgz", + "integrity": "sha512-SrfE7NEsgZjBvU3c+RR6oQRu0ToXY5uVJEbieXEF0YTctIV2zAVlbaMjWLts074QCgh3a+XHWkR/lWh2VH2LUg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@bruits/satteri-win32-x64-msvc": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-x64-msvc/-/satteri-win32-x64-msvc-0.9.5.tgz", + "integrity": "sha512-5Kw9ZAtTGS8WHizyn+CJhjjfIQrw+7jcZodpmpXJjefnO15M8UexIi6JR2E5thyvsmHyhL6ZDDMUNR4bKJPd4g==", + "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.4.3", + "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.4.3.tgz", + "integrity": "sha512-/kr3UWNtdJfxZtPgDqUOmG2pvwlmcLGheex5yiZKdwbzZJxhV+HMNR9QNmyY5cGwTNV6LrR7Jtp+KjhUAP1qBQ==", + "license": "MIT", + "dependencies": { + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/@clack/prompts": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.7.0.tgz", + "integrity": "sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A==", + "license": "MIT", + "dependencies": { + "@clack/core": "1.4.3", + "fast-string-width": "^3.0.2", + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.2.tgz", + "integrity": "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", + "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "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/@esbuild/aix-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "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.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.3.tgz", + "integrity": "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==", + "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.2" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.3.tgz", + "integrity": "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==", + "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.2" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.3.tgz", + "integrity": "sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.2.tgz", + "integrity": "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==", + "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.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.2.tgz", + "integrity": "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==", + "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.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.2.tgz", + "integrity": "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==", + "cpu": [ + "arm" + ], + "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.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.2.tgz", + "integrity": "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==", + "cpu": [ + "arm64" + ], + "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.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.2.tgz", + "integrity": "sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==", + "cpu": [ + "ppc64" + ], + "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.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.2.tgz", + "integrity": "sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==", + "cpu": [ + "riscv64" + ], + "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.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.2.tgz", + "integrity": "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==", + "cpu": [ + "s390x" + ], + "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.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.2.tgz", + "integrity": "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==", + "cpu": [ + "x64" + ], + "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.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.2.tgz", + "integrity": "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==", + "cpu": [ + "arm64" + ], + "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.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.2.tgz", + "integrity": "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==", + "cpu": [ + "x64" + ], + "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.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.3.tgz", + "integrity": "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==", + "cpu": [ + "arm" + ], + "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.2" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.3.tgz", + "integrity": "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==", + "cpu": [ + "arm64" + ], + "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.2" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.3.tgz", + "integrity": "sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==", + "cpu": [ + "ppc64" + ], + "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.2" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.3.tgz", + "integrity": "sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==", + "cpu": [ + "riscv64" + ], + "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.2" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.3.tgz", + "integrity": "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==", + "cpu": [ + "s390x" + ], + "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.2" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.3.tgz", + "integrity": "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==", + "cpu": [ + "x64" + ], + "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.2" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.3.tgz", + "integrity": "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==", + "cpu": [ + "arm64" + ], + "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.2" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.3.tgz", + "integrity": "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==", + "cpu": [ + "x64" + ], + "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.2" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.3.tgz", + "integrity": "sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.3.tgz", + "integrity": "sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.3.tgz", + "integrity": "sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==", + "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.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.3.tgz", + "integrity": "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==", + "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.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.3.tgz", + "integrity": "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==", + "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/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "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.139.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz", + "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", + "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz", + "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz", + "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz", + "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==", + "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.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz", + "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==", + "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.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz", + "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz", + "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz", + "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz", + "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz", + "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz", + "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz", + "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz", + "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-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/@rolldown/binding-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/@rolldown/binding-win32-arm64-msvc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz", + "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==", + "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.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz", + "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==", + "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/@rollup/pluginutils": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", + "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@shikijs/core": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.3.1.tgz", + "integrity": "sha512-ANMDxuaPsNMdDC1m4vfvhlDmJweMwkE5XitTwrq2rWHx5jM+dlm4MmHt2PP6t0uejfR77SuhrhJ0zEijIF/uhA==", + "license": "MIT", + "dependencies": { + "@shikijs/primitive": "4.3.1", + "@shikijs/types": "4.3.1", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4", + "hast-util-to-html": "^9.0.5" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-javascript": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.3.1.tgz", + "integrity": "sha512-JBItcnPuYq7jVJdZo/vMj94r+szT7XEjHFX+mvFDGSEIbVAXAGyHAHzhbWzpGOwYidCZrErJLLgn2PVeiokHnQ==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.3.1", + "@shikijs/vscode-textmate": "^10.0.2", + "oniguruma-to-es": "^4.3.6" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.3.1.tgz", + "integrity": "sha512-OXyNMzg0pews+msMj4cHeqT4xiYKKvbnn6VbdAXxfoFl3SSx4fJTc8FadECuc5/H9p3BzhNAoAUXKwAu9rWYhg==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.3.1", + "@shikijs/vscode-textmate": "^10.0.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/langs": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.3.1.tgz", + "integrity": "sha512-m0l9nsDqgBHvbZbk7A0/kXz/impK3uB/c6rAn6Gpg/uPtdZRQ+alsN/17MU5thb68XTj/4DxkZAotrM0GGSpDQ==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/primitive": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.3.1.tgz", + "integrity": "sha512-CXQRQOYy1leqQ8ceTeJdmXv/bsUY++6QyLpXJ94LZAAYj5X2SKRdc5ipguv4NPyGVKItB2PPwUpRNe0Sjh5S1A==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.3.1", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/themes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.3.1.tgz", + "integrity": "sha512-dgpoJ4WqNi2yTmizQHBJ5zcX6j2lE6icN/0yt4l1kkf16jrY/pwPLoTb1ETsWMz0OBLf9ZNvwmxft+cH+N9qSA==", + "license": "MIT", + "dependencies": { + "@shikijs/types": "4.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@shikijs/types": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.3.1.tgz", + "integrity": "sha512-CHFxE0jztBIZRHH6gxXE7DXUCFXjReEGxZ/j0rfSLGKZuwp2xBYycEP14875DSa9KLL/6700oxIq6oO6ef9K2g==", + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + }, + "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/@supabase/auth-js": { + "version": "2.110.8", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.110.8.tgz", + "integrity": "sha512-TQ5neTUDX2C2WmyYa03yGhLMkhdE/SkHXtK8/qxO/APUy3rsymsJCBP48p4jcN6iO2G0ow6RRexQd2mX+dSyJg==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.110.8", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.110.8.tgz", + "integrity": "sha512-5yB9TLYzvv2oSQxwb0gamEvIAsuH66pVt7AM/pz03S7wN6ehD34GNgbShrccetqPedXQSz7e/1hAJ9NeEhoZVg==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@supabase/phoenix": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/@supabase/phoenix/-/phoenix-0.4.5.tgz", + "integrity": "sha512-aAn9H9ovVyeApKy11OWOrrOGq8DV68yWeH4ud2lN9fzn4aO8Zb5GLL9m1pUg9nLqIcT+ZDfAcsZe0E/nqdv2lw==", + "license": "MIT" + }, + "node_modules/@supabase/postgrest-js": { + "version": "2.110.8", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.110.8.tgz", + "integrity": "sha512-QeRROxl1PpOZw5Jzi7BwdN9icsycMrLlCCvsjS0hYLW+nZoaT46zdagz/glJirj8jHF4jSd5Jyipuae2cBClCw==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.110.8", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.110.8.tgz", + "integrity": "sha512-mwX7ituX6O31fLf+0g65rpLlNxqgnMaPltPsQwzox6jfmbfVl3tCxXrfr3HEsQcCRjpjuJG1+A0vFzP1yVjKHA==", + "license": "MIT", + "dependencies": { + "@supabase/phoenix": "0.4.5", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.110.8", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.110.8.tgz", + "integrity": "sha512-CcfhkZFBLxsthgUabZKxwfsoXdrikIGsL3LsGoV3FZTqCMx/s1y49taT4jT/oya5+1IuB0sFFHw6pF0o0iJniQ==", + "license": "MIT", + "dependencies": { + "iceberg-js": "^0.8.1", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.110.8", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.110.8.tgz", + "integrity": "sha512-E5qzoe74zhJRv4wRcbO9eMYzeQDb/+h6c603pL8shcxLGBjTKsIF7XXj05IcNj23TLDgJN1WkMw7mwAPyu5dZg==", + "license": "MIT", + "dependencies": { + "@supabase/auth-js": "2.110.8", + "@supabase/functions-js": "2.110.8", + "@supabase/postgrest-js": "2.110.8", + "@supabase/realtime-js": "2.110.8", + "@supabase/storage-js": "2.110.8" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", + "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.24.1", + "jiti": "^2.7.0", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.3" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "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.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "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/@tailwindcss/node/node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "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/@tailwindcss/node/node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "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/@tailwindcss/node/node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "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/@tailwindcss/node/node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "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/@tailwindcss/node/node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@tailwindcss/node/node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "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/@tailwindcss/node/node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "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/@tailwindcss/oxide": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.3.tgz", + "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==", + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-x64": "4.3.3", + "@tailwindcss/oxide-freebsd-x64": "4.3.3", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-x64-musl": "4.3.3", + "@tailwindcss/oxide-wasm32-wasi": "4.3.3", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz", + "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz", + "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz", + "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz", + "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz", + "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz", + "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz", + "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz", + "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz", + "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz", + "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.11.1", + "@emnapi/runtime": "^1.11.1", + "@emnapi/wasi-threads": "^1.2.2", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", + "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz", + "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.3.tgz", + "integrity": "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.3.3", + "@tailwindcss/oxide": "4.3.3", + "tailwindcss": "4.3.3" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "license": "MIT", + "optional": true, + "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/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.3.3", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz", + "integrity": "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==", + "license": "ISC" + }, + "node_modules/am-i-vibing": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/am-i-vibing/-/am-i-vibing-0.4.0.tgz", + "integrity": "sha512-MxT4XZL7pzLHpuvhDKdMaQHMGGkJDLluKBLsbstn+8wv9sWcFT6h+0ve9qkml95amVTZtZV83gQe2hY+ojgHLg==", + "license": "MIT", + "dependencies": { + "process-ancestry": "^0.1.0" + }, + "bin": { + "am-i-vibing": "dist/cli.mjs" + } + }, + "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/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "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.1.3", + "resolved": "https://registry.npmjs.org/astro/-/astro-7.1.3.tgz", + "integrity": "sha512-4dhPyAAXthf3xLEYnG8SeL7yr/nTPPABfY7e9YF0yuO+vK9Xp+8Q5j4xzsmL3GueukQv4oNwGNTBepLOiDGeJA==", + "license": "MIT", + "dependencies": { + "@astrojs/compiler-rs": "^0.3.1", + "@astrojs/internal-helpers": "0.10.1", + "@astrojs/markdown-satteri": "0.3.4", + "@astrojs/telemetry": "3.3.3", + "@capsizecss/unpack": "^4.0.0", + "@clack/prompts": "^1.1.0", + "@oslojs/encoding": "^1.1.0", + "@rollup/pluginutils": "^5.3.0", + "am-i-vibing": "^0.4.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.8.1", + "diff": "^8.0.3", + "dset": "^3.1.4", + "es-module-lexer": "^2.0.0", + "esbuild": "^0.28.0", + "flattie": "^1.1.1", + "fontace": "~0.4.1", + "get-tsconfig": "5.0.0-beta.4", + "github-slugger": "^2.0.0", + "html-escaper": "3.0.3", + "http-cache-semantics": "^4.2.0", + "js-yaml": "^4.1.1", + "jsonc-parser": "^3.3.1", + "magic-string": "^0.30.21", + "magicast": "^0.5.2", + "mrmime": "^2.0.1", + "neotraverse": "^1.0.1", + "obug": "^2.1.1", + "p-limit": "^7.3.0", + "p-queue": "^9.1.0", + "package-manager-detector": "^1.6.0", + "piccolore": "^0.1.3", + "picomatch": "^4.0.4", + "semver": "^7.7.4", + "shiki": "^4.0.2", + "smol-toml": "^1.6.0", + "svgo": "^4.0.1", + "tinyclip": "^0.1.12", + "tinyexec": "^1.0.4", + "tinyglobby": "^0.2.15", + "ultrahtml": "^1.6.0", + "unifont": "~0.7.4", + "unstorage": "^1.17.5", + "vite": "^8.0.13", + "vitefu": "^1.1.2", + "xxhash-wasm": "^1.1.0", + "yargs-parser": "^22.0.0", + "zod": "^4.3.6" + }, + "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.34.0 || ^0.35.0" + }, + "peerDependencies": { + "@astrojs/markdown-remark": "7.2.1" + }, + "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": "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/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/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": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.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": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "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.8.2", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.8.2.tgz", + "integrity": "sha512-DObPPAfdtFbXjxLqK8s2Xk9ZuWz5+ZoFEhC7J76es4GU/rEiXwHTmbImoCdyoCOcBH1UF3+Cz6Z2sYD4hyl5TA==", + "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": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", + "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/dom-serializer/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/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/enhanced-resolve": { + "version": "5.24.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.3.tgz", + "integrity": "sha512-PwKooW9JUzh5chmYfHM3IQl5OkK2u2Nm011MgeZrss3JmFraUx/fqrf78kk8GUMYoibx/14MdwTl/1WKkG7TpQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-module-lexer": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "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-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-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/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-tsconfig": { + "version": "5.0.0-beta.4", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-5.0.0-beta.4.tgz", + "integrity": "sha512-7nF7C9fIPFEMHgEMEfgIlO9wDdZ8CyHw27rWciFZfHvHDReIiPhsYuzPRXsfvBCqFy1l8RRyyWV7QLM+ZhUJsQ==", + "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/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "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-from-html": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^7.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "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/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.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.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause" + }, + "node_modules/iceberg-js": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz", + "integrity": "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "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/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-yaml": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "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/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" + ], + "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" + ], + "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" + ], + "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" + ], + "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.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.3.tgz", + "integrity": "sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.3", + "@babel/types": "^7.29.0", + "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.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "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/nanoid": { + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "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.4", + "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.4.tgz", + "integrity": "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==", + "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": "2.1.4", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", + "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", + "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.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "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.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-7.3.1.tgz", + "integrity": "sha512-0trZaiG7Y7kN/Egy9a8j47t9osC0Tch4PaIWd9yGF6bvmlk7muExRvGNYb8sXBwEKMoNKsbNN9P8EefuQekE4Q==", + "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.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.1.tgz", + "integrity": "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-manager-detector": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.8.0.tgz", + "integrity": "sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==", + "license": "MIT" + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "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.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.22", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.22.tgz", + "integrity": "sha512-KBDEIpLrvpv16pp3K0Fw+UCoZfopFjjgeB+0tA/aaThfEE74kKDLrgg603YvOWJyg3+WYtyq3xYsQWsIyZlPqQ==", + "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.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "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": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "license": "MIT", + "engines": { + "node": ">= 20.19.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/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.1.5", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", + "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==", + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.139.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.1.5", + "@rolldown/binding-darwin-arm64": "1.1.5", + "@rolldown/binding-darwin-x64": "1.1.5", + "@rolldown/binding-freebsd-x64": "1.1.5", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", + "@rolldown/binding-linux-arm64-gnu": "1.1.5", + "@rolldown/binding-linux-arm64-musl": "1.1.5", + "@rolldown/binding-linux-ppc64-gnu": "1.1.5", + "@rolldown/binding-linux-s390x-gnu": "1.1.5", + "@rolldown/binding-linux-x64-gnu": "1.1.5", + "@rolldown/binding-linux-x64-musl": "1.1.5", + "@rolldown/binding-openharmony-arm64": "1.1.5", + "@rolldown/binding-wasm32-wasi": "1.1.5", + "@rolldown/binding-win32-arm64-msvc": "1.1.5", + "@rolldown/binding-win32-x64-msvc": "1.1.5" + } + }, + "node_modules/satteri": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/satteri/-/satteri-0.9.5.tgz", + "integrity": "sha512-ZuWVl+vnM64y+/TtX8Kosv2c00W+hLQiiwnEL6H0UKVVrxFqMw4D2CJHHQaouVd89OAhtBBfjWLqhKi3TVUV4w==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.5", + "@types/hast": "^3.0.4", + "@types/mdast": "^4.0.4", + "@types/unist": "^3.0.3" + }, + "optionalDependencies": { + "@bruits/satteri-darwin-arm64": "0.9.5", + "@bruits/satteri-darwin-x64": "0.9.5", + "@bruits/satteri-linux-arm64-gnu": "0.9.5", + "@bruits/satteri-linux-arm64-musl": "0.9.5", + "@bruits/satteri-linux-x64-gnu": "0.9.5", + "@bruits/satteri-linux-x64-musl": "0.9.5", + "@bruits/satteri-wasm32-wasi": "0.9.5", + "@bruits/satteri-win32-arm64-msvc": "0.9.5", + "@bruits/satteri-win32-x64-msvc": "0.9.5" + } + }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "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==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.3.tgz", + "integrity": "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==", + "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.3", + "@img/sharp-darwin-x64": "0.35.3", + "@img/sharp-freebsd-wasm32": "0.35.3", + "@img/sharp-libvips-darwin-arm64": "1.3.2", + "@img/sharp-libvips-darwin-x64": "1.3.2", + "@img/sharp-libvips-linux-arm": "1.3.2", + "@img/sharp-libvips-linux-arm64": "1.3.2", + "@img/sharp-libvips-linux-ppc64": "1.3.2", + "@img/sharp-libvips-linux-riscv64": "1.3.2", + "@img/sharp-libvips-linux-s390x": "1.3.2", + "@img/sharp-libvips-linux-x64": "1.3.2", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", + "@img/sharp-libvips-linuxmusl-x64": "1.3.2", + "@img/sharp-linux-arm": "0.35.3", + "@img/sharp-linux-arm64": "0.35.3", + "@img/sharp-linux-ppc64": "0.35.3", + "@img/sharp-linux-riscv64": "0.35.3", + "@img/sharp-linux-s390x": "0.35.3", + "@img/sharp-linux-x64": "0.35.3", + "@img/sharp-linuxmusl-arm64": "0.35.3", + "@img/sharp-linuxmusl-x64": "0.35.3", + "@img/sharp-webcontainers-wasm32": "0.35.3", + "@img/sharp-win32-arm64": "0.35.3", + "@img/sharp-win32-ia32": "0.35.3", + "@img/sharp-win32-x64": "0.35.3" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/shiki": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-4.3.1.tgz", + "integrity": "sha512-oR+qDVi2OjX1tmDpyv+3KviX01KzO6Af+0NNnKnsp9491UEGz2YpxTuJboS/6VhYpTdqzmuJBuiTlrAWWJAssw==", + "license": "MIT", + "dependencies": { + "@shikijs/core": "4.3.1", + "@shikijs/engine-javascript": "4.3.1", + "@shikijs/engine-oniguruma": "4.3.1", + "@shikijs/langs": "4.3.1", + "@shikijs/themes": "4.3.1", + "@shikijs/types": "4.3.1", + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + }, + "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/smol-toml": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.7.0.tgz", + "integrity": "sha512-aqVvWoyO21L23mb+drl4RmMXbf6N7FdHjAhTRA9ZBL7apWBgfWC16KjrASI+1p9GAroljyMHj6fK67i0UiTNvQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "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/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/svgo": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.2.tgz", + "integrity": "sha512-ekx94z1rRc5LDi6oSUaeRnYhd0UOJxdtQCL2rF8xpWxD3TPAsISWOrxezqGovqS38GRZOdpDfvQe3ts6F7nsng==", + "license": "MIT", + "dependencies": { + "commander": "^11.1.0", + "css-select": "^5.1.0", + "css-tree": "^3.0.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.1.1", + "sax": "^1.5.0" + }, + "bin": { + "svgo": "bin/svgo.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/tailwindcss": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz", + "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "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": "0.1.15", + "resolved": "https://registry.npmjs.org/tinyclip/-/tinyclip-0.1.15.tgz", + "integrity": "sha512-uo33abH+Ays0xYaDysoBt494Hb3hsEczMpcC0MwFl773pazORx4fmvKhclhR1wonUbB6vvpRsvVMwnhfqeMc+A==", + "license": "MIT", + "engines": { + "node": "^16.14.0 || >= 17.3.0" + } + }, + "node_modules/tinyexec": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "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==", + "license": "0BSD" + }, + "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/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.4", + "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.7.4.tgz", + "integrity": "sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg==", + "license": "MIT", + "dependencies": { + "css-tree": "^3.1.0", + "ofetch": "^1.5.1", + "ohash": "^2.0.11" + } + }, + "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/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-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.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.1.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.5.tgz", + "integrity": "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==", + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.17", + "rolldown": "~1.1.5", + "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.3.0", + "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/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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/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.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "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..d19c975 --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "felg-dashboard", + "type": "module", + "version": "1.3.0-r1", + "engines": { + "node": ">=22.12.0" + }, + "scripts": { + "dev": "astro dev", + "build": "astro build", + "preview": "astro preview", + "astro": "astro" + }, + "dependencies": { + "@supabase/supabase-js": "^2.49.1", + "@tailwindcss/vite": "^4.3.3", + "astro": "^7.1.3", + "tailwindcss": "^4.3.3" + } +} \ No newline at end of file diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..7f48a94 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..8055128 --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1,3 @@ + + π + \ No newline at end of file diff --git a/scripts/sync-to-supabase.js b/scripts/sync-to-supabase.js new file mode 100644 index 0000000..7938968 --- /dev/null +++ b/scripts/sync-to-supabase.js @@ -0,0 +1,77 @@ +#!/usr/bin/env node + +/** + * sync-to-supabase.js + * Upserts data/dashboard.json into the Supabase dashboard_state table. + * CI-only: uses the service-role key (never shipped to the browser). + * + * Environment variables: + * PUBLIC_SUPABASE_URL β Supabase project URL + * SUPABASE_SERVICE_ROLE_KEY β service-role key (secret, WRITE access) + * + * Usage: node scripts/sync-to-supabase.js + */ + +import { readFileSync } from 'fs'; +import { join, dirname } from 'path'; +import { fileURLToPath } from 'url'; +import { createClient } from '@supabase/supabase-js'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const DATA_PATH = join(__dirname, '..', 'data', 'dashboard.json'); + +const SUPABASE_URL = process.env.PUBLIC_SUPABASE_URL; +const SERVICE_ROLE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY; + +if (!SUPABASE_URL) { + console.error('β PUBLIC_SUPABASE_URL is not set'); + process.exit(1); +} + +if (!SERVICE_ROLE_KEY) { + console.error('β SUPABASE_SERVICE_ROLE_KEY is not set'); + process.exit(1); +} + +// Create client with service-role key (admin, bypasses RLS) +const supabase = createClient(SUPABASE_URL, SERVICE_ROLE_KEY); + +async function main() { + let raw; + try { + raw = readFileSync(DATA_PATH, 'utf-8'); + } catch { + console.error(`β Cannot read ${DATA_PATH}`); + process.exit(1); + } + + let payload; + try { + payload = JSON.parse(raw); + } catch { + console.error('β Invalid JSON in dashboard.json'); + process.exit(1); + } + + console.log('π€ Syncing data to Supabase...'); + + const { error } = await supabase + .from('dashboard_state') + .upsert( + { + id: 'current', + payload, + updated_at: new Date().toISOString(), + }, + { onConflict: 'id' } + ); + + if (error) { + console.error('β Sync failed:', error.message); + process.exit(1); + } + + console.log('β Data synced successfully to Supabase'); +} + +main(); \ No newline at end of file diff --git a/scripts/validate-data.js b/scripts/validate-data.js new file mode 100644 index 0000000..2609d57 --- /dev/null +++ b/scripts/validate-data.js @@ -0,0 +1,129 @@ +#!/usr/bin/env node + +/** + * validate-data.js + * Validates data/dashboard.json against the PRD Appendix B schema contract. + * Runs in CI before sync-to-supabase.js. + * + * Checks: + * - Required top-level keys exist + * - brands.top_5_ids match entries in brands.all_brands + * - Status enum values are valid + * - All required fields per brand are present + * - No unrecognized top-level keys + */ + +import { readFileSync } from 'fs'; +import { join, dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const DATA_PATH = join(__dirname, '..', 'data', 'dashboard.json'); + +const VALID_STATUSES = ['live', 'in_progress', 'not_started', 'paused']; +const VALID_MILESTONE_STATUSES = ['done', 'in_progress', 'pending']; + +const REQUIRED_TOP_LEVEL = ['dashboard', 'brands', 'fun', 'earning', 'learning', 'giving']; +const REQUIRED_BRAND_FIELDS = ['id', 'name', 'emoji', 'status', 'tagline', 'category', 'lead', 'metric_label', 'metric_value', 'milestones', 'links', 'last_updated']; + +let exitCode = 0; + +function error(msg) { + console.error(`β ${msg}`); + exitCode = 1; +} + +function main() { + let raw; + try { + raw = readFileSync(DATA_PATH, 'utf-8'); + } catch { + error(`Cannot read ${DATA_PATH}`); + process.exit(1); + } + + let data; + try { + data = JSON.parse(raw); + } catch { + error('Invalid JSON'); + process.exit(1); + } + + // Check required top-level keys + for (const key of REQUIRED_TOP_LEVEL) { + if (!(key in data)) error(`Missing top-level key: "${key}"`); + } + + // No unexpected top-level keys + for (const key of Object.keys(data)) { + if (!REQUIRED_TOP_LEVEL.includes(key) && key !== '$schema') { + error(`Unexpected top-level key: "${key}"`); + } + } + + // Dashboard meta + if (data.dashboard) { + if (!data.dashboard.name) error('dashboard.name is required'); + if (!data.dashboard.last_updated) error('dashboard.last_updated is required'); + } + + // Brands validation + if (data.brands) { + const { top_5_ids, all_brands } = data.brands; + + if (!Array.isArray(top_5_ids)) error('brands.top_5_ids must be an array'); + if (!Array.isArray(all_brands)) error('brands.all_brands must be an array'); + + if (all_brands) { + const brandIds = new Set(all_brands.map(b => b.id)); + + // Check top_5_ids reference valid brands + if (Array.isArray(top_5_ids)) { + for (const id of top_5_ids) { + if (!brandIds.has(id)) error(`top_5_ids references unknown brand: "${id}"`); + } + } + + // Validate each brand + for (const brand of all_brands) { + for (const field of REQUIRED_BRAND_FIELDS) { + if (!(field in brand)) error(`Brand "${brand.id || '(missing id)'}" missing field: "${field}"`); + } + + if (brand.status && !VALID_STATUSES.includes(brand.status)) { + error(`Brand "${brand.id}" has invalid status: "${brand.status}"`); + } + + if (Array.isArray(brand.milestones)) { + for (const ms of brand.milestones) { + if (ms.status && !VALID_MILESTONE_STATUSES.includes(ms.status)) { + error(`Brand "${brand.id}" milestone "${ms.label}" has invalid status: "${ms.status}"`); + } + } + } + } + } + } + + // Social media check + if (data.fun?.social_media) { + const sm = data.fun.social_media; + if (!sm.youtube?.subscribers) error('fun.social_media.youtube.subscribers is required'); + if (!sm.twitter?.followers) error('fun.social_media.twitter.followers is required'); + } + + // Earning check + if (data.earning?.pipeline) { + if (typeof data.earning.pipeline.sub_projects_complete !== 'number') error('earning.pipeline.sub_projects_complete is required'); + if (typeof data.earning.pipeline.sub_projects_total !== 'number') error('earning.pipeline.sub_projects_total is required'); + } + + if (exitCode === 0) { + console.log('β data/dashboard.json is valid'); + } + + process.exit(exitCode); +} + +main(); \ No newline at end of file diff --git a/src/components/BrandCard.astro b/src/components/BrandCard.astro new file mode 100644 index 0000000..a9aa390 --- /dev/null +++ b/src/components/BrandCard.astro @@ -0,0 +1,28 @@ +--- +import type { Brand } from '../lib/types'; +import { getStatusColor, getStatusLabel } from '../lib/utils'; + +export interface Props { brand: Brand; } + +const { brand } = Astro.props; +const statusColor = getStatusColor(brand.status); +const statusLabel = getStatusLabel(brand.status); +--- + + + + + {brand.emoji} + {brand.name} + + β {statusLabel} + + {brand.tagline} + + {brand.metric_label} + {brand.metric_value.toLocaleString()} + + diff --git a/src/components/BrandGrid.astro b/src/components/BrandGrid.astro new file mode 100644 index 0000000..f6a6c79 --- /dev/null +++ b/src/components/BrandGrid.astro @@ -0,0 +1,20 @@ +--- +import type { Brand } from '../lib/types'; +import BrandCard from './BrandCard.astro'; +export interface Props { topBrands: Brand[]; } +const { topBrands } = Astro.props; +--- + + + + + + Top 5 Ecosystem Brands + ({topBrands.length}) + + View all β + + + {topBrands.map((brand) => )} + + diff --git a/src/components/EarningSection.astro b/src/components/EarningSection.astro new file mode 100644 index 0000000..ca567bb --- /dev/null +++ b/src/components/EarningSection.astro @@ -0,0 +1,89 @@ +--- +export interface Props { + pipeline: { ships_in_flight: number; sub_projects_complete: number; sub_projects_total: number; p0_blockers: number; prds_drafted: number; prds_in_progress: number; w29_milestone_percent: number; }; + members_investors: { active_oca_members: number; pre_launch_signups: number; investor_pipeline: number; sound_of_money_raised_usd: number; f1visa001_pre_orders: number; }; + lastUpdated: string; +} +const { pipeline, members_investors, lastUpdated } = Astro.props; +function fmtUSD(n: number): string { return '$' + n.toLocaleString(); } +const progress = pipeline.sub_projects_total > 0 ? Math.round((pipeline.sub_projects_complete / pipeline.sub_projects_total) * 100) : 0; +--- + + + + + Earning + + + + + + + π’ + Pipeline + + + + + Ships in Flight + {pipeline.ships_in_flight} + + + Sub-Projects Complete + {pipeline.sub_projects_complete}/{pipeline.sub_projects_total} + + + P0 Blockers + {pipeline.p0_blockers} + + + PRDs Drafted / In Progress + {pipeline.prds_drafted} / {pipeline.prds_in_progress} + + + + + + W29 Milestone + {pipeline.w29_milestone_percent}% + + + + + + + + + + + π₯ + Members + Investors + + + + + Active (.OCA) Members + {members_investors.active_oca_members} + + + Pre-Launch Signups + {members_investors.pre_launch_signups} + + + Investor Pipeline + {members_investors.investor_pipeline} + + + #SoundOfMoney Raised + {fmtUSD(members_investors.sound_of_money_raised_usd)} + + + F1VISA-001 Pre-Orders + {members_investors.f1visa001_pre_orders} + + + + + + Updated just now + diff --git a/src/components/Footer.astro b/src/components/Footer.astro new file mode 100644 index 0000000..15380cb --- /dev/null +++ b/src/components/Footer.astro @@ -0,0 +1,14 @@ + diff --git a/src/components/GivingSection.astro b/src/components/GivingSection.astro new file mode 100644 index 0000000..9edc344 --- /dev/null +++ b/src/components/GivingSection.astro @@ -0,0 +1,67 @@ +--- +export interface Props { + giving: { open_source: { repos: number; pull_requests: number; contributors: number }; community: { total_members_all_channels: number }; ecosystem: { instances_deployed: number; members_onboarded: number }; }; + lastUpdated: string; +} +const { giving, lastUpdated } = Astro.props; +--- + + + + + Giving + + + + + π Open Source + + + + + Repos + {giving.open_source.repos} + + + Pull Requests + {giving.open_source.pull_requests} + + + Contributors + {giving.open_source.contributors} + + + + + + + π₯ Community + + + {giving.community.total_members_all_channels.toLocaleString()} + Total Members + + + + + + + + π Ecosystem + + + + + Instances Deployed + {giving.ecosystem.instances_deployed} + + + Members Onboarded + {giving.ecosystem.members_onboarded} + + + + + + Updated just now + diff --git a/src/components/Header.astro b/src/components/Header.astro new file mode 100644 index 0000000..9fc4149 --- /dev/null +++ b/src/components/Header.astro @@ -0,0 +1,51 @@ +--- +export interface Props { + dashboard: { name: string; domain: string; last_updated: string; version: string }; +} + +const { dashboard } = Astro.props; + +function formatRelativeTime(dateStr: string): string { + const date = new Date(dateStr); + const now = new Date(); + const diffMs = now.getTime() - date.getTime(); + const diffMin = Math.floor(diffMs / 60000); + if (diffMin < 1) return 'just now'; + if (diffMin < 60) return `${diffMin}m ago`; + const diffHrs = Math.floor(diffMin / 60); + if (diffHrs < 24) return `${diffHrs}h ago`; + const diffDays = Math.floor(diffHrs / 24); + return `${diffDays}d ago`; +} +--- + + + + + + F + {dashboard.name} + + + π Fun + Β· + π° Earning + Β· + π Learning + Β· + π«Ά Giving + + + + All Brands + β³ Refresh + Β· + + + {formatRelativeTime(dashboard.last_updated)} + + Β· + π + + + diff --git a/src/components/LearningSection.astro b/src/components/LearningSection.astro new file mode 100644 index 0000000..4c6a1f3 --- /dev/null +++ b/src/components/LearningSection.astro @@ -0,0 +1,41 @@ +--- +export interface Props { + learning: { lessons_counter: number; prds_completed: number; skills_modules: number; members_trained: number; new_this_week: number; }; + lastUpdated: string; +} +const { learning, lastUpdated } = Astro.props; +--- + + + + + Learning + + + + + + π Lessons + {learning.lessons_counter} + + + PRDs Completed + {learning.prds_completed} + + + Skills Modules + {learning.skills_modules} + + + Members Trained + {learning.members_trained} + + + New This Week + {learning.new_this_week} new + + + + + Updated just now + diff --git a/src/components/SocialSection.astro b/src/components/SocialSection.astro new file mode 100644 index 0000000..d504cd4 --- /dev/null +++ b/src/components/SocialSection.astro @@ -0,0 +1,79 @@ +--- +export interface Props { + social: { + youtube: { subscribers: number; views: number; watch_time_hours: number }; + twitter: { followers: number; impressions_monthly: number; engagement_rate: number }; + linkedin: { connections: number; post_reach_monthly: number }; + instagram: { followers: number; story_views_avg: number }; + signal_telegram: { members: number }; + email: { subscribers: number; open_rate: number; click_rate: number }; + }; + lastUpdated: string; +} +const { social, lastUpdated } = Astro.props; +function fmt(n: number): string { + if (n >= 1_000_000) return (n / 1_000_000).toFixed(1) + 'M'; + if (n >= 1_000) return (n / 1_000).toFixed(1) + 'K'; + return n.toLocaleString(); +} +--- + + + + + Social Media + live + + + + + + + Platform + Primary + Secondary + Details + + + + + πΉYouTube + {fmt(social.youtube.subscribers)} subs + {fmt(social.youtube.views)} views + {fmt(social.youtube.watch_time_hours)}h watch time + + + π¦X/Twitter + {fmt(social.twitter.followers)} followers + {social.twitter.engagement_rate}% eng. + {fmt(social.twitter.impressions_monthly)} monthly + + + πΌLinkedIn + {fmt(social.linkedin.connections)} conn. + {fmt(social.linkedin.post_reach_monthly)} reach + + + + π±Instagram + {fmt(social.instagram.followers)} followers + {fmt(social.instagram.story_views_avg)} story avg + + + + π¬Signal/Telegram + {social.signal_telegram.members.toLocaleString()} members + + + + + π§Email + {fmt(social.email.subscribers)} subs + {social.email.open_rate}% open + {social.email.click_rate}% click + + + + + Updated just now + diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro new file mode 100644 index 0000000..0c2186a --- /dev/null +++ b/src/layouts/Layout.astro @@ -0,0 +1,74 @@ +--- +import '../styles/global.css'; + +export interface Props { + title?: string; + description?: string; + ogTitle?: string; + ogDescription?: string; +} + +const { + title = 'FELG Ecosystem Dashboard', + description = 'βΎοΈ WeOwnNet π Ecosystem Dashboard', + ogTitle = title, + ogDescription = description, +} = Astro.props; +--- + + + + + + + + + + {title} + + + + + + + + + + + + + + + diff --git a/src/lib/data.ts b/src/lib/data.ts new file mode 100644 index 0000000..2dd235d --- /dev/null +++ b/src/lib/data.ts @@ -0,0 +1,14 @@ +import { supabase } from './supabase'; +import type { DashboardData } from './types'; +import { readFileSync } from 'fs'; +import { join } from 'path'; + +export async function getDashboard(): Promise { + if (supabase) { + const { data } = await supabase.from('dashboard_state').select('payload').eq('id', 'current').single(); + if (data?.payload) return data.payload as unknown as DashboardData; + } + // Fallback: read local JSON for dev/build without Supabase + const raw = readFileSync(join(process.cwd(), 'data', 'dashboard.json'), 'utf-8'); + return JSON.parse(raw); +} \ No newline at end of file diff --git a/src/lib/supabase.ts b/src/lib/supabase.ts new file mode 100644 index 0000000..bc5234f --- /dev/null +++ b/src/lib/supabase.ts @@ -0,0 +1,8 @@ +import { createClient } from '@supabase/supabase-js'; + +const url = import.meta.env.PUBLIC_SUPABASE_URL; +const key = import.meta.env.PUBLIC_SUPABASE_ANON_KEY; + +export const supabase = (url && key && !url.startsWith('http://placeholder') && !url.startsWith('https://placeholder')) + ? createClient(url, key) + : null; \ No newline at end of file diff --git a/src/lib/types.d.ts b/src/lib/types.d.ts new file mode 100644 index 0000000..6198b8f --- /dev/null +++ b/src/lib/types.d.ts @@ -0,0 +1,67 @@ +export interface Brand { + id: string; + name: string; + emoji: string; + status: 'live' | 'in_progress' | 'not_started' | 'paused'; + tagline: string; + category: string; + lead: string; + metric_label: string; + metric_value: number; + milestones: { label: string; status: 'done' | 'in_progress' | 'pending'; date?: string; target?: string }[]; + links: { website?: string; prd?: string; channel?: string; repo?: string }; + last_updated: string; +} + +export interface DashboardData { + dashboard: { + name: string; + domain: string; + last_updated: string; + version: string; + }; + brands: { + top_5_ids: string[]; + all_brands: Brand[]; + }; + fun: { + social_media: { + youtube: { subscribers: number; views: number; watch_time_hours: number }; + twitter: { followers: number; impressions_monthly: number; engagement_rate: number }; + linkedin: { connections: number; post_reach_monthly: number }; + instagram: { followers: number; story_views_avg: number }; + signal_telegram: { members: number }; + email: { subscribers: number; open_rate: number; click_rate: number }; + }; + }; + earning: { + pipeline: { + ships_in_flight: number; + sub_projects_complete: number; + sub_projects_total: number; + p0_blockers: number; + prds_drafted: number; + prds_in_progress: number; + w29_milestone_percent: number; + }; + members_investors: { + active_oca_members: number; + pre_launch_signups: number; + investor_pipeline: number; + sound_of_money_raised_usd: number; + f1visa001_pre_orders: number; + }; + }; + learning: { + lessons_counter: number; + prds_completed: number; + skills_modules: number; + members_trained: number; + new_this_week: number; + }; + giving: { + open_source: { repos: number; pull_requests: number; contributors: number }; + community: { total_members_all_channels: number }; + ecosystem: { instances_deployed: number; members_onboarded: number }; + }; +} \ No newline at end of file diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 0000000..f25f946 --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1,32 @@ +export function getStatusColor(status: string): string { + switch (status) { + case 'live': return '#22c55e'; + case 'in_progress': return '#eab308'; + case 'not_started': return '#75758f'; + case 'paused': return '#ef4444'; + default: return '#75758f'; + } +} + +export function getStatusLabel(status: string): string { + switch (status) { + case 'live': return 'Live'; + case 'in_progress': return 'In Progress'; + case 'not_started': return 'Not Started'; + case 'paused': return 'Paused'; + default: return status; + } +} + +export function formatRelativeTime(dateStr: string): string { + const date = new Date(dateStr); + const now = new Date(); + const diffMs = now.getTime() - date.getTime(); + const diffMin = Math.floor(diffMs / 60000); + if (diffMin < 1) return 'just now'; + if (diffMin < 60) return `${diffMin}m ago`; + const diffHrs = Math.floor(diffMin / 60); + if (diffHrs < 24) return `${diffHrs}h ago`; + const diffDays = Math.floor(diffHrs / 24); + return `${diffDays}d ago`; +} \ No newline at end of file diff --git a/src/pages/brand/[id].astro b/src/pages/brand/[id].astro new file mode 100644 index 0000000..92f0504 --- /dev/null +++ b/src/pages/brand/[id].astro @@ -0,0 +1,118 @@ +--- +import Layout from '../../layouts/Layout.astro'; +import Header from '../../components/Header.astro'; +import Footer from '../../components/Footer.astro'; +import { getDashboard } from '../../lib/data'; +import type { Brand } from '../../lib/types'; +import { getStatusColor, getStatusLabel, formatRelativeTime } from '../../lib/utils'; + +export async function getStaticPaths() { + const dashboard = await getDashboard(); + return dashboard.brands.all_brands.map((brand: Brand) => ({ + params: { id: brand.id }, + props: { brand, dashboardMeta: dashboard.dashboard }, + })); +} + +const { brand, dashboardMeta } = Astro.props; +const statusColor = getStatusColor(brand.status); +const statusLabel = getStatusLabel(brand.status); + +function fmt(n: number): string { + if (n >= 1_000_000) return (n / 1_000_000).toFixed(1) + 'M'; + if (n >= 1_000) return (n / 1_000).toFixed(1) + 'K'; + return n.toLocaleString(); +} +--- + + + + + + + + Dashboardβ + Brandsβ + {brand.name} + + + + + + {brand.emoji} + + {brand.name} + {brand.tagline} + + + + β {statusLabel} + {brand.lead} + + + + + + + {brand.metric_label} + {fmt(brand.metric_value)} + + + + Category + {brand.category} + + + + Lead + {brand.lead} + + + + + + + + + π Milestones + + + {brand.milestones.map((ms) => { + const dotColor = ms.status === 'done' ? '#22c55e' : ms.status === 'in_progress' ? '#eab308' : '#75758f'; + const label = ms.status === 'done' ? 'Done' : ms.status === 'in_progress' ? 'In Progress' : 'Pending'; + return ( + + + {ms.label} + {ms.date || ms.target || ''} + {label} + + ); + })} + + + + + + π Links + + {brand.links.website && π Website} + {brand.links.prd && π PRD} + {brand.links.channel && π¬ {brand.links.channel}} + {brand.links.repo && π¦ Repo} + + + + + Last updated: {formatRelativeTime(brand.last_updated)} + + + + + + diff --git a/src/pages/brands.astro b/src/pages/brands.astro new file mode 100644 index 0000000..36a8228 --- /dev/null +++ b/src/pages/brands.astro @@ -0,0 +1,60 @@ +--- +import Layout from '../layouts/Layout.astro'; +import Header from '../components/Header.astro'; +import Footer from '../components/Footer.astro'; +import BrandCard from '../components/BrandCard.astro'; +import { getDashboard } from '../lib/data'; +import type { Brand } from '../lib/types'; +import { getStatusColor, getStatusLabel } from '../lib/utils'; + +const dashboard = await getDashboard(); + +const groups: Record = { live: [], in_progress: [], not_started: [], paused: [] }; +for (const brand of dashboard.brands.all_brands) { + (groups[brand.status] || []).push(brand); +} +--- + + + + + + + + All Brands + {dashboard.brands.all_brands.length} total + + β Dashboard + + + {Object.entries(groups).filter(([, bs]) => bs.length > 0).map(([status, brands]) => { + const color = getStatusColor(status); + return ( + + + + {getStatusLabel(status)} + {brands.length} + + + {brands.map((b) => )} + + + ); + })} + + + + + + diff --git a/src/pages/index.astro b/src/pages/index.astro new file mode 100644 index 0000000..bbda573 --- /dev/null +++ b/src/pages/index.astro @@ -0,0 +1,77 @@ +--- +import Layout from '../layouts/Layout.astro'; +import Header from '../components/Header.astro'; +import Footer from '../components/Footer.astro'; +import BrandGrid from '../components/BrandGrid.astro'; +import SocialSection from '../components/SocialSection.astro'; +import EarningSection from '../components/EarningSection.astro'; +import LearningSection from '../components/LearningSection.astro'; +import GivingSection from '../components/GivingSection.astro'; +import { getDashboard } from '../lib/data'; +import type { Brand } from '../lib/types'; + +const dashboard = await getDashboard(); + +const topBrands: Brand[] = dashboard.brands.top_5_ids + .map((id: string) => dashboard.brands.all_brands.find((b: Brand) => b.id === id)!) + .filter(Boolean); +--- + + + + + + + + + Fun + β + Earning + β + Learning + β + Giving + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/scripts/live/client.ts b/src/scripts/live/client.ts new file mode 100644 index 0000000..070eb1e --- /dev/null +++ b/src/scripts/live/client.ts @@ -0,0 +1,158 @@ +/** + * Live-patch functions: each takes a slice of the realtime payload and updates + * the specific leaf DOM nodes tagged with a matching data-live attribute. + * + * IMPORTANT: these must only ever set textContent on LEAF nodes (the innermost + * wrapping a single value) β never on a container that also holds other + * data-live descendants. Setting textContent on a parent erases its children, + * which would silently break every nested patch that runs afterward. + * + * Structural changes (a brand added/removed, or moving between status groups + * on /brands) are not live-patched β those still require a page reload. That's + * an accepted MVP tradeoff, not an oversight. + */ + +function setLiveValue(path: string, value: string): void { + const el = document.querySelector(`[data-live="${path}"]`); + if (el) el.textContent = value; +} + +/** + * Patch brand metric values wherever a BrandCard is rendered + * (Top 5 grid on `/`, or the full grouped grid on `/brands`). + */ +export function patchBrandValue(allBrands: any[]): void { + if (!allBrands) return; + for (const brand of allBrands) { + setLiveValue(`brands.${brand.id}.metric_value`, brand.metric_value?.toLocaleString() ?? '0'); + } +} + +/** + * Patch social media metrics. + */ +export function patchSocial(social: any): void { + if (!social) return; + + if (social.youtube) { + setLiveValue('social.youtube.subscribers', formatNum(social.youtube.subscribers)); + setLiveValue('social.youtube.views', formatNum(social.youtube.views)); + } + + if (social.twitter) { + setLiveValue('social.twitter.followers', formatNum(social.twitter.followers)); + setLiveValue('social.twitter.engagement_rate', social.twitter.engagement_rate + '%'); + } + + if (social.linkedin) { + setLiveValue('social.linkedin.connections', formatNum(social.linkedin.connections)); + setLiveValue('social.linkedin.post_reach_monthly', formatNum(social.linkedin.post_reach_monthly)); + } + + if (social.instagram) { + setLiveValue('social.instagram.followers', formatNum(social.instagram.followers)); + setLiveValue('social.instagram.story_views_avg', formatNum(social.instagram.story_views_avg)); + } + + if (social.signal_telegram) { + setLiveValue('social.signal_telegram.members', social.signal_telegram.members?.toLocaleString() ?? '0'); + } + + if (social.email) { + setLiveValue('social.email.subscribers', formatNum(social.email.subscribers)); + setLiveValue('social.email.open_rate', social.email.open_rate + '%'); + setLiveValue('social.email.click_rate', social.email.click_rate + '%'); + } +} + +/** + * Patch earning section. + */ +export function patchEarning(earning: any): void { + if (!earning) return; + + if (earning.pipeline) { + setLiveValue('earning.pipeline.ships_in_flight', earning.pipeline.ships_in_flight?.toLocaleString() ?? '0'); + setLiveValue('earning.pipeline.sub_projects_complete', earning.pipeline.sub_projects_complete?.toLocaleString() ?? '0'); + setLiveValue('earning.pipeline.p0_blockers', earning.pipeline.p0_blockers?.toLocaleString() ?? '0'); + setLiveValue('earning.pipeline.prds_drafted', earning.pipeline.prds_drafted?.toLocaleString() ?? '0'); + setLiveValue('earning.pipeline.prds_in_progress', earning.pipeline.prds_in_progress?.toLocaleString() ?? '0'); + setLiveValue('earning.pipeline.w29_milestone_percent', earning.pipeline.w29_milestone_percent + '%'); + } + + if (earning.members_investors) { + setLiveValue('earning.members_investors.active_oca_members', earning.members_investors.active_oca_members?.toLocaleString() ?? '0'); + setLiveValue('earning.members_investors.pre_launch_signups', earning.members_investors.pre_launch_signups?.toLocaleString() ?? '0'); + setLiveValue('earning.members_investors.investor_pipeline', earning.members_investors.investor_pipeline?.toLocaleString() ?? '0'); + setLiveValue('earning.members_investors.sound_of_money_raised_usd', '$' + (earning.members_investors.sound_of_money_raised_usd?.toLocaleString() ?? '0')); + setLiveValue('earning.members_investors.f1visa001_pre_orders', earning.members_investors.f1visa001_pre_orders?.toLocaleString() ?? '0'); + } +} + +/** + * Patch learning section. + */ +export function patchLearning(learning: any): void { + if (!learning) return; + setLiveValue('learning.lessons_counter', learning.lessons_counter?.toLocaleString() ?? '0'); + setLiveValue('learning.prds_completed', learning.prds_completed?.toLocaleString() ?? '0'); + setLiveValue('learning.skills_modules', learning.skills_modules?.toLocaleString() ?? '0'); + setLiveValue('learning.members_trained', learning.members_trained?.toLocaleString() ?? '0'); + setLiveValue('learning.new_this_week', learning.new_this_week?.toLocaleString() ?? '0'); +} + +/** + * Patch giving section. + */ +export function patchGiving(giving: any): void { + if (!giving) return; + + if (giving.open_source) { + setLiveValue('giving.open_source.repos', giving.open_source.repos?.toLocaleString() ?? '0'); + setLiveValue('giving.open_source.pull_requests', giving.open_source.pull_requests?.toLocaleString() ?? '0'); + setLiveValue('giving.open_source.contributors', giving.open_source.contributors?.toLocaleString() ?? '0'); + } + + if (giving.community) { + setLiveValue('giving.community.total_members_all_channels', giving.community.total_members_all_channels?.toLocaleString() ?? '0'); + } + + if (giving.ecosystem) { + setLiveValue('giving.ecosystem.instances_deployed', giving.ecosystem.instances_deployed?.toLocaleString() ?? '0'); + setLiveValue('giving.ecosystem.members_onboarded', giving.ecosystem.members_onboarded?.toLocaleString() ?? '0'); + } +} + +/** + * Patch last-updated timestamps. + */ +export function patchLastUpdated(iso: string): void { + if (!iso) return; + const relative = formatRelativeTime(iso); + setLiveValue('last-updated', relative); + setLiveValue('social.last-updated', relative); + setLiveValue('earning.last-updated', relative); + setLiveValue('learning.last-updated', relative); + setLiveValue('giving.last-updated', relative); +} + +// --- Helpers --- + +function formatNum(n: number): string { + if (n >= 1_000_000) return (n / 1_000_000).toFixed(1) + 'M'; + if (n >= 1_000) return (n / 1_000).toFixed(1) + 'K'; + return n.toLocaleString(); +} + +function formatRelativeTime(dateStr: string): string { + const date = new Date(dateStr); + const now = new Date(); + const diffMs = now.getTime() - date.getTime(); + const diffMin = Math.floor(diffMs / 60000); + if (diffMin < 1) return 'just now'; + if (diffMin < 60) return `${diffMin}m ago`; + const diffHrs = Math.floor(diffMin / 60); + if (diffHrs < 24) return `${diffHrs}h ago`; + const diffDays = Math.floor(diffHrs / 24); + return `${diffDays}d ago`; +} diff --git a/src/styles/global.css b/src/styles/global.css new file mode 100644 index 0000000..2ea3322 --- /dev/null +++ b/src/styles/global.css @@ -0,0 +1,73 @@ +@import "tailwindcss"; + +@theme { + /* Page canvas β PRD Visual Design Principles: deep dark background */ + --color-canvas: #0a0a1a; + + /* Surfaces & borders (dark) */ + --color-border: #23233a; + --color-border-light: #1a1a2c; + --color-bg-muted: #13131f; + --color-bg-hover: #171725; + --color-bg-row-hover: #14141f; + + /* Text (dark) */ + --color-text: #e0e0ff; + --color-text-secondary: #a8a8c8; + --color-text-muted: #75758f; + --color-text-tertiary: #4a4a63; + + /* FELG pillar colors β PRD Visual Design Principles table. Do not drift from this. */ + --color-pillar-fun: #FF6B6B; + --color-pillar-earning: #FFD700; + --color-pillar-learning: #4ECDC4; + --color-pillar-giving: #95E1D3; + + /* Status colors (not PRD-specified β chosen for dark-bg contrast) */ + --color-status-live: #22c55e; + --color-status-progress: #eab308; + --color-status-not-started: #75758f; + --color-status-paused: #ef4444; + + /* Accent (links, interactive) */ + --color-accent: #7dabff; + --color-accent-light: rgba(125, 171, 255, 0.12); +} + +/* ββ Light Theme ββββββββββββββββββββββββββββββββββββββββ */ +[data-theme="light"] { + --color-canvas: #ffffff; + + --color-border: #e2e8f0; + --color-border-light: #f1f5f9; + --color-bg-muted: #f8f9fc; + --color-bg-hover: #fafbfc; + --color-bg-row-hover: #fafbfc; + + --color-text: #0f172a; + --color-text-secondary: #334155; + --color-text-muted: #64748b; + --color-text-tertiary: #94a3b8; + + /* Pillar colors stay the same β they're already vibrant enough on light bg */ + --color-pillar-fun: #ef4444; + --color-pillar-earning: #f59e0b; + --color-pillar-learning: #14b8a6; + --color-pillar-giving: #10b981; + + --color-status-live: #22c55e; + --color-status-progress: #eab308; + --color-status-not-started: #94a3b8; + --color-status-paused: #ef4444; + + --color-accent: #2563eb; + --color-accent-light: rgba(37, 99, 235, 0.12); +} + +/* ββ Smooth transitions on theme switch βββββββββββββββββ */ +html { + transition: background-color 0.2s ease, color 0.2s ease; +} +html * { + transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease; +} \ No newline at end of file diff --git a/supabase/README.md b/supabase/README.md new file mode 100644 index 0000000..f8bc4b9 --- /dev/null +++ b/supabase/README.md @@ -0,0 +1,21 @@ +# Supabase Setup + +## Provisioning + +1. Create a new Supabase project at https://supabase.com +2. Go to the SQL Editor and run `schema.sql` to create the table, RLS policies, and enable Realtime +3. In the Supabase Dashboard β Realtime, ensure `dashboard_state` is listed under "Replication" +4. Seed the initial data: + ```sql + INSERT INTO dashboard_state (id, payload) + VALUES ('current', '[PASTE_CONTENTS_OF_data/dashboard.json_HERE]'::jsonb); + ``` +5. Get your project URL and anon key from Settings β API +6. Set the following secrets in your GitHub repo: + - `PUBLIC_SUPABASE_URL` β your Supabase project URL + - `PUBLIC_SUPABASE_ANON_KEY` β the anon/public key (safe for client) + - `SUPABASE_SERVICE_ROLE_KEY` β the service-role key (secret, CI-only) + +## RLS Verified + +The anon role can only SELECT. Any INSERT, UPDATE, or DELETE from the browser will be rejected. \ No newline at end of file diff --git a/supabase/schema.sql b/supabase/schema.sql new file mode 100644 index 0000000..dce16de --- /dev/null +++ b/supabase/schema.sql @@ -0,0 +1,41 @@ +-- FELG Dashboard β Supabase Schema +-- One table, one row, one JSONB column +-- Amendment A2: Realtime replication enabled on this table + +CREATE TABLE IF NOT EXISTS dashboard_state ( + id TEXT PRIMARY KEY DEFAULT 'current', + payload JSONB NOT NULL, + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +-- Enable Row Level Security +ALTER TABLE dashboard_state ENABLE ROW LEVEL SECURITY; + +-- RLS: anon role = SELECT only (FR-66) +CREATE POLICY "anon_can_read_dashboard_state" + ON dashboard_state + FOR SELECT + TO anon + USING (true); + +-- RLS: explicitly deny all writes from anon +CREATE POLICY "anon_cannot_insert_dashboard_state" + ON dashboard_state + FOR INSERT + TO anon + WITH CHECK (false); + +CREATE POLICY "anon_cannot_update_dashboard_state" + ON dashboard_state + FOR UPDATE + TO anon + USING (false); + +CREATE POLICY "anon_cannot_delete_dashboard_state" + ON dashboard_state + FOR DELETE + TO anon + USING (false); + +-- Enable Realtime on this table +ALTER PUBLICATION supabase_realtime ADD TABLE dashboard_state; \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..8bf91d3 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "astro/tsconfigs/strict", + "include": [".astro/types.d.ts", "**/*"], + "exclude": ["dist"] +}
{brand.tagline}
Updated just now
Last updated: {formatRelativeTime(brand.last_updated)}
{dashboard.brands.all_brands.length} total