Zaparoo App is a mobile-first React 19 + TypeScript + Vite application for Zaparoo Core. It runs on web, iOS, and Android through Capacitor 8, and talks to Core over WebSocket JSON-RPC.
package.json is the source of truth for scripts. Common commands:
npm run dev— start the Vite dev server. Ask before running because it is long-lived.npm run dev:server— start dev mode withNODE_ENV=development; requiresDEV_SERVER_IPin.env. Ask before running because it is long-lived.npm run preview— preview a production build locally. Ask before running because it is long-lived.
npm run typecheck— TypeScript check withtsconfig.test.json.npm run lint— run ESLint.npm run lint:fix— run ESLint autofixes.npm run format:check— check Prettier formatting.npm run format— apply Prettier formatting.npm run test— run Vitest.npm run test:coverage— run Vitest with coverage.
npm run build— production build, then Capacitor sync.npm run build:web— web-only production build.npm run build:core— embedded Core build mode.npm run build:server— development server build, then Capacitor sync.npm run build:analyze— analyzer build.npm run sync— Capacitor sync only.npm run live-update— signed live update upload; requireslive-update-private.pem,LIVE_UPDATE_CHANNEL, andVITE_RELEASE_KEY.npx cap open ios/npx cap open android— open native projects.
src/
components/ React components
ui/ shadcn/ui Radix-based primitives
wui/ custom Zaparoo UI components
home/ home-page components
nfc/ NFC components
hooks/ custom React hooks
lib/ core utilities, stores, API, transport, crypto
routes/ TanStack Router file-based routes
translations/ i18next JSON files
__tests__/ unit, integration, and validation tests
test-utils/ render helpers, MSW handlers, factories
__mocks__/ Capacitor plugin mocks
docs/ focused guides for Capacitor, deployment, and testing
- Edit existing files when possible; do not create new files speculatively.
- Use the
@/alias for imports fromsrc/. - Use Capacitor
Preferencesfor persisted state; never uselocalStorage. - Log errors with
logger.error(msg, err, { category, action, severity }). - Add new UI strings to
src/translations/en-US.jsononly; other locales fall back. - Keep TypeScript strict. Do not use
any.
- Adding a Capacitor plugin, because it forces a native rebuild and store release.
- Bumping the app version; see
docs/deployment.mdbecause package, Android, iOS, What's New, and live update channels must stay in lockstep. - Refactors that touch many files or shared infrastructure.
- Force-pushes, branch deletes, force-resets, hook bypasses, or anything that overwrites others' work.
- Starting long-running local servers.
- Skip pre-commit hooks with
--no-verifyor commit secrets. - Dismiss lint, type, or test failures as pre-existing; fix them or report the blocker.
- Mock the component under test, build fake components inside test files, or use hardcoded delays. Use
findBy*andwaitForfor async UI. - Reconnect the WebSocket manually; the transport owns reconnect behavior.
- Test CSS classes; test accessible behavior instead.
- Primary app state lives in
useStatusStoreatsrc/lib/store.ts. - Persisted preferences and capability flags live in
usePreferencesStoreatsrc/lib/preferencesStore.ts. - Wait for
_hasHydratedbefore rendering UI that depends on persisted preferences. - Read
coreVersionfrom the store; do not pass it through props. runQueueis{ value: string; unsafe: boolean } | null.writeQueueis a plainstring.- For tests, reset stores with
useStatusStore.setState({ ... })inbeforeEach; there is nogetInitialState().
CoreAPIinsrc/lib/coreApi.tsis the JSON-RPC client over WebSocket. Check that file for the current method inventory.- Call
CoreAPI.reset()between tests. - Transport code in
src/lib/transport/owns auto-reconnect, heartbeat, and offline queueing. - Queues are processed by
useRunQueueProcessoranduseWriteQueueProcessor.
- Add feature gates to
FEATURE_GATESinsrc/lib/featureGates.tswithsince,marquee, andlabelKey. - Add the
labelKeytosrc/translations/en-US.jsonunderfeatures. - Wrap gated UI in
<GatedFeature featureId="...">or useuseCoreFeature(id). - Dev versions (
-dev,-rc,-beta,-alpha, or empty) pass all gates.
logger.log,logger.debug, andlogger.warnare dev-only.logger.erroralways logs and reports to Rollbar in native production builds.- Categories:
nfc,storage,purchase,api,camera,accelerometer,queue,connection,share,lifecycle,websocket,general. - Severities:
critical,error,warning,info,debug.
- Guard native-only calls with
Capacitor.isNativePlatform(). - Capability hooks hydrate cached flags in
usePreferencesStore; read those cached flags in components. - NFC operations should use high-level helpers from
src/lib/nfc.ts:readTag,writeTag,formatTag,eraseTag, andreadRaw. They wrap session lifecycle internally. - Use
useHaptics()for haptics. It respects user preferences, andwuicomponents trigger haptics through theirintentprop. - Use
safeInsetsfromuseStatusStorefor notch and cutout padding. - See
docs/capacitor.mdfor the full plugin list and platform patterns.
- Source of truth:
src/translations/en-US.json. - Spanish translations are available in
src/translations/es-ES.json; continue adding new UI strings toen-USonly. - Do not edit generated or fallback locale files unless explicitly requested.
- In tests,
t()returns the key. Assert against keys, not human strings.
- shadcn/ui components live under
src/components/ui/. - Custom Zaparoo components live under
src/components/wui/. Prefer them over inline JSX. - Before adding or changing UI, read
docs/design-language.md, then inspect the nearest sibling screens/components and copy their layout, typography, spacing, states, and component patterns exactly. - Do not invent new card styles, label treatments, badges, stat blocks, helper text, descriptions, icons, loading affordances, or other visual flair unless explicitly requested. If the existing design language does not cover the state, stop and ask before introducing a new visual pattern.
- Use
<EmptyState>for empty placeholders so they stay consistent. - Use
classnamesfor conditional Tailwind classes. - Use focus rings such as
focus-visible:ring-2 focus-visible:ring-white/50.
- Add
<SkipLink />at the top of new pages. - Use
usePageHeadingFocus()for page headings. - Use
<A11yAnnouncerProvider>anduseAnnouncer()for live announcements. - Use
useScreenReaderEnabled()for screen-reader-aware behavior. - Honor
textZoomLevelfromusePreferencesStore.
-
Vitest + React Testing Library + MSW are used for tests.
-
Import test helpers from
test-utils:import { render, screen, waitFor } from "@/test-utils";
-
Prefer integration tests over narrow unit tests when behavior crosses component or store boundaries.
-
Use the AAA pattern and
should + verbtest names. -
Use accessible queries in this order:
getByRole,getByLabelText,getByText, thengetByTestId. -
Use
userEvent.setup()for interactions. -
Use
findBy*andwaitForfor async behavior. -
Use factories from
src/test-utils/factories.ts, such asmockReaderInfo(). -
See
docs/testing.mdfor the full testing guide anddocs/playwright-testing.mdfor visual and end-to-end testing.
- Keep the summary concise.
- Do not include a separate test plan section.
- For a store release, follow the checklist in
docs/deployment.md, keep AndroidversionCodeand iOSCURRENT_PROJECT_VERSIONmatched, and keepsrc/__tests__/validation/release-config.test.tspassing.
docs/capacitor.md— Capacitor plugins, NFC, haptics, storage, and safe-area patterns.docs/deployment.md— live updates, store releases, version bumps, and Capawesome Cloud secrets.docs/testing.md— Testing Trophy, AAA, anti-patterns, and factories.docs/playwright-testing.md— visual and end-to-end test flows.