acl-recovery-tracker
tracks the recovery of your ACL when you've just had surgery
| Size | Folder | Inside |
|---|---|---|
| 192 kB | src/ | 26 source files · 3 hooks |
| 49.4 kB | public/ | 3 assets · 1 other file |
| 2.1 kB | .github/ | 2 CI files |
| 5.1 kB | supabase/ | 1 source file |
| 313 kB | (root) | 5 config files · 3 other files |
| Size | Path |
|---|---|
| 621 B | .env.example · binary |
| 66 B | .gitattributes |
| 805 B | .github/workflows/audit.yml |
| 1.3 kB | .github/workflows/deploy.yml |
| 374 B | .gitignore |
| 1.1 kB | LICENSE |
| 7 kB | README.md |
| 1.6 kB | eslint.config.js |
| 1.6 kB | index.html |
| 296 kB | package-lock.json |
| 1.1 kB | package.json |
| 1.3 kB | public/404.html |
| 588 B | public/favicon.svg |
| 12.2 kB | public/icons/icon-192x192.png · binary |
| 35.3 kB | public/icons/icon-512x512.png · binary |
| 1.3 kB | src/App.tsx |
| 6.9 kB | src/components/ExerciseCard.tsx |
| 1.6 kB | src/components/Layout.tsx |
| 4 kB | src/components/MilestoneTimeline.tsx |
| 2.4 kB | src/components/NavBar.tsx |
| 2.5 kB | src/components/PhaseIndicator.tsx |
| 2.1 kB | src/components/ProgressRing.tsx |
| 5 kB | src/components/Timer.tsx |
| 370 B | src/config.ts |
| 3.8 kB | src/context/SettingsProvider.tsx |
| 1.3 kB | src/context/settingsContext.ts |
| 4.1 kB | src/data/emsProtocol.ts |
| 1.9 kB | src/data/milestones.ts |
| 36.2 kB | src/data/recoveryPlan.ts |
| 9.5 kB | src/db/database.ts |
| 6.5 kB | src/hooks/useDailyExercises.ts |
| 1.6 kB | src/hooks/usePhase.ts |
| 6.5 kB | src/hooks/useTimer.ts |
| 7.9 kB | src/index.css |
| 472 B | src/main.tsx |
| 6.3 kB | src/pages/CheckIn.tsx |
| 2.2 kB | src/pages/CheckInHelpers.tsx |
| 28.1 kB | src/pages/Exercise.tsx |
| 6.6 kB | src/pages/Onboarding.tsx |
| 12.6 kB | src/pages/Progress.tsx |
| 9.3 kB | src/pages/Settings.tsx |
| 4.8 kB | src/pages/Timers.tsx |
| 13.8 kB | src/pages/Today.tsx |
| 2.2 kB | src/utils/date.ts |
| 317 B | src/vite-env.d.ts |
| 5.1 kB | supabase/schema.sql |
| 732 B | tsconfig.app.json |
| 119 B | tsconfig.json |
| 653 B | tsconfig.node.json |
| 1.7 kB | vite.config.ts |
Already bought this?
View your purchasesREADME
ACL Recovery Tracker
A progressive web app for tracking recovery after ACL reconstruction. It turns a surgery date into a week-by-week rehabilitation plan: the right exercises for your current phase, timers for ice and EMS sessions, weekly pain and range-of-motion check-ins, and a milestone timeline showing how far you've come.
Installs to the home screen on iOS and Android, works as a normal website everywhere else.
Version 1.0.3 · React 19 · TypeScript · Vite · Tailwind CSS 4 · Supabase
Not medical advice. This app follows a general ACL rehabilitation template. Recovery timelines vary by graft type, meniscus involvement and surgeon preference. Always follow the plan your surgeon and physiotherapist give you.
Features
| Phased plan | Five phases from Protection & Early Healing through Return to Activity, advancing automatically from your surgery date |
| Daily exercises | Grouped by strength, mobility, cardio, balance and recovery, with per-session tracking and a long-press shortcut to complete |
| Guided sessions | Step-by-step exercise player with set/rep counters, rest timers and audio cues |
| Timers | Presets for ice, EMS and collagen loading, with a screen wake lock so the display stays on |
| Weekly check-ins | Pain, swelling, extension and flexion, kept as a full history |
| Progress | Streaks, range-of-motion trend, milestone timeline and a browsable overview of the whole plan |
| EMS protocols | Per-phase electrode placement and stimulator settings |
| Offline-capable | Installable PWA with an auto-updating service worker |
Quick start
You'll need Node.js 20+ and a free Supabase account.
1. Install
git clone <your-repo-url>
cd acl-recovery-tracker
npm install
2. Create the database
- Create a new project at supabase.com/dashboard.
- Open SQL Editor → New query.
- Paste the contents of
supabase/schema.sqland click Run.
This creates four tables — exercise_logs, daily_logs, weekly_check_ins and user_settings — and enables row level security on each.
3. Configure
cp .env.example .env
Fill in the two values from Project Settings → Data API in your Supabase dashboard:
VITE_SUPABASE_URL=https://your-project-ref.supabase.co
VITE_SUPABASE_ANON_KEY=your-anon-public-key
4. Run
npm run dev
Open the URL it prints. On first launch you'll be asked for your name, surgery date and procedure — everything else is derived from those.
Deploying
The app is a static bundle, so any static host works. Set the two VITE_* environment variables in your host's dashboard before building.
GitHub Pages
A workflow is included at .github/workflows/deploy.yml.
- Settings → Pages → Source: GitHub Actions.
- Settings → Secrets and variables → Actions → Secrets: add
SUPABASE_URLandSUPABASE_ANON_KEY. - If you're deploying to a project site (
https://<user>.github.io/<repo>/), add a repository variable namedBASE_PATHset to/<repo>/.
Push to main and the workflow does the rest.
Netlify / Vercel / Cloudflare Pages
- Build command:
npm run build - Output directory:
dist - Environment variables:
VITE_SUPABASE_URL,VITE_SUPABASE_ANON_KEY
No BASE_PATH is needed — these serve from the root.
Security
Read this before sharing a deployment.
The app ships without authentication. It is built on the assumption that one deployment belongs to one person, which keeps setup to a single SQL paste and no login screen.
The consequence: the row level security policies in supabase/schema.sql grant full access to the anon role, so anyone who obtains your project URL and anon key can read and write your recovery data. Both values are embedded in the JavaScript bundle and are therefore visible to anyone who loads the page. For a personal deployment at an unlisted URL that is usually a reasonable trade-off — but it is a trade-off, and it is the wrong one if you plan to publish the link.
To make it genuinely multi-user:
- Enable an auth provider in Supabase → Authentication.
- Add
user_id uuid not null references auth.users default auth.uid()to each of the four tables. - Replace
using (true)in each policy withusing (auth.uid() = user_id). - Add a sign-in screen and gate the app on an active session.
Customising
Everything clinical lives in three plain data files — no database migration or component changes are needed to adapt the plan.
| File | Contents |
|---|---|
src/data/recoveryPlan.ts | Phase definitions (goals, cautions, week ranges) and every exercise: sets, reps, holds, frequency, instructions, progression notes |
src/data/milestones.ts | The milestone timeline and its target weeks |
src/data/emsProtocol.ts | Per-phase EMS settings and electrode placement |
Branding lives in src/config.ts (app name and tagline), vite.config.ts (PWA manifest) and src/index.css (the colour palette, defined as CSS custom properties on :root). Icons are in public/icons/.
If you are redistributing this as your own product, update the copyright holder in LICENSE to your own name or company.
Project structure
src/
├── components/ Reusable UI — cards, nav, timers, progress ring
├── config.ts App name, tagline, version
├── context/ Settings provider (profile + phase override)
├── data/ The recovery plan, milestones and EMS protocols
├── db/ Supabase client, types and queries
├── hooks/ usePhase, useDailyExercises, useTimer, useNotifications
├── pages/ Onboarding, Today, Exercise, Timers, Check-In, Progress, Settings
└── utils/ Shared date helpers
How the phase system works
usePhase() reads the surgery date from the settings context, converts it to a day count and then a week number, and looks up the matching phase in recoveryPlan.ts. useDailyExercises() filters exercises to that phase and merges them with today's logs. A user can override the phase manually from the Settings page when their physiotherapist moves them on early or holds them back.
Scripts
| Command | Description |
|---|---|
npm run dev | Start the dev server with hot reload |
npm run build | Type-check and build to dist/ |
npm run preview | Serve the production build locally |
npm run lint | Run ESLint |
npm run typecheck | Type-check without building |
Licence
MIT
Security scan report
Scanned by X402 Git, OSV (opens in a new tab) and Socket (opens in a new tab) — passed on all three. Findings from all three are listed below.
- secrets
- none found
- X402 Git
- size limits
- within caps
- X402 Git
- known malware
- none in 545 packages
- OSV
- known vulnerabilities
- 1 moderate in 545 packages — see below
- OSV
- dependency behaviour
- 11 direct packages declare network access, filesystem access, environment variables and more · 1,116 more in transitive packages · 43 packages flagged for review
- Socket
- instruction smuggling
- none found
- X402 Git
- what it can do
- listed below
- X402 Git
What this can do
- Tools
- none
- Runs
- .github/workflows/audit.yml (push, pull_request, schedule, workflow_dispatch)
- .github/workflows/deploy.yml (push, workflow_dispatch)
- Talks to
- eslint.org
- fonts.googleapis.com
- fonts.gstatic.com
- github.com
- opencollective.com
- registry.npmjs.org
- tidelift.com
- user.github.io
- your-name.github.io
- your-project-ref.supabase.co
- Reads
- SUPABASE_ANON_KEY
- SUPABASE_URL
- VITE_BASE_PATH
- VITE_SUPABASE_ANON_KEY
- VITE_SUPABASE_URL
- At install
- nothing runs
- Links to
- nodejs.org
- supabase.com
- your-project-ref.supabase.co
Findings (130)
- HIGHjson-schema 0.4.0 (dev only) — json-schema 0.4.0 (transitive, dev only) — contains an obfuscated file
- MODERATE@humanfs/node 0.16.7 — @humanfs/node@0.16.7 (npm): humanfs: Recursive copy follows symlinked files and copies data from outside the source tree GHSA-p498-v437-472g
- flagged for reviewreact-dom 19.2.4 — react-dom 19.2.4 (direct) — flagged for review by Socket's classifier as a security risk
- flagged for review@rollup/plugin-terser 1.0.0 (dev only) — @rollup/plugin-terser 1.0.0 (transitive, dev only) — flagged for review by Socket's classifier as a security risk
- MODERATEesbuild 0.28.2 (dev only) — esbuild 0.28.2 (transitive, dev only) — install scripts run when installed
- MODERATE@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — makes network requests at runtime
- MODERATE@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — makes network requests at runtime
- MODERATE@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — makes network requests at runtime
- MODERATE@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — makes network requests at runtime
- MODERATE@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — makes network requests at runtime
- MODERATE@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — makes network requests at runtime
- MODERATE@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — makes network requests at runtime
- MODERATE@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — makes network requests at runtime
- MODERATE@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — makes network requests at runtime
- MODERATE@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — makes network requests at runtime
- MODERATE@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — makes network requests at runtime
- MODERATE@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — makes network requests at runtime
- MODERATE@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — makes network requests at runtime
- MODERATE@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — makes network requests at runtime
- MODERATE@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — makes network requests at runtime
- MODERATE@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — makes network requests at runtime
- MODERATE@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — makes network requests at runtime
- MODERATE@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — makes network requests at runtime
- MODERATE@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — makes network requests at runtime
- MODERATE@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — makes network requests at runtime
- MODERATE@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — makes network requests at runtime
- MODERATE@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — makes network requests at runtime
- MODERATE@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — makes network requests at runtime
- MODERATE@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — makes network requests at runtime
- MODERATE@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — makes network requests at runtime
- MODERATE@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — makes network requests at runtime
- MODERATEvite 7.3.6 (dev only) — vite 7.3.6 (direct, dev only) — makes network requests at runtime
- MODERATEvite 7.3.6 (dev only) — vite 7.3.6 (direct, dev only) — makes network requests at runtime
- MODERATEvite 7.3.6 (dev only) — vite 7.3.6 (direct, dev only) — makes network requests at runtime
- MODERATEvite 7.3.6 (dev only) — vite 7.3.6 (direct, dev only) — evaluates code at runtime
- MODERATE.github/workflows/audit.yml — A GitHub Actions workflow runs on push, pull_request, schedule, workflow_dispatch
- MODERATE.github/workflows/deploy.yml — A GitHub Actions workflow runs on push, workflow_dispatch
- LOWeslint 9.39.3 (dev only) — eslint 9.39.3 (direct, dev only) — can attach a debugger
- LOWeslint 9.39.3 (dev only) — eslint 9.39.3 (direct, dev only) — can attach a debugger
- LOWvite 7.3.6 (dev only) — vite 7.3.6 (direct, dev only) — can attach a debugger
- LOWvite-plugin-pwa 1.2.0 (dev only) — vite-plugin-pwa 1.2.0 (direct, dev only) — can attach a debugger
- LOWeslint 9.39.3 (dev only) — eslint 9.39.3 (direct, dev only) — deprecated by its author
- LOWeslint 9.39.3 (dev only) — eslint 9.39.3 (direct, dev only) — loads modules by a computed name
- LOWeslint 9.39.3 (dev only) — eslint 9.39.3 (direct, dev only) — loads modules by a computed name
- LOWeslint 9.39.3 (dev only) — eslint 9.39.3 (direct, dev only) — loads modules by a computed name
- LOW.github/workflows/deploy.yml — Reads the environment variable SUPABASE_ANON_KEY
- LOW.github/workflows/deploy.yml — Reads the environment variable SUPABASE_URL
- LOW.github/workflows/deploy.yml — Reads the environment variable VITE_BASE_PATH
- LOW.github/workflows/deploy.yml — Reads the environment variable VITE_SUPABASE_ANON_KEY
- LOW.github/workflows/deploy.yml — Reads the environment variable VITE_SUPABASE_URL
- LOWeslint 9.39.3 (dev only) — eslint 9.39.3 (direct, dev only) — reads environment variables
- LOWeslint 9.39.3 (dev only) — eslint 9.39.3 (direct, dev only) — reads environment variables
- LOWeslint 9.39.3 (dev only) — eslint 9.39.3 (direct, dev only) — reads environment variables
- LOWeslint 9.39.3 (dev only) — eslint 9.39.3 (direct, dev only) — reads environment variables
- LOWeslint 9.39.3 (dev only) — eslint 9.39.3 (direct, dev only) — reads environment variables
- LOWeslint 9.39.3 (dev only) — eslint 9.39.3 (direct, dev only) — reads environment variables
- LOWeslint 9.39.3 (dev only) — eslint 9.39.3 (direct, dev only) — reads environment variables
- LOWeslint-plugin-react-hooks 7.0.1 (dev only) — eslint-plugin-react-hooks 7.0.1 (direct, dev only) — reads environment variables
- LOWreact 19.2.4 — react 19.2.4 (direct) — reads environment variables
- LOWreact 19.2.4 — react 19.2.4 (direct) — reads environment variables
- LOWreact-dom 19.2.4 — react-dom 19.2.4 (direct) — reads environment variables
- LOWreact-dom 19.2.4 — react-dom 19.2.4 (direct) — reads environment variables
- LOWreact-dom 19.2.4 — react-dom 19.2.4 (direct) — reads environment variables
- LOWvite 7.3.6 (dev only) — vite 7.3.6 (direct, dev only) — reads environment variables
- LOWvite-plugin-pwa 1.2.0 (dev only) — vite-plugin-pwa 1.2.0 (direct, dev only) — reads environment variables
- LOWvite-plugin-pwa 1.2.0 (dev only) — vite-plugin-pwa 1.2.0 (direct, dev only) — reads environment variables
- LOWvite-plugin-pwa 1.2.0 (dev only) — vite-plugin-pwa 1.2.0 (direct, dev only) — reads environment variables
- LOWvite-plugin-pwa 1.2.0 (dev only) — vite-plugin-pwa 1.2.0 (direct, dev only) — reads environment variables
- LOWvite-plugin-pwa 1.2.0 (dev only) — vite-plugin-pwa 1.2.0 (direct, dev only) — reads environment variables
- LOWvite-plugin-pwa 1.2.0 (dev only) — vite-plugin-pwa 1.2.0 (direct, dev only) — reads environment variables
- LOWvite-plugin-pwa 1.2.0 (dev only) — vite-plugin-pwa 1.2.0 (direct, dev only) — reads environment variables
- LOWvite-plugin-pwa 1.2.0 (dev only) — vite-plugin-pwa 1.2.0 (direct, dev only) — reads environment variables
- LOWeslint 9.39.3 (dev only) — eslint 9.39.3 (direct, dev only) — reads or writes files
- LOWeslint 9.39.3 (dev only) — eslint 9.39.3 (direct, dev only) — reads or writes files
- LOWeslint 9.39.3 (dev only) — eslint 9.39.3 (direct, dev only) — reads or writes files
- LOWeslint 9.39.3 (dev only) — eslint 9.39.3 (direct, dev only) — reads or writes files
- LOWeslint 9.39.3 (dev only) — eslint 9.39.3 (direct, dev only) — reads or writes files
- LOWeslint 9.39.3 (dev only) — eslint 9.39.3 (direct, dev only) — reads or writes files
- LOWeslint 9.39.3 (dev only) — eslint 9.39.3 (direct, dev only) — reads or writes files
- LOWeslint 9.39.3 (dev only) — eslint 9.39.3 (direct, dev only) — reads or writes files
- LOWeslint 9.39.3 (dev only) — eslint 9.39.3 (direct, dev only) — reads or writes files
- LOWvite 7.3.6 (dev only) — vite 7.3.6 (direct, dev only) — reads or writes files
- LOWvite-plugin-pwa 1.2.0 (dev only) — vite-plugin-pwa 1.2.0 (direct, dev only) — reads or writes files
- LOWvite-plugin-pwa 1.2.0 (dev only) — vite-plugin-pwa 1.2.0 (direct, dev only) — reads or writes files
- LOWvite-plugin-pwa 1.2.0 (dev only) — vite-plugin-pwa 1.2.0 (direct, dev only) — reads or writes files
- LOWvite-plugin-pwa 1.2.0 (dev only) — vite-plugin-pwa 1.2.0 (direct, dev only) — reads or writes files
- LOWvite-plugin-pwa 1.2.0 (dev only) — vite-plugin-pwa 1.2.0 (direct, dev only) — reads or writes files
- LOWvite-plugin-pwa 1.2.0 (dev only) — vite-plugin-pwa 1.2.0 (direct, dev only) — reads or writes files
- LOWvite-plugin-pwa 1.2.0 (dev only) — vite-plugin-pwa 1.2.0 (direct, dev only) — reads or writes files
- LOWvite-plugin-pwa 1.2.0 (dev only) — vite-plugin-pwa 1.2.0 (direct, dev only) — reads or writes files
- LOWvite-plugin-pwa 1.2.0 (dev only) — vite-plugin-pwa 1.2.0 (direct, dev only) — reads or writes files
- LOWvite-plugin-pwa 1.2.0 (dev only) — vite-plugin-pwa 1.2.0 (direct, dev only) — reads or writes files
- flagged for reviewvite 7.3.6 (dev only) — vite 7.3.6 (direct, dev only) — flagged for review by Socket's classifier as anomalous
- LOWvite 7.3.6 (dev only) — vite 7.3.6 (direct, dev only) — contains high-entropy strings
- LOWjiti 2.6.1 (dev only) — jiti 2.6.1 (transitive, dev only) — contains high-entropy strings
- LOWjiti 2.6.1 (dev only) — jiti 2.6.1 (transitive, dev only) — contains high-entropy strings
- LOWpackage-lock.json — Talks to eslint.org
- LOWindex.html — Talks to fonts.googleapis.com
- LOWindex.html — Talks to fonts.gstatic.com
- LOWpackage-lock.json — Talks to github.com
- LOWpackage-lock.json — Talks to opencollective.com
- LOWpackage-lock.json — Talks to registry.npmjs.org
- LOWpackage-lock.json — Talks to tidelift.com
- LOWvite.config.ts — Talks to user.github.io
- LOW.env.example — Talks to your-name.github.io
- LOW.env.example — Talks to your-project-ref.supabase.co
- LOW@supabase/supabase-js 2.98.0 — @supabase/supabase-js 2.98.0 (direct) — contains hard-coded URLs
- LOW@types/node 24.11.0 — @types/node 24.11.0 (direct) — contains hard-coded URLs
- LOW@vitejs/plugin-react 5.1.4 (dev only) — @vitejs/plugin-react 5.1.4 (direct, dev only) — contains hard-coded URLs
- LOWeslint 9.39.3 (dev only) — eslint 9.39.3 (direct, dev only) — contains hard-coded URLs
- LOWlucide-react 0.575.0 — lucide-react 0.575.0 (direct) — contains hard-coded URLs
- LOWreact 19.2.4 — react 19.2.4 (direct) — contains hard-coded URLs
- LOWreact-dom 19.2.4 — react-dom 19.2.4 (direct) — contains hard-coded URLs
- LOWtailwindcss 4.2.1 (dev only) — tailwindcss 4.2.1 (direct, dev only) — contains hard-coded URLs
- LOWvite 7.3.6 (dev only) — vite 7.3.6 (direct, dev only) — contains hard-coded URLs
- LOWvite-plugin-pwa 1.2.0 (dev only) — vite-plugin-pwa 1.2.0 (direct, dev only) — contains hard-coded URLs
- flagged for reviewgptAnomaly — 40 transitive packages, e.g. @babel/core@7.29.7, @babel/helper-module-imports@7.29.7, @babel/helper-module-transforms@7.29.7
- MODERATEusesEval — 23 transitive packages, e.g. @babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5, @babel/plugin-transform-async-generator-functions@7.29.0, @babel/plugin-transform-async-to-generator@7.28.6
- MODERATEnetworkAccess — 16 transitive packages, e.g. @supabase/auth-js@2.98.0, @supabase/functions-js@2.98.0, @supabase/postgrest-js@2.98.0
- MODERATEshellAccess — 9 transitive packages, e.g. @tailwindcss/oxide@4.2.1, commander@2.20.3, cross-spawn@7.0.6
- LOWurlStrings — 56 transitive packages, e.g. @babel/core@7.29.7, @babel/helper-create-class-features-plugin@7.28.6, @babel/helper-define-polyfill-provider@0.6.6
- LOWenvVars — 50 transitive packages, e.g. @babel/code-frame@7.29.7, @babel/core@7.29.7, @babel/helper-compilation-targets@7.29.7
- LOWfilesystemAccess — 40 transitive packages, e.g. @babel/core@7.29.7, @rollup/plugin-node-resolve@16.0.3, @tailwindcss/oxide@4.2.1
- LOWunmaintained — 40 transitive packages, e.g. acorn-jsx@5.3.2, at-least-node@1.0.0, buffer-from@1.1.2
- LOWdebugAccess — 12 transitive packages, e.g. @babel/core@7.29.7, @babel/helper-define-polyfill-provider@0.6.6, @humanwhocodes/module-importer@1.0.1
- LOWdynamicRequire — 10 transitive packages, e.g. @babel/helper-module-imports@7.29.7, @tailwindcss/oxide@4.2.1, browserslist@4.28.8
- LOWminifiedFile — 9 transitive packages, e.g. @isaacs/cliui@9.0.0, common-tags@1.8.2, esquery@1.7.0
- LOWnewAuthor — 4 transitive packages, e.g. async-function@1.0.0, convert-source-map@2.0.0, source-map@0.8.0-beta.0
- LOWtrivialPackage — 4 transitive packages, e.g. at-least-node@1.0.0, is-obj@1.0.1, is-regexp@1.0.0
- LOWdeprecated — 2 transitive packages, e.g. glob@11.1.0, source-map@0.8.0-beta.0
Releases 4
v1.0.32026-09-01
Version bump only. No application code, dependency or database schema change since v1.0.2 — the two trees are identical apart from the version string. If you are running v1.0.2, there is nothing to upgrade for.
This tag exists to give the fixed scanner an unburned version number to verify against. A scan verdict is terminal, so v1.0.2 cannot be re-scanned and stays failed.
Why v1.0.2 failed
Its 8 HIGH findings (1
react, 7vite) were artefacts of the scanner, not of this repository.Caret ranges in
package.jsoncould not be resolved to an exact version, so every dependency was queried twice — once pinned frompackage-lock.json, once versionless frompackage.json. Those dedupe to different keys, so both queries ran. A versionless OSV query is not a narrower question; it returns every advisory ever filed against the package at any version. One of the eight was filed against Vite 2, while this repo runs 7.3.6.Measured separately over the v1.0.2 tree:
Half Packages Vulnerabilities HIGH/CRITICAL Pinned 545 0 0 Versionless 20 26 8 All 8 blockers came from the phantom queries. No upgrade could clear them — which is why v1.0.2 failed on the same eight as v1.0.1, despite the commit in between genuinely fixing 15 real advisories.
The scanner-side fix (
dedupePackagesnow drops a versionless entry when the sameecosystem|nameis pinned elsewhere) lives in the scanner's own codebase, not in this repository.Verified on this tree
npm audit0 vulnerabilities ·tsc -bclean ·eslint0 errors · production build succeeds.Unchanged from v1.0.0
- The app ships without authentication. RLS is enabled but grants the
anonrole full access. See Security in the README. - MIT permits buyers to redistribute and resell the source.
- Not medical advice.
- The app ships without authentication. RLS is enabled but grants the
v1.0.22026-09-01
Fixes the dependency advisories that failed the v1.0.1 release scan. 15 HIGH vulnerabilities, now 0. No application code changed.
Vulnerabilities fixed
Package Affected Issue vite<=7.3.3Path traversal in optimized deps .maphandling;server.fs.denybypass via queries and Windows alternate paths; arbitrary file read via dev server WebSocketreact-router-dom<=7.14.1Vulnerable via react-routerws8.0.0–8.20.1Uninitialized memory disclosure; memory exhaustion DoS serialize-javascript<=7.0.4RCE via RegExp.flags; CPU exhaustion DoS. Transitive throughworkbox-build→@rollup/plugin-terserThe
viteandreact-router-domfloors were raised inpackage.json, not just pinned in the lockfile — otherwise regeneratingpackage-lock.jsoncould land back on a vulnerable version.Also checked
The scan could have failed on a secret or a size limit. Both were audited and were clean:
- Secrets — no
.envhas ever been committed,.env.examplecontains only placeholders, and no credential-shaped strings exist in any tracked file. - Size — 660 KB tracked in total, the largest file being the 292 KB lockfile.
Added
.github/workflows/audit.ymlrunsnpm audit --audit-level=highon every push and weekly, so new advisories surface immediately rather than at the next release scan. It is deliberately separate from the deploy workflow: a new advisory in a transitive dependency should report itself, not block a deploy of code that was already fine.Verified
npm ciclean ·tsc -bclean ·eslint0 errors · root and sub-path production builds both succeed.Unchanged from v1.0.0
- The app ships without authentication. RLS is enabled but grants the
anonrole full access. See Security in the README. - MIT permits buyers to redistribute and resell the source.
- Not medical advice.
- Secrets — no
v1.0.12026-09-01
A metadata release. No application code, dependencies or database schema changed since v1.0.0 — if you are already running it, there is nothing to upgrade for.
Changed
- LICENSE now names a real copyright holder. v1.0.0 shipped the placeholder
Copyright (c) 2026 ACL Recovery Tracker, left over from stripping the fork of personal details. It now readsCopyright (c) 2026 Paul Martin. - Version bumped to 1.0.1 in
package.json,package-lock.jsonand the README. The version is injected into the bundle at build time and shown on the Settings page, so all four now agree. - README tells redistributors to substitute their own name in the licence, rather than implying the field was left unset.
Still worth knowing
Unchanged from v1.0.0, and both still apply:
- The app ships without authentication. RLS is enabled but grants the
anonrole full access, and the anon key is visible in the JavaScript bundle — fine for one person at an unlisted URL, wrong if you publish the link. See Security in the README. - MIT permits buyers to redistribute and resell the source. If that is not what you want from a paid product, swap the licence before distributing.
- Not medical advice.
- LICENSE now names a real copyright holder. v1.0.0 shipped the placeholder
v1.0.02026-09-01
The first distributable release of ACL Recovery Tracker: a progressive web app that turns a surgery date into a week-by-week ACL rehabilitation plan.
What it does
- Phased plan — five phases from Protection & Early Healing through Return to Activity, advancing automatically from the user's surgery date, with a manual override for when a physio moves them on early.
- Daily exercises — grouped by strength, mobility, cardio, balance and recovery, with per-session tracking and long-press to complete.
- Guided sessions — step-by-step player with set/rep counters, rest timers and audio cues.
- Timers — ice, EMS and collagen presets, with a screen wake lock.
- Weekly check-ins — pain, swelling, extension and flexion, kept as history.
- Progress — streaks, range-of-motion trend, milestone timeline and a browsable overview of the whole plan.
- EMS protocols — per-phase electrode placement and stimulator settings.
- Installable PWA — works offline, auto-updating service worker.
Setting it up
Requires Node.js 20+ and a free Supabase project. Run
supabase/schema.sql, copy.env.exampleto.env, fill in the twoVITE_SUPABASE_*values, thennpm install && npm run dev. Full instructions, deployment guides and customisation notes are in the README.On first launch the app asks for a name, surgery date and procedure — nothing is hardcoded, and all three stay editable from Settings.
Please read before sharing a deployment
The app ships without authentication, on the assumption that one deployment belongs to one person. Row level security is enabled, but the policies grant the
anonrole full access, and the anon key is visible in the JavaScript bundle. Anyone who obtains your project URL and key can read and write the data.That is a reasonable trade-off for a personal deployment at an unlisted URL. It is the wrong one if you plan to publish the link. The Security section of the README sets out the four steps to make it genuinely multi-user.
Notes
- Licensed MIT. Update the copyright holder before distributing.
lucide-reactis pinned to^0.575.0; version0.577.0ships no type declarations and breaksnpm run build.- Not medical advice. Recovery timelines vary by graft type, meniscus involvement and surgeon preference.