Skip to content

Apple Health upload flow in the new app (parser in a worker, preview, save) - #4

Open
Meerav29 wants to merge 16 commits into
masterfrom
feat/upload-flow
Open

Meerav29 wants to merge 16 commits into
masterfrom
feat/upload-flow

Conversation

@Meerav29

Copy link
Copy Markdown
Owner

Summary

Moves the Apple Health upload into the new app (/app/upload), per docs/superpowers/specs/2026-10-04-upload-flow-design.md.

  • Streaming parser in a Web Worker (web/src/lib/health/): parseExport turns UTF-8 chunks into per-day running totals (memory grows with days, not records); fileToChunks reads export.xml or streams the health XML out of export.zip with fflate (ZIP64 and data-descriptor ZIPs covered by fixtures). A parity test extracts the classic parseHealthXML from index.html and asserts identical daily/summary.
  • Upload page: guide + drop zone -> progress (Cancel) -> preview (cards, sparklines, replace warning) -> explicit Save -> saved. Specific error messages per failure; a failed save keeps the parsed result so Retry doesn't re-parse.
  • Saving: from the browser under RLS, replacing daily/summary and keeping the user's existing life events; never reads the demo row. Only aggregated daily numbers are stored; raw records never leave the browser.
  • Entry points: nav "Upload" link; dashboard empty state and sample-data banner link to /upload.
  • API: a Supabase outage now returns 503 instead of "No health data found" (so a user with data is never told they have none).
  • Intentional differences from the classic parser: ZIP accepted directly; no 600 MB cap; quote-aware tag tokenizer; new users start with no life events; Safari-safe sleep timestamp parsing.

Not verified yet: a real export in a real browser

Please run the acceptance checklist on the Vercel preview before merging (plan Task 12):

  1. Drop your real export.zip (and the unzipped export.xml) at /app/upload: the tab stays responsive, progress moves, and the days/averages match the classic app.
  2. Save, then open the dashboard: your data shows with no "sample data" banner. Upload again: the preview warns it replaces your data and keeps your events.
  3. A .txt file, an empty file, and a ZIP with no export.xml each give a specific message; Cancel mid-parse returns to the drop zone.
  4. The classic upload at / still works. If a large export fails, note the browser and tab memory.

Known follow-ups (deferred, not in this PR)

  • A valid export with >~20 trailing whitespace characters after </HealthData> is rejected as corrupt (Apple writes a single newline).
  • Out-of-memory while inflating a huge ZIP is still reported as "damaged or incomplete" (the ZIP reader wraps the error before the new worker-failed mapping).
  • An export whose only in-window records are sleep-in-bed / walking HR / respiratory rate saves an empty dashboard (same as classic).
  • Replace warning is omitted while the health query is pending/errored; reset/start during an in-flight save can be overwritten (unreachable via the UI).
  • Classic-app users keep the author's default semester events (life-event editing is out of scope).
  • Two commits lack a blank line before Co-Authored-By (squash-merge fixes it).

Test plan

  • python -m pytest -q (56 passed)
  • npm --prefix web test (225 passed), lint, typecheck, build (separate parse.worker-*.js chunk emitted under /app/assets)
  • Vercel preview checklist above

🤖 Generated with Claude Code

Meerav29 and others added 16 commits October 4, 2026 19:07
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Also adds @types/node and "node" in tsconfig types for node:fs/node:url in tests.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…sult types

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…-boundary tests

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ife events

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…nd single-flight save

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ror panels

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Remove aria-live from the whole section; add one polite status line
that changes only at coarse milestones.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ntry points

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…errors on event read

- extract toWorkerError; non-UploadError failures (OOM, unreadable file) are no longer "corrupt-file"
- loadOwnEvents maps expired JWTs to session-expired like the upsert
- worker-failed hint no longer promises a Try again button
- guard test: ErrorPanel never shows the raw error message

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…le wording

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ght and retention cutoff

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
somada Ready Ready Preview Oct 11, 2026 11:24pm UTC

This branch was successfully deployed

1 active deployment
Preview — fe96f5b8 Deployed Oct 11, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant