@wazir-dev/cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +111 -0
- package/CHANGELOG.md +14 -0
- package/CONTRIBUTING.md +101 -0
- package/LICENSE +21 -0
- package/README.md +314 -0
- package/assets/composition-engine.mmd +34 -0
- package/assets/demo-script.sh +17 -0
- package/assets/logo-dark.svg +14 -0
- package/assets/logo.svg +14 -0
- package/assets/pipeline.mmd +39 -0
- package/assets/record-demo.sh +51 -0
- package/docs/README.md +51 -0
- package/docs/adapters/context-mode.md +60 -0
- package/docs/concepts/architecture.md +87 -0
- package/docs/concepts/artifact-model.md +60 -0
- package/docs/concepts/composition-engine.md +36 -0
- package/docs/concepts/indexing-and-recall.md +160 -0
- package/docs/concepts/observability.md +41 -0
- package/docs/concepts/roles-and-workflows.md +59 -0
- package/docs/concepts/terminology-policy.md +27 -0
- package/docs/getting-started/01-installation.md +78 -0
- package/docs/getting-started/02-first-run.md +102 -0
- package/docs/getting-started/03-adding-to-project.md +15 -0
- package/docs/getting-started/04-host-setup.md +15 -0
- package/docs/guides/ci-integration.md +15 -0
- package/docs/guides/creating-skills.md +15 -0
- package/docs/guides/expertise-module-authoring.md +15 -0
- package/docs/guides/hook-development.md +15 -0
- package/docs/guides/memory-and-learnings.md +34 -0
- package/docs/guides/multi-host-export.md +15 -0
- package/docs/guides/troubleshooting.md +101 -0
- package/docs/guides/writing-custom-roles.md +15 -0
- package/docs/plans/2026-03-15-cli-pipeline-integration-design.md +592 -0
- package/docs/plans/2026-03-15-cli-pipeline-integration-plan.md +598 -0
- package/docs/plans/2026-03-15-docs-enforcement-plan.md +238 -0
- package/docs/readmes/INDEX.md +99 -0
- package/docs/readmes/features/expertise/README.md +171 -0
- package/docs/readmes/features/exports/README.md +222 -0
- package/docs/readmes/features/hooks/README.md +103 -0
- package/docs/readmes/features/hooks/loop-cap-guard.md +133 -0
- package/docs/readmes/features/hooks/post-tool-capture.md +121 -0
- package/docs/readmes/features/hooks/post-tool-lint.md +130 -0
- package/docs/readmes/features/hooks/pre-compact-summary.md +122 -0
- package/docs/readmes/features/hooks/pre-tool-capture-route.md +100 -0
- package/docs/readmes/features/hooks/protected-path-write-guard.md +128 -0
- package/docs/readmes/features/hooks/session-start.md +119 -0
- package/docs/readmes/features/hooks/stop-handoff-harvest.md +125 -0
- package/docs/readmes/features/roles/README.md +157 -0
- package/docs/readmes/features/roles/clarifier.md +152 -0
- package/docs/readmes/features/roles/content-author.md +190 -0
- package/docs/readmes/features/roles/designer.md +193 -0
- package/docs/readmes/features/roles/executor.md +184 -0
- package/docs/readmes/features/roles/learner.md +210 -0
- package/docs/readmes/features/roles/planner.md +182 -0
- package/docs/readmes/features/roles/researcher.md +164 -0
- package/docs/readmes/features/roles/reviewer.md +184 -0
- package/docs/readmes/features/roles/specifier.md +162 -0
- package/docs/readmes/features/roles/verifier.md +215 -0
- package/docs/readmes/features/schemas/README.md +178 -0
- package/docs/readmes/features/skills/README.md +63 -0
- package/docs/readmes/features/skills/brainstorming.md +96 -0
- package/docs/readmes/features/skills/debugging.md +148 -0
- package/docs/readmes/features/skills/design.md +120 -0
- package/docs/readmes/features/skills/prepare-next.md +109 -0
- package/docs/readmes/features/skills/run-audit.md +159 -0
- package/docs/readmes/features/skills/scan-project.md +109 -0
- package/docs/readmes/features/skills/self-audit.md +176 -0
- package/docs/readmes/features/skills/tdd.md +137 -0
- package/docs/readmes/features/skills/using-skills.md +92 -0
- package/docs/readmes/features/skills/verification.md +120 -0
- package/docs/readmes/features/skills/writing-plans.md +104 -0
- package/docs/readmes/features/tooling/README.md +320 -0
- package/docs/readmes/features/workflows/README.md +186 -0
- package/docs/readmes/features/workflows/author.md +181 -0
- package/docs/readmes/features/workflows/clarify.md +154 -0
- package/docs/readmes/features/workflows/design-review.md +171 -0
- package/docs/readmes/features/workflows/design.md +169 -0
- package/docs/readmes/features/workflows/discover.md +162 -0
- package/docs/readmes/features/workflows/execute.md +173 -0
- package/docs/readmes/features/workflows/learn.md +167 -0
- package/docs/readmes/features/workflows/plan-review.md +165 -0
- package/docs/readmes/features/workflows/plan.md +170 -0
- package/docs/readmes/features/workflows/prepare-next.md +167 -0
- package/docs/readmes/features/workflows/review.md +169 -0
- package/docs/readmes/features/workflows/run-audit.md +191 -0
- package/docs/readmes/features/workflows/spec-challenge.md +159 -0
- package/docs/readmes/features/workflows/specify.md +160 -0
- package/docs/readmes/features/workflows/verify.md +177 -0
- package/docs/readmes/packages/README.md +50 -0
- package/docs/readmes/packages/ajv.md +117 -0
- package/docs/readmes/packages/context-mode.md +118 -0
- package/docs/readmes/packages/gray-matter.md +116 -0
- package/docs/readmes/packages/node-test.md +137 -0
- package/docs/readmes/packages/yaml.md +112 -0
- package/docs/reference/configuration-reference.md +159 -0
- package/docs/reference/expertise-index.md +52 -0
- package/docs/reference/git-flow.md +43 -0
- package/docs/reference/hooks.md +87 -0
- package/docs/reference/host-exports.md +50 -0
- package/docs/reference/launch-checklist.md +172 -0
- package/docs/reference/marketplace-listings.md +76 -0
- package/docs/reference/release-process.md +34 -0
- package/docs/reference/roles-reference.md +77 -0
- package/docs/reference/skills.md +33 -0
- package/docs/reference/templates.md +29 -0
- package/docs/reference/tooling-cli.md +94 -0
- package/docs/truth-claims.yaml +222 -0
- package/expertise/PROGRESS.md +63 -0
- package/expertise/README.md +18 -0
- package/expertise/antipatterns/PROGRESS.md +56 -0
- package/expertise/antipatterns/backend/api-design-antipatterns.md +1271 -0
- package/expertise/antipatterns/backend/auth-antipatterns.md +1195 -0
- package/expertise/antipatterns/backend/caching-antipatterns.md +622 -0
- package/expertise/antipatterns/backend/database-antipatterns.md +1038 -0
- package/expertise/antipatterns/backend/index.md +24 -0
- package/expertise/antipatterns/backend/microservices-antipatterns.md +850 -0
- package/expertise/antipatterns/code/architecture-antipatterns.md +919 -0
- package/expertise/antipatterns/code/async-antipatterns.md +622 -0
- package/expertise/antipatterns/code/code-smells.md +1186 -0
- package/expertise/antipatterns/code/dependency-antipatterns.md +1209 -0
- package/expertise/antipatterns/code/error-handling-antipatterns.md +1360 -0
- package/expertise/antipatterns/code/index.md +27 -0
- package/expertise/antipatterns/code/naming-and-abstraction.md +1118 -0
- package/expertise/antipatterns/code/state-management-antipatterns.md +1076 -0
- package/expertise/antipatterns/code/testing-antipatterns.md +1053 -0
- package/expertise/antipatterns/design/accessibility-antipatterns.md +1136 -0
- package/expertise/antipatterns/design/dark-patterns.md +1121 -0
- package/expertise/antipatterns/design/index.md +22 -0
- package/expertise/antipatterns/design/ui-antipatterns.md +1202 -0
- package/expertise/antipatterns/design/ux-antipatterns.md +680 -0
- package/expertise/antipatterns/frontend/css-layout-antipatterns.md +691 -0
- package/expertise/antipatterns/frontend/flutter-antipatterns.md +1827 -0
- package/expertise/antipatterns/frontend/index.md +23 -0
- package/expertise/antipatterns/frontend/mobile-antipatterns.md +573 -0
- package/expertise/antipatterns/frontend/react-antipatterns.md +1128 -0
- package/expertise/antipatterns/frontend/spa-antipatterns.md +1235 -0
- package/expertise/antipatterns/index.md +31 -0
- package/expertise/antipatterns/performance/index.md +20 -0
- package/expertise/antipatterns/performance/performance-antipatterns.md +1013 -0
- package/expertise/antipatterns/performance/premature-optimization.md +623 -0
- package/expertise/antipatterns/performance/scaling-antipatterns.md +785 -0
- package/expertise/antipatterns/process/ai-coding-antipatterns.md +853 -0
- package/expertise/antipatterns/process/code-review-antipatterns.md +656 -0
- package/expertise/antipatterns/process/deployment-antipatterns.md +920 -0
- package/expertise/antipatterns/process/index.md +23 -0
- package/expertise/antipatterns/process/technical-debt-antipatterns.md +647 -0
- package/expertise/antipatterns/security/index.md +20 -0
- package/expertise/antipatterns/security/secrets-antipatterns.md +849 -0
- package/expertise/antipatterns/security/security-theater.md +843 -0
- package/expertise/antipatterns/security/vulnerability-patterns.md +801 -0
- package/expertise/architecture/PROGRESS.md +70 -0
- package/expertise/architecture/data/caching-architecture.md +671 -0
- package/expertise/architecture/data/data-consistency.md +574 -0
- package/expertise/architecture/data/data-modeling.md +536 -0
- package/expertise/architecture/data/event-streams-and-queues.md +634 -0
- package/expertise/architecture/data/index.md +25 -0
- package/expertise/architecture/data/search-architecture.md +663 -0
- package/expertise/architecture/data/sql-vs-nosql.md +708 -0
- package/expertise/architecture/decisions/architecture-decision-records.md +640 -0
- package/expertise/architecture/decisions/build-vs-buy.md +616 -0
- package/expertise/architecture/decisions/index.md +23 -0
- package/expertise/architecture/decisions/monolith-to-microservices.md +790 -0
- package/expertise/architecture/decisions/technology-selection.md +616 -0
- package/expertise/architecture/distributed/cap-theorem-and-tradeoffs.md +800 -0
- package/expertise/architecture/distributed/circuit-breaker-bulkhead.md +741 -0
- package/expertise/architecture/distributed/consensus-and-coordination.md +796 -0
- package/expertise/architecture/distributed/distributed-systems-fundamentals.md +564 -0
- package/expertise/architecture/distributed/idempotency-and-retry.md +796 -0
- package/expertise/architecture/distributed/index.md +25 -0
- package/expertise/architecture/distributed/saga-pattern.md +797 -0
- package/expertise/architecture/foundations/architectural-thinking.md +460 -0
- package/expertise/architecture/foundations/coupling-and-cohesion.md +770 -0
- package/expertise/architecture/foundations/design-principles-solid.md +649 -0
- package/expertise/architecture/foundations/domain-driven-design.md +719 -0
- package/expertise/architecture/foundations/index.md +25 -0
- package/expertise/architecture/foundations/separation-of-concerns.md +472 -0
- package/expertise/architecture/foundations/twelve-factor-app.md +797 -0
- package/expertise/architecture/index.md +34 -0
- package/expertise/architecture/integration/api-design-graphql.md +638 -0
- package/expertise/architecture/integration/api-design-grpc.md +804 -0
- package/expertise/architecture/integration/api-design-rest.md +892 -0
- package/expertise/architecture/integration/index.md +25 -0
- package/expertise/architecture/integration/third-party-integration.md +795 -0
- package/expertise/architecture/integration/webhooks-and-callbacks.md +1152 -0
- package/expertise/architecture/integration/websockets-realtime.md +791 -0
- package/expertise/architecture/mobile-architecture/index.md +22 -0
- package/expertise/architecture/mobile-architecture/mobile-app-architecture.md +780 -0
- package/expertise/architecture/mobile-architecture/mobile-backend-for-frontend.md +670 -0
- package/expertise/architecture/mobile-architecture/offline-first.md +719 -0
- package/expertise/architecture/mobile-architecture/push-and-sync.md +782 -0
- package/expertise/architecture/patterns/cqrs-event-sourcing.md +717 -0
- package/expertise/architecture/patterns/event-driven.md +797 -0
- package/expertise/architecture/patterns/hexagonal-clean-architecture.md +870 -0
- package/expertise/architecture/patterns/index.md +27 -0
- package/expertise/architecture/patterns/layered-architecture.md +736 -0
- package/expertise/architecture/patterns/microservices.md +753 -0
- package/expertise/architecture/patterns/modular-monolith.md +692 -0
- package/expertise/architecture/patterns/monolith.md +626 -0
- package/expertise/architecture/patterns/plugin-architecture.md +735 -0
- package/expertise/architecture/patterns/serverless.md +780 -0
- package/expertise/architecture/scaling/database-scaling.md +615 -0
- package/expertise/architecture/scaling/feature-flags-and-rollouts.md +757 -0
- package/expertise/architecture/scaling/horizontal-vs-vertical.md +606 -0
- package/expertise/architecture/scaling/index.md +24 -0
- package/expertise/architecture/scaling/multi-tenancy.md +800 -0
- package/expertise/architecture/scaling/stateless-design.md +787 -0
- package/expertise/backend/embedded-firmware.md +625 -0
- package/expertise/backend/go.md +853 -0
- package/expertise/backend/index.md +24 -0
- package/expertise/backend/java-spring.md +448 -0
- package/expertise/backend/node-typescript.md +625 -0
- package/expertise/backend/python-fastapi.md +724 -0
- package/expertise/backend/rust.md +458 -0
- package/expertise/backend/solidity.md +711 -0
- package/expertise/composition-map.yaml +443 -0
- package/expertise/content/foundations/content-modeling.md +395 -0
- package/expertise/content/foundations/editorial-standards.md +449 -0
- package/expertise/content/foundations/index.md +24 -0
- package/expertise/content/foundations/microcopy.md +455 -0
- package/expertise/content/foundations/terminology-governance.md +509 -0
- package/expertise/content/index.md +34 -0
- package/expertise/content/patterns/accessibility-copy.md +518 -0
- package/expertise/content/patterns/index.md +24 -0
- package/expertise/content/patterns/notification-content.md +433 -0
- package/expertise/content/patterns/sample-content.md +486 -0
- package/expertise/content/patterns/state-copy.md +439 -0
- package/expertise/design/PROGRESS.md +58 -0
- package/expertise/design/disciplines/dark-mode-theming.md +577 -0
- package/expertise/design/disciplines/design-systems.md +595 -0
- package/expertise/design/disciplines/index.md +25 -0
- package/expertise/design/disciplines/information-architecture.md +800 -0
- package/expertise/design/disciplines/interaction-design.md +788 -0
- package/expertise/design/disciplines/responsive-design.md +552 -0
- package/expertise/design/disciplines/usability-testing.md +516 -0
- package/expertise/design/disciplines/user-research.md +792 -0
- package/expertise/design/foundations/accessibility-design.md +796 -0
- package/expertise/design/foundations/color-theory.md +797 -0
- package/expertise/design/foundations/iconography.md +795 -0
- package/expertise/design/foundations/index.md +26 -0
- package/expertise/design/foundations/motion-and-animation.md +653 -0
- package/expertise/design/foundations/rtl-design.md +585 -0
- package/expertise/design/foundations/spacing-and-layout.md +607 -0
- package/expertise/design/foundations/typography.md +800 -0
- package/expertise/design/foundations/visual-hierarchy.md +761 -0
- package/expertise/design/index.md +32 -0
- package/expertise/design/patterns/authentication-flows.md +474 -0
- package/expertise/design/patterns/content-consumption.md +789 -0
- package/expertise/design/patterns/data-display.md +618 -0
- package/expertise/design/patterns/e-commerce.md +1494 -0
- package/expertise/design/patterns/feedback-and-states.md +642 -0
- package/expertise/design/patterns/forms-and-input.md +819 -0
- package/expertise/design/patterns/gamification.md +801 -0
- package/expertise/design/patterns/index.md +31 -0
- package/expertise/design/patterns/microinteractions.md +449 -0
- package/expertise/design/patterns/navigation.md +800 -0
- package/expertise/design/patterns/notifications.md +705 -0
- package/expertise/design/patterns/onboarding.md +700 -0
- package/expertise/design/patterns/search-and-filter.md +601 -0
- package/expertise/design/patterns/settings-and-preferences.md +768 -0
- package/expertise/design/patterns/social-and-community.md +748 -0
- package/expertise/design/platforms/desktop-native.md +612 -0
- package/expertise/design/platforms/index.md +25 -0
- package/expertise/design/platforms/mobile-android.md +825 -0
- package/expertise/design/platforms/mobile-cross-platform.md +983 -0
- package/expertise/design/platforms/mobile-ios.md +699 -0
- package/expertise/design/platforms/tablet.md +794 -0
- package/expertise/design/platforms/web-dashboard.md +790 -0
- package/expertise/design/platforms/web-responsive.md +550 -0
- package/expertise/design/psychology/behavioral-nudges.md +449 -0
- package/expertise/design/psychology/cognitive-load.md +1191 -0
- package/expertise/design/psychology/error-psychology.md +778 -0
- package/expertise/design/psychology/index.md +22 -0
- package/expertise/design/psychology/persuasive-design.md +736 -0
- package/expertise/design/psychology/user-mental-models.md +623 -0
- package/expertise/design/tooling/open-pencil.md +266 -0
- package/expertise/frontend/angular.md +1073 -0
- package/expertise/frontend/desktop-electron.md +546 -0
- package/expertise/frontend/flutter.md +782 -0
- package/expertise/frontend/index.md +27 -0
- package/expertise/frontend/native-android.md +409 -0
- package/expertise/frontend/native-ios.md +490 -0
- package/expertise/frontend/react-native.md +1160 -0
- package/expertise/frontend/react.md +808 -0
- package/expertise/frontend/vue.md +1089 -0
- package/expertise/humanize/domain-rules-code.md +79 -0
- package/expertise/humanize/domain-rules-content.md +67 -0
- package/expertise/humanize/domain-rules-technical-docs.md +56 -0
- package/expertise/humanize/index.md +35 -0
- package/expertise/humanize/self-audit-checklist.md +87 -0
- package/expertise/humanize/sentence-patterns.md +218 -0
- package/expertise/humanize/vocabulary-blacklist.md +105 -0
- package/expertise/i18n/PROGRESS.md +65 -0
- package/expertise/i18n/advanced/accessibility-and-i18n.md +28 -0
- package/expertise/i18n/advanced/bidirectional-text-algorithm.md +38 -0
- package/expertise/i18n/advanced/complex-scripts.md +30 -0
- package/expertise/i18n/advanced/performance-and-i18n.md +27 -0
- package/expertise/i18n/advanced/testing-i18n.md +28 -0
- package/expertise/i18n/content/content-adaptation.md +23 -0
- package/expertise/i18n/content/locale-specific-formatting.md +23 -0
- package/expertise/i18n/content/machine-translation-integration.md +28 -0
- package/expertise/i18n/content/translation-management.md +29 -0
- package/expertise/i18n/foundations/date-time-calendars.md +67 -0
- package/expertise/i18n/foundations/i18n-architecture.md +272 -0
- package/expertise/i18n/foundations/locale-and-language-tags.md +79 -0
- package/expertise/i18n/foundations/numbers-currency-units.md +61 -0
- package/expertise/i18n/foundations/pluralization-and-gender.md +109 -0
- package/expertise/i18n/foundations/string-externalization.md +236 -0
- package/expertise/i18n/foundations/text-direction-bidi.md +241 -0
- package/expertise/i18n/foundations/unicode-and-encoding.md +86 -0
- package/expertise/i18n/index.md +38 -0
- package/expertise/i18n/platform/backend-i18n.md +31 -0
- package/expertise/i18n/platform/flutter-i18n.md +148 -0
- package/expertise/i18n/platform/native-android-i18n.md +36 -0
- package/expertise/i18n/platform/native-ios-i18n.md +36 -0
- package/expertise/i18n/platform/react-i18n.md +103 -0
- package/expertise/i18n/platform/web-css-i18n.md +81 -0
- package/expertise/i18n/rtl/arabic-specific.md +175 -0
- package/expertise/i18n/rtl/hebrew-specific.md +149 -0
- package/expertise/i18n/rtl/rtl-animations-and-transitions.md +111 -0
- package/expertise/i18n/rtl/rtl-forms-and-input.md +161 -0
- package/expertise/i18n/rtl/rtl-fundamentals.md +211 -0
- package/expertise/i18n/rtl/rtl-icons-and-images.md +181 -0
- package/expertise/i18n/rtl/rtl-layout-mirroring.md +252 -0
- package/expertise/i18n/rtl/rtl-navigation-and-gestures.md +107 -0
- package/expertise/i18n/rtl/rtl-testing-and-qa.md +147 -0
- package/expertise/i18n/rtl/rtl-typography.md +160 -0
- package/expertise/index.md +113 -0
- package/expertise/index.yaml +216 -0
- package/expertise/infrastructure/cloud-aws.md +597 -0
- package/expertise/infrastructure/cloud-gcp.md +599 -0
- package/expertise/infrastructure/cybersecurity.md +816 -0
- package/expertise/infrastructure/database-mongodb.md +447 -0
- package/expertise/infrastructure/database-postgres.md +400 -0
- package/expertise/infrastructure/devops-cicd.md +787 -0
- package/expertise/infrastructure/index.md +27 -0
- package/expertise/performance/PROGRESS.md +50 -0
- package/expertise/performance/backend/api-latency.md +1204 -0
- package/expertise/performance/backend/background-jobs.md +506 -0
- package/expertise/performance/backend/connection-pooling.md +1209 -0
- package/expertise/performance/backend/database-query-optimization.md +515 -0
- package/expertise/performance/backend/index.md +23 -0
- package/expertise/performance/backend/rate-limiting-and-throttling.md +971 -0
- package/expertise/performance/foundations/algorithmic-complexity.md +954 -0
- package/expertise/performance/foundations/caching-strategies.md +489 -0
- package/expertise/performance/foundations/concurrency-and-parallelism.md +847 -0
- package/expertise/performance/foundations/index.md +24 -0
- package/expertise/performance/foundations/measuring-and-profiling.md +440 -0
- package/expertise/performance/foundations/memory-management.md +964 -0
- package/expertise/performance/foundations/performance-budgets.md +1314 -0
- package/expertise/performance/index.md +31 -0
- package/expertise/performance/infrastructure/auto-scaling.md +1059 -0
- package/expertise/performance/infrastructure/cdn-and-edge.md +1081 -0
- package/expertise/performance/infrastructure/index.md +22 -0
- package/expertise/performance/infrastructure/load-balancing.md +1081 -0
- package/expertise/performance/infrastructure/observability.md +1079 -0
- package/expertise/performance/mobile/index.md +23 -0
- package/expertise/performance/mobile/mobile-animations.md +544 -0
- package/expertise/performance/mobile/mobile-memory-battery.md +416 -0
- package/expertise/performance/mobile/mobile-network.md +452 -0
- package/expertise/performance/mobile/mobile-rendering.md +599 -0
- package/expertise/performance/mobile/mobile-startup-time.md +505 -0
- package/expertise/performance/platform-specific/flutter-performance.md +647 -0
- package/expertise/performance/platform-specific/index.md +22 -0
- package/expertise/performance/platform-specific/node-performance.md +1307 -0
- package/expertise/performance/platform-specific/postgres-performance.md +1366 -0
- package/expertise/performance/platform-specific/react-performance.md +1403 -0
- package/expertise/performance/web/bundle-optimization.md +1239 -0
- package/expertise/performance/web/image-and-media.md +636 -0
- package/expertise/performance/web/index.md +24 -0
- package/expertise/performance/web/network-optimization.md +1133 -0
- package/expertise/performance/web/rendering-performance.md +1098 -0
- package/expertise/performance/web/ssr-and-hydration.md +918 -0
- package/expertise/performance/web/web-vitals.md +1374 -0
- package/expertise/quality/accessibility.md +985 -0
- package/expertise/quality/evidence-based-verification.md +499 -0
- package/expertise/quality/index.md +24 -0
- package/expertise/quality/ml-model-audit.md +614 -0
- package/expertise/quality/performance.md +600 -0
- package/expertise/quality/testing-api.md +891 -0
- package/expertise/quality/testing-mobile.md +496 -0
- package/expertise/quality/testing-web.md +849 -0
- package/expertise/security/PROGRESS.md +54 -0
- package/expertise/security/agentic-identity.md +540 -0
- package/expertise/security/compliance-frameworks.md +601 -0
- package/expertise/security/data/data-encryption.md +364 -0
- package/expertise/security/data/data-privacy-gdpr.md +692 -0
- package/expertise/security/data/database-security.md +1171 -0
- package/expertise/security/data/index.md +22 -0
- package/expertise/security/data/pii-handling.md +531 -0
- package/expertise/security/foundations/authentication.md +1041 -0
- package/expertise/security/foundations/authorization.md +603 -0
- package/expertise/security/foundations/cryptography.md +1001 -0
- package/expertise/security/foundations/index.md +25 -0
- package/expertise/security/foundations/owasp-top-10.md +1354 -0
- package/expertise/security/foundations/secrets-management.md +1217 -0
- package/expertise/security/foundations/secure-sdlc.md +700 -0
- package/expertise/security/foundations/supply-chain-security.md +698 -0
- package/expertise/security/index.md +31 -0
- package/expertise/security/infrastructure/cloud-security-aws.md +1296 -0
- package/expertise/security/infrastructure/cloud-security-gcp.md +1376 -0
- package/expertise/security/infrastructure/container-security.md +721 -0
- package/expertise/security/infrastructure/incident-response.md +1295 -0
- package/expertise/security/infrastructure/index.md +24 -0
- package/expertise/security/infrastructure/logging-and-monitoring.md +1618 -0
- package/expertise/security/infrastructure/network-security.md +1337 -0
- package/expertise/security/mobile/index.md +23 -0
- package/expertise/security/mobile/mobile-android-security.md +1218 -0
- package/expertise/security/mobile/mobile-binary-protection.md +1229 -0
- package/expertise/security/mobile/mobile-data-storage.md +1265 -0
- package/expertise/security/mobile/mobile-ios-security.md +1401 -0
- package/expertise/security/mobile/mobile-network-security.md +1520 -0
- package/expertise/security/smart-contract-security.md +594 -0
- package/expertise/security/testing/index.md +22 -0
- package/expertise/security/testing/penetration-testing.md +1258 -0
- package/expertise/security/testing/security-code-review.md +1765 -0
- package/expertise/security/testing/threat-modeling.md +1074 -0
- package/expertise/security/testing/vulnerability-scanning.md +1062 -0
- package/expertise/security/web/api-security.md +586 -0
- package/expertise/security/web/cors-and-headers.md +433 -0
- package/expertise/security/web/csrf.md +562 -0
- package/expertise/security/web/file-upload.md +1477 -0
- package/expertise/security/web/index.md +25 -0
- package/expertise/security/web/injection.md +1375 -0
- package/expertise/security/web/session-management.md +1101 -0
- package/expertise/security/web/xss.md +1158 -0
- package/exports/README.md +17 -0
- package/exports/hosts/claude/.claude/agents/clarifier.md +42 -0
- package/exports/hosts/claude/.claude/agents/content-author.md +63 -0
- package/exports/hosts/claude/.claude/agents/designer.md +55 -0
- package/exports/hosts/claude/.claude/agents/executor.md +55 -0
- package/exports/hosts/claude/.claude/agents/learner.md +51 -0
- package/exports/hosts/claude/.claude/agents/planner.md +53 -0
- package/exports/hosts/claude/.claude/agents/researcher.md +43 -0
- package/exports/hosts/claude/.claude/agents/reviewer.md +54 -0
- package/exports/hosts/claude/.claude/agents/specifier.md +47 -0
- package/exports/hosts/claude/.claude/agents/verifier.md +71 -0
- package/exports/hosts/claude/.claude/commands/author.md +42 -0
- package/exports/hosts/claude/.claude/commands/clarify.md +38 -0
- package/exports/hosts/claude/.claude/commands/design-review.md +46 -0
- package/exports/hosts/claude/.claude/commands/design.md +44 -0
- package/exports/hosts/claude/.claude/commands/discover.md +37 -0
- package/exports/hosts/claude/.claude/commands/execute.md +48 -0
- package/exports/hosts/claude/.claude/commands/learn.md +38 -0
- package/exports/hosts/claude/.claude/commands/plan-review.md +42 -0
- package/exports/hosts/claude/.claude/commands/plan.md +39 -0
- package/exports/hosts/claude/.claude/commands/prepare-next.md +37 -0
- package/exports/hosts/claude/.claude/commands/review.md +40 -0
- package/exports/hosts/claude/.claude/commands/run-audit.md +41 -0
- package/exports/hosts/claude/.claude/commands/spec-challenge.md +41 -0
- package/exports/hosts/claude/.claude/commands/specify.md +38 -0
- package/exports/hosts/claude/.claude/commands/verify.md +37 -0
- package/exports/hosts/claude/.claude/settings.json +34 -0
- package/exports/hosts/claude/CLAUDE.md +19 -0
- package/exports/hosts/claude/export.manifest.json +38 -0
- package/exports/hosts/claude/host-package.json +67 -0
- package/exports/hosts/codex/AGENTS.md +19 -0
- package/exports/hosts/codex/export.manifest.json +38 -0
- package/exports/hosts/codex/host-package.json +41 -0
- package/exports/hosts/cursor/.cursor/hooks.json +16 -0
- package/exports/hosts/cursor/.cursor/rules/wazir-core.mdc +19 -0
- package/exports/hosts/cursor/export.manifest.json +38 -0
- package/exports/hosts/cursor/host-package.json +42 -0
- package/exports/hosts/gemini/GEMINI.md +19 -0
- package/exports/hosts/gemini/export.manifest.json +38 -0
- package/exports/hosts/gemini/host-package.json +41 -0
- package/hooks/README.md +18 -0
- package/hooks/definitions/loop_cap_guard.yaml +21 -0
- package/hooks/definitions/post_tool_capture.yaml +24 -0
- package/hooks/definitions/pre_compact_summary.yaml +19 -0
- package/hooks/definitions/pre_tool_capture_route.yaml +19 -0
- package/hooks/definitions/protected_path_write_guard.yaml +19 -0
- package/hooks/definitions/session_start.yaml +19 -0
- package/hooks/definitions/stop_handoff_harvest.yaml +20 -0
- package/hooks/loop-cap-guard +17 -0
- package/hooks/post-tool-lint +36 -0
- package/hooks/protected-path-write-guard +17 -0
- package/hooks/session-start +41 -0
- package/llms-full.txt +2355 -0
- package/llms.txt +43 -0
- package/package.json +79 -0
- package/roles/README.md +20 -0
- package/roles/clarifier.md +42 -0
- package/roles/content-author.md +63 -0
- package/roles/designer.md +55 -0
- package/roles/executor.md +55 -0
- package/roles/learner.md +51 -0
- package/roles/planner.md +53 -0
- package/roles/researcher.md +43 -0
- package/roles/reviewer.md +54 -0
- package/roles/specifier.md +47 -0
- package/roles/verifier.md +71 -0
- package/schemas/README.md +24 -0
- package/schemas/accepted-learning.schema.json +20 -0
- package/schemas/author-artifact.schema.json +156 -0
- package/schemas/clarification.schema.json +19 -0
- package/schemas/design-artifact.schema.json +80 -0
- package/schemas/docs-claim.schema.json +18 -0
- package/schemas/export-manifest.schema.json +20 -0
- package/schemas/hook.schema.json +67 -0
- package/schemas/host-export-package.schema.json +18 -0
- package/schemas/implementation-plan.schema.json +19 -0
- package/schemas/proposed-learning.schema.json +19 -0
- package/schemas/research.schema.json +18 -0
- package/schemas/review.schema.json +29 -0
- package/schemas/run-manifest.schema.json +18 -0
- package/schemas/spec-challenge.schema.json +18 -0
- package/schemas/spec.schema.json +20 -0
- package/schemas/usage.schema.json +102 -0
- package/schemas/verification-proof.schema.json +29 -0
- package/schemas/wazir-manifest.schema.json +173 -0
- package/skills/README.md +40 -0
- package/skills/brainstorming/SKILL.md +77 -0
- package/skills/debugging/SKILL.md +50 -0
- package/skills/design/SKILL.md +61 -0
- package/skills/dispatching-parallel-agents/SKILL.md +128 -0
- package/skills/executing-plans/SKILL.md +70 -0
- package/skills/finishing-a-development-branch/SKILL.md +169 -0
- package/skills/humanize/SKILL.md +123 -0
- package/skills/init-pipeline/SKILL.md +124 -0
- package/skills/prepare-next/SKILL.md +20 -0
- package/skills/receiving-code-review/SKILL.md +123 -0
- package/skills/requesting-code-review/SKILL.md +105 -0
- package/skills/requesting-code-review/code-reviewer.md +108 -0
- package/skills/run-audit/SKILL.md +197 -0
- package/skills/scan-project/SKILL.md +41 -0
- package/skills/self-audit/SKILL.md +153 -0
- package/skills/subagent-driven-development/SKILL.md +154 -0
- package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -0
- package/skills/subagent-driven-development/implementer-prompt.md +102 -0
- package/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -0
- package/skills/tdd/SKILL.md +23 -0
- package/skills/using-git-worktrees/SKILL.md +163 -0
- package/skills/using-skills/SKILL.md +95 -0
- package/skills/verification/SKILL.md +22 -0
- package/skills/wazir/SKILL.md +463 -0
- package/skills/writing-plans/SKILL.md +30 -0
- package/skills/writing-skills/SKILL.md +157 -0
- package/skills/writing-skills/anthropic-best-practices.md +122 -0
- package/skills/writing-skills/persuasion-principles.md +50 -0
- package/templates/README.md +20 -0
- package/templates/artifacts/README.md +10 -0
- package/templates/artifacts/accepted-learning.md +19 -0
- package/templates/artifacts/accepted-learning.template.json +12 -0
- package/templates/artifacts/author.md +74 -0
- package/templates/artifacts/author.template.json +19 -0
- package/templates/artifacts/clarification.md +21 -0
- package/templates/artifacts/clarification.template.json +12 -0
- package/templates/artifacts/execute-notes.md +19 -0
- package/templates/artifacts/implementation-plan.md +21 -0
- package/templates/artifacts/implementation-plan.template.json +11 -0
- package/templates/artifacts/learning-proposal.md +19 -0
- package/templates/artifacts/next-run-handoff.md +21 -0
- package/templates/artifacts/plan-review.md +19 -0
- package/templates/artifacts/proposed-learning.template.json +12 -0
- package/templates/artifacts/research.md +21 -0
- package/templates/artifacts/research.template.json +12 -0
- package/templates/artifacts/review-findings.md +19 -0
- package/templates/artifacts/review.template.json +11 -0
- package/templates/artifacts/run-manifest.template.json +8 -0
- package/templates/artifacts/spec-challenge.md +19 -0
- package/templates/artifacts/spec-challenge.template.json +11 -0
- package/templates/artifacts/spec.md +21 -0
- package/templates/artifacts/spec.template.json +12 -0
- package/templates/artifacts/verification-proof.md +19 -0
- package/templates/artifacts/verification-proof.template.json +11 -0
- package/templates/examples/accepted-learning.example.json +14 -0
- package/templates/examples/author.example.json +152 -0
- package/templates/examples/clarification.example.json +15 -0
- package/templates/examples/docs-claim.example.json +8 -0
- package/templates/examples/export-manifest.example.json +7 -0
- package/templates/examples/host-export-package.example.json +11 -0
- package/templates/examples/implementation-plan.example.json +17 -0
- package/templates/examples/proposed-learning.example.json +13 -0
- package/templates/examples/research.example.json +15 -0
- package/templates/examples/research.example.md +6 -0
- package/templates/examples/review.example.json +17 -0
- package/templates/examples/run-manifest.example.json +9 -0
- package/templates/examples/spec-challenge.example.json +14 -0
- package/templates/examples/spec.example.json +21 -0
- package/templates/examples/verification-proof.example.json +21 -0
- package/templates/examples/wazir-manifest.example.yaml +65 -0
- package/templates/task-definition-schema.md +99 -0
- package/tooling/README.md +20 -0
- package/tooling/src/adapters/context-mode.js +50 -0
- package/tooling/src/capture/command.js +376 -0
- package/tooling/src/capture/store.js +99 -0
- package/tooling/src/capture/usage.js +270 -0
- package/tooling/src/checks/branches.js +50 -0
- package/tooling/src/checks/brand-truth.js +110 -0
- package/tooling/src/checks/changelog.js +231 -0
- package/tooling/src/checks/command-registry.js +36 -0
- package/tooling/src/checks/commits.js +102 -0
- package/tooling/src/checks/docs-drift.js +103 -0
- package/tooling/src/checks/docs-truth.js +201 -0
- package/tooling/src/checks/runtime-surface.js +156 -0
- package/tooling/src/cli.js +116 -0
- package/tooling/src/command-options.js +56 -0
- package/tooling/src/commands/validate.js +320 -0
- package/tooling/src/doctor/command.js +91 -0
- package/tooling/src/export/command.js +77 -0
- package/tooling/src/export/compiler.js +498 -0
- package/tooling/src/guards/loop-cap-guard.js +52 -0
- package/tooling/src/guards/protected-path-write-guard.js +67 -0
- package/tooling/src/index/command.js +152 -0
- package/tooling/src/index/storage.js +1061 -0
- package/tooling/src/index/summarizers.js +261 -0
- package/tooling/src/loaders.js +18 -0
- package/tooling/src/project-root.js +22 -0
- package/tooling/src/recall/command.js +225 -0
- package/tooling/src/schema-validator.js +30 -0
- package/tooling/src/state-root.js +40 -0
- package/tooling/src/status/command.js +71 -0
- package/wazir.manifest.yaml +135 -0
- package/workflows/README.md +19 -0
- package/workflows/author.md +42 -0
- package/workflows/clarify.md +38 -0
- package/workflows/design-review.md +46 -0
- package/workflows/design.md +44 -0
- package/workflows/discover.md +37 -0
- package/workflows/execute.md +48 -0
- package/workflows/learn.md +38 -0
- package/workflows/plan-review.md +42 -0
- package/workflows/plan.md +39 -0
- package/workflows/prepare-next.md +37 -0
- package/workflows/review.md +40 -0
- package/workflows/run-audit.md +41 -0
- package/workflows/spec-challenge.md +41 -0
- package/workflows/specify.md +38 -0
- package/workflows/verify.md +37 -0
|
@@ -0,0 +1,1160 @@
|
|
|
1
|
+
# React Native — Expertise Module
|
|
2
|
+
|
|
3
|
+
> A React Native specialist builds cross-platform iOS and Android applications using TypeScript
|
|
4
|
+
> and the React Native framework (currently React Native 0.79, shipped in Expo SDK 53). The scope
|
|
5
|
+
> covers Expo-managed workflows, TypeScript architecture, state management (TanStack Query v5 +
|
|
6
|
+
> Zustand v5), navigation (Expo Router v4), native module boundaries, performance tuning (Hermes,
|
|
7
|
+
> New Architecture, FlashList v2, Reanimated 4), testing (Jest + React Native Testing Library,
|
|
8
|
+
> Maestro/Detox), CI/CD (EAS Build + EAS Update + EAS Submit), and delivery to iOS App Store
|
|
9
|
+
> and Google Play.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Core Patterns & Conventions
|
|
14
|
+
|
|
15
|
+
### Project Structure
|
|
16
|
+
|
|
17
|
+
Use **feature-first** organization for apps beyond a single feature. Each feature owns its routes,
|
|
18
|
+
components, hooks, and store slices. Shared code lives in `components/`, `hooks/`, `services/`,
|
|
19
|
+
and `stores/`.
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
app/ # Expo Router v4 file-based routes
|
|
23
|
+
(auth)/ # Guarded Group — unauthenticated routes
|
|
24
|
+
login.tsx
|
|
25
|
+
register.tsx
|
|
26
|
+
(app)/ # Guarded Group — authenticated routes
|
|
27
|
+
_layout.tsx
|
|
28
|
+
index.tsx
|
|
29
|
+
profile.tsx
|
|
30
|
+
_layout.tsx # Root layout: providers + auth guard
|
|
31
|
+
components/
|
|
32
|
+
ui/ # Reusable presentational components
|
|
33
|
+
forms/
|
|
34
|
+
features/
|
|
35
|
+
auth/
|
|
36
|
+
hooks/
|
|
37
|
+
stores/
|
|
38
|
+
feed/
|
|
39
|
+
hooks/
|
|
40
|
+
stores/
|
|
41
|
+
hooks/ # Shared cross-feature hooks
|
|
42
|
+
services/ # API clients, native wrappers
|
|
43
|
+
stores/ # Zustand store slices
|
|
44
|
+
constants/ # SCREAMING_SNAKE values, theme tokens
|
|
45
|
+
.maestro/ # Maestro E2E flows
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Import `SafeAreaProvider` and `react-native-edge-to-edge` setup in the root `app/_layout.tsx`.
|
|
49
|
+
Wire `react-native-safe-area-context` for all inset-aware padding.
|
|
50
|
+
|
|
51
|
+
### Naming Conventions
|
|
52
|
+
|
|
53
|
+
| Element | Convention | Example |
|
|
54
|
+
|---------|-----------|---------|
|
|
55
|
+
| Files | `kebab-case` | `user-profile.tsx` |
|
|
56
|
+
| Components | `PascalCase` | `UserProfileCard` |
|
|
57
|
+
| Custom hooks | `use` prefix | `useAuthStore`, `useUserProfile` |
|
|
58
|
+
| Stores | camelCase + `Store` suffix | `authStore`, `feedStore` |
|
|
59
|
+
| Constants | `SCREAMING_SNAKE_CASE` | `MAX_RETRY_COUNT` |
|
|
60
|
+
| Types / Interfaces | `PascalCase` | `UserProfile`, `ApiResponse<T>` |
|
|
61
|
+
| Route files | `kebab-case` | `user-settings.tsx` |
|
|
62
|
+
| Test files | `*.test.tsx` | `user-profile.test.tsx` |
|
|
63
|
+
|
|
64
|
+
### Architecture Patterns
|
|
65
|
+
|
|
66
|
+
Follow a **feature-slice** architecture:
|
|
67
|
+
- **Screens** own composition only — no inline business logic.
|
|
68
|
+
- **Custom hooks** own data fetching (via TanStack Query) and derived state.
|
|
69
|
+
- **Zustand store slices** own persistent client state (auth tokens in memory, preferences, UI flags).
|
|
70
|
+
- **Services** own network calls and native module interactions.
|
|
71
|
+
- No God components. No prop drilling beyond two levels — pass a callback or use a store.
|
|
72
|
+
|
|
73
|
+
The three-tier state split is mandatory:
|
|
74
|
+
1. **Server state** — TanStack Query v5 (caching, sync, pagination, background refetch).
|
|
75
|
+
2. **App/client state** — Zustand v5 (auth session, user settings, cross-screen flags).
|
|
76
|
+
3. **Component-local state** — `useState` / `useReducer` (form fields, toggles, modal visibility).
|
|
77
|
+
|
|
78
|
+
### State Management
|
|
79
|
+
|
|
80
|
+
**TanStack Query v5** for all server-derived state:
|
|
81
|
+
- Unified object API: `useQuery({ queryKey: ['user', id], queryFn: fetchUser })`.
|
|
82
|
+
- **`onlineManager` wiring is required** — TanStack Query does not use `navigator.onLine` on React Native:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import NetInfo from '@react-native-community/netinfo';
|
|
86
|
+
import { onlineManager } from '@tanstack/react-query';
|
|
87
|
+
|
|
88
|
+
// Wire once at app startup (before QueryClientProvider renders)
|
|
89
|
+
onlineManager.setEventListener((setOnline) =>
|
|
90
|
+
NetInfo.addEventListener((state) => setOnline(!!state.isConnected))
|
|
91
|
+
);
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
- **`focusManager` wiring** for AppState foreground refetch:
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
import { AppState, AppStateStatus } from 'react-native';
|
|
98
|
+
import { focusManager } from '@tanstack/react-query';
|
|
99
|
+
|
|
100
|
+
AppState.addEventListener('change', (status: AppStateStatus) =>
|
|
101
|
+
focusManager.setFocused(status === 'active')
|
|
102
|
+
);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Zustand v5** for client/app state:
|
|
106
|
+
- **`useShallow` is mandatory for object selectors** — v5 crashes (not just re-renders) without it:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
import { useShallow } from 'zustand/react/shallow';
|
|
110
|
+
|
|
111
|
+
// WRONG in v5 — crashes with "Maximum update depth exceeded"
|
|
112
|
+
const { user, token } = useAuthStore((s) => ({ user: s.user, token: s.token }));
|
|
113
|
+
|
|
114
|
+
// CORRECT
|
|
115
|
+
const { user, token } = useAuthStore(useShallow((s) => ({ user: s.user, token: s.token })));
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
- Outside-React access via `store.getState()` — use in navigation guards, request interceptors, app init.
|
|
119
|
+
- Keep store state flat — avoid deeply nested objects for frequently-updated data.
|
|
120
|
+
|
|
121
|
+
### Routing & Navigation
|
|
122
|
+
|
|
123
|
+
**Expo Router v4** (bundled with Expo SDK 53) is the default. File-based routing with typed routes
|
|
124
|
+
generated automatically by Expo CLI.
|
|
125
|
+
|
|
126
|
+
- Use **Guarded Groups** for auth flows — the canonical pattern in v4. A `guard` function on the
|
|
127
|
+
group checks state synchronously and redirects to an anchor route. Never use `useEffect` + manual
|
|
128
|
+
`router.replace()` for auth redirects.
|
|
129
|
+
- Guards call `useAuthStore.getState()` (not hooks) because the guard function runs outside the
|
|
130
|
+
React render cycle.
|
|
131
|
+
- Use `<Link prefetch />` or the imperative prefetch API for performance-sensitive transitions.
|
|
132
|
+
- Use typed `href` values from `expo-router` — the compiler catches broken routes.
|
|
133
|
+
|
|
134
|
+
**React Navigation v7** is the escape hatch for legacy projects or when Expo Router's file-based
|
|
135
|
+
model cannot express the required navigation structure.
|
|
136
|
+
|
|
137
|
+
### Data Flow
|
|
138
|
+
|
|
139
|
+
Unidirectional data flow only:
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
API → TanStack Query cache → screen hook → component props
|
|
143
|
+
↑ |
|
|
144
|
+
└──────── mutations (invalidateQueries) ─┘
|
|
145
|
+
|
|
146
|
+
Cross-screen shared state: API → TanStack Query → Zustand action → component
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
No circular data flows. Screens never call services directly — they call hooks that call services.
|
|
150
|
+
|
|
151
|
+
### Error Handling
|
|
152
|
+
|
|
153
|
+
Define typed error states at every boundary:
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
// Narrow unknown errors to a displayable message
|
|
157
|
+
function toErrorMessage(err: unknown): string {
|
|
158
|
+
if (err instanceof Error) return err.message;
|
|
159
|
+
if (typeof err === 'string') return err;
|
|
160
|
+
return 'An unexpected error occurred';
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// TanStack Query exposes typed error state
|
|
164
|
+
const { data, error, isError } = useQuery({
|
|
165
|
+
queryKey: ['profile'],
|
|
166
|
+
queryFn: fetchProfile,
|
|
167
|
+
});
|
|
168
|
+
if (isError) return <ErrorScreen message={toErrorMessage(error)} />;
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
- Mount an `ErrorBoundary` at screen level to catch render errors.
|
|
172
|
+
- Use Zod to validate API response shapes at the boundary — parse, don't trust.
|
|
173
|
+
- Never let raw network errors surface to users without transformation.
|
|
174
|
+
|
|
175
|
+
### Logging & Observability
|
|
176
|
+
|
|
177
|
+
- **Sentry** (`@sentry/react-native`) for crash reporting and performance traces. Wire
|
|
178
|
+
`Sentry.init()` before `<App />` renders. Use `@sentry/expo` for Expo projects — it provides
|
|
179
|
+
automatic source map upload.
|
|
180
|
+
- **Firebase Crashlytics** as alternative for Firebase-first projects.
|
|
181
|
+
- Development: **Reactotron** for state/API inspection, or React Native Debugger.
|
|
182
|
+
- Never ship `console.log()` in production. Use a leveled logger that is a no-op in release builds.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Anti-Patterns & Pitfalls
|
|
187
|
+
|
|
188
|
+
### 1. Treating Server State as Client State
|
|
189
|
+
|
|
190
|
+
**Why it's a problem:** Fetching data from an API and storing it in Zustand or `useState` manually
|
|
191
|
+
means you own all the complexity TanStack Query already handles — caching, background refetch,
|
|
192
|
+
deduplication, stale-while-revalidate, error retries, and loading states. Duplication leads to
|
|
193
|
+
stale data, extra network calls, and cache inconsistency.
|
|
194
|
+
**Instead:** Use TanStack Query v5 for all server-derived state. Only put in Zustand what the API
|
|
195
|
+
does not own: session data, user preferences, UI state, transient cross-screen flags.
|
|
196
|
+
|
|
197
|
+
### 2. Overusing Context for High-Frequency State
|
|
198
|
+
|
|
199
|
+
**Why it's a problem:** React Context triggers a re-render of every consumer on every value change.
|
|
200
|
+
Using Context for state that changes frequently (auth token, theme, cart contents) causes
|
|
201
|
+
full-subtree re-renders, degrading performance on lower-end Android devices where frame budget is
|
|
202
|
+
already tight.
|
|
203
|
+
**Instead:** Use Zustand with granular selectors for shared high-frequency state. Context is
|
|
204
|
+
appropriate for static or rarely-changing values (locale, color scheme). For everything dynamic,
|
|
205
|
+
prefer Zustand + `useShallow`.
|
|
206
|
+
|
|
207
|
+
### 3. Blocking the JS Thread with Synchronous Work
|
|
208
|
+
|
|
209
|
+
**Why it's a problem:** React Native's JS thread is single-threaded. Heavy synchronous operations
|
|
210
|
+
(JSON parsing of large payloads, cryptographic operations, string processing loops) block the JS
|
|
211
|
+
thread and cause dropped frames. New Architecture JSI enables synchronous JS↔native calls —
|
|
212
|
+
but synchronous JS work still blocks the JS thread regardless.
|
|
213
|
+
**Instead:** Offload heavy computation to a Web Worker equivalent via `react-native-workers`, or
|
|
214
|
+
batch processing using `InteractionManager.runAfterInteractions()` to yield the thread after
|
|
215
|
+
animation frames complete. Use streaming JSON parsers for large payloads.
|
|
216
|
+
|
|
217
|
+
### 4. Using ScrollView for Large or Infinite Collections
|
|
218
|
+
|
|
219
|
+
**Why it's a problem:** `ScrollView` renders all children eagerly, regardless of visibility.
|
|
220
|
+
For lists with more than ~20 items, this causes slow initial render, high memory usage, and janky
|
|
221
|
+
scrolling. The problem scales directly with list size.
|
|
222
|
+
**Instead:** Use **FlashList v2** (requires New Architecture — React Native 0.76+ / Expo SDK 53+).
|
|
223
|
+
FlashList v2 is a pure-JS implementation that uses New Architecture's synchronous layout
|
|
224
|
+
measurement for pixel-perfect cell recycling with no `estimatedItemSize` required. For projects
|
|
225
|
+
still on Legacy Architecture, use **FlashList v1**. Never use `FlatList` for performance-sensitive
|
|
226
|
+
lists — FlashList is a drop-in replacement with significantly better throughput.
|
|
227
|
+
|
|
228
|
+
### 5. Ignoring Safe Areas, Insets, and Keyboard Avoidance
|
|
229
|
+
|
|
230
|
+
**Why it's a problem:** Ignoring safe areas causes content to appear behind notches, home
|
|
231
|
+
indicators, status bars, and the soft keyboard. Expo SDK 53 enables `react-native-edge-to-edge`
|
|
232
|
+
by default for new Android projects, making edge-to-edge the default layout model. Android 16
|
|
233
|
+
(API 36) makes edge-to-edge mandatory for all apps — opting out will not be possible.
|
|
234
|
+
**Instead:** Wrap the app root with `SafeAreaProvider` from `react-native-safe-area-context`.
|
|
235
|
+
Use `SafeAreaView` or `useSafeAreaInsets()` in layouts that need inset-aware padding. Install
|
|
236
|
+
`react-native-edge-to-edge` for correct edge-to-edge Android behavior. Use `KeyboardAvoidingView`
|
|
237
|
+
or `react-native-keyboard-controller` for forms.
|
|
238
|
+
|
|
239
|
+
### 6. Sprinkling Platform Checks Throughout UI
|
|
240
|
+
|
|
241
|
+
**Why it's a problem:** `Platform.OS === 'ios'` scattered through components creates invisible
|
|
242
|
+
platform-specific code paths, makes components harder to test, and accumulates into unmaintainable
|
|
243
|
+
conditionals over time.
|
|
244
|
+
**Instead:** Extract platform differences into dedicated abstraction layers. Use `Platform.select()`
|
|
245
|
+
for concise conditional values. Create platform-specific file variants (`Component.ios.tsx`,
|
|
246
|
+
`Component.android.tsx`) when the component differs substantially between platforms. Keep
|
|
247
|
+
components themselves platform-agnostic.
|
|
248
|
+
|
|
249
|
+
### 7. Storing Secrets in AsyncStorage
|
|
250
|
+
|
|
251
|
+
**Why it's a problem:** `AsyncStorage` is unencrypted plain text stored on the device filesystem.
|
|
252
|
+
Auth tokens, API keys, and session credentials stored there are trivially extractable via file
|
|
253
|
+
system access on rooted/jailbroken devices or via ADB on non-production builds.
|
|
254
|
+
**Instead:** Use `expo-secure-store` for Expo managed projects (backed by iOS Keychain Services
|
|
255
|
+
and Android Keystore AES encryption, 2048-byte limit per value on iOS). Use
|
|
256
|
+
`react-native-keychain` for bare React Native projects when biometric auth is needed. Store a
|
|
257
|
+
pointer — not the data itself — if the payload exceeds the size limit.
|
|
258
|
+
|
|
259
|
+
### 8. Leaking AppState, Navigation, and Native Event Subscriptions
|
|
260
|
+
|
|
261
|
+
**Why it's a problem:** Event listeners and subscriptions registered in `useEffect` without a
|
|
262
|
+
cleanup function leak memory across screen unmounts. On long user sessions, leaked subscriptions
|
|
263
|
+
cause duplicate callbacks, background battery drain, and eventual OOM on low-memory devices.
|
|
264
|
+
**Instead:** Always return a cleanup function from every `useEffect` that registers a subscription.
|
|
265
|
+
For AppState, `NetInfo`, and `Keyboard` listeners, call the returned `remove()` function or
|
|
266
|
+
`subscription.remove()` in the cleanup. For navigation event listeners, call `navigation.addListener`
|
|
267
|
+
and return the returned unsubscribe function.
|
|
268
|
+
|
|
269
|
+
### 9. Re-rendering Heavy Screens Without Memo Boundaries
|
|
270
|
+
|
|
271
|
+
**Why it's a problem:** A parent state change (e.g., scroll position, timer tick) propagates
|
|
272
|
+
through the component tree and re-renders every child that lacks a memo boundary. On screens with
|
|
273
|
+
complex lists, charts, or media, this causes frame drops.
|
|
274
|
+
**Instead:** Wrap expensive child components in `React.memo()`. Stabilize callback props with
|
|
275
|
+
`useCallback`. Stabilize object props with `useMemo`. Verify with React Native DevTools component
|
|
276
|
+
profiler before applying memos — over-memoization is also a performance cost.
|
|
277
|
+
|
|
278
|
+
### 10. Running Complex Animations on the JS Thread
|
|
279
|
+
|
|
280
|
+
**Why it's a problem:** `Animated` API (Legacy Architecture) runs animations through a JS→bridge
|
|
281
|
+
→native round-trip per frame. At 60fps this is 16ms — any JS jank drops the animation. Even with
|
|
282
|
+
`useNativeDriver: true`, the animation definition still originates on the JS thread.
|
|
283
|
+
**Instead:** Use **Reanimated 4** (requires New Architecture). Worklets declared with `'worklet'`
|
|
284
|
+
directive run entirely on the UI thread with no JS involvement per frame. Reanimated 4 also adds
|
|
285
|
+
CSS-style animations and transitions as a first-class API. For projects on Legacy Architecture,
|
|
286
|
+
use **Reanimated 3.x** — which supports worklets but requires the bridge for initial setup.
|
|
287
|
+
|
|
288
|
+
### 11. Mixing Expo-Managed and Bare-Native Assumptions
|
|
289
|
+
|
|
290
|
+
**Why it's a problem:** Expo-managed workflow hides `ios/` and `android/` directories. Bare
|
|
291
|
+
React Native exposes them. Packages that modify native files (e.g., add `AppDelegate.swift`
|
|
292
|
+
entries, modify `AndroidManifest.xml`) require either an Expo Config Plugin (managed) or manual
|
|
293
|
+
native edit (bare). Conflating the two leads to broken builds that work locally but fail on CI.
|
|
294
|
+
**Instead:** FlashList v2 and Reanimated 4 both require New Architecture — verify before adopting.
|
|
295
|
+
In Expo managed, use `app.config.js` plugins only. Run `npx expo prebuild` deliberately when
|
|
296
|
+
switching to bare. Never manually edit `ios/` and `android/` in a managed project.
|
|
297
|
+
|
|
298
|
+
### 12. Weak Offline, Loading, and Error State Design
|
|
299
|
+
|
|
300
|
+
**Why it's a problem:** Rendering nothing (blank screen) during loading, crashing on API errors,
|
|
301
|
+
and ignoring offline state are the most common RN UX failures. Mobile networks are unreliable;
|
|
302
|
+
offline is not an edge case.
|
|
303
|
+
**Instead:** Always handle three explicit states: `isLoading` → skeleton/shimmer, `isError` →
|
|
304
|
+
error screen with retry action, `isSuccess` → data. Wire TanStack Query's `onlineManager` so
|
|
305
|
+
queries automatically pause when offline and refetch on reconnect. Consider
|
|
306
|
+
`architecture/mobile-architecture/offline-first.md` for apps requiring local-first data.
|
|
307
|
+
|
|
308
|
+
### 13. Fragile Deep Linking and Push Navigation Wiring
|
|
309
|
+
|
|
310
|
+
**Why it's a problem:** Manually wiring deep links via `Linking.getInitialURL()` + AppState
|
|
311
|
+
listeners is error-prone, misses cold-start edge cases, and breaks when the app is killed.
|
|
312
|
+
Push notifications that navigate to content can land on wrong screens or fail silently.
|
|
313
|
+
**Instead:** Use Expo Router v4's built-in universal links support — file-based routes map
|
|
314
|
+
directly to URLs with zero configuration. For push notification navigation, use
|
|
315
|
+
`expo-notifications` `addNotificationResponseReceivedListener` and resolve the route before the
|
|
316
|
+
app finishes loading to avoid navigation timing bugs.
|
|
317
|
+
|
|
318
|
+
### 14. Profiling Only in Debug or Remote-Dev Mode
|
|
319
|
+
|
|
320
|
+
**Why it's a problem:** Debug builds with remote JS debugging enabled run JavaScript on the host
|
|
321
|
+
machine VM, not Hermes. Performance characteristics are completely different. Startup times,
|
|
322
|
+
memory usage, and animation smoothness in debug mode do not represent production behaviour.
|
|
323
|
+
**Instead:** Profile on a Release build using Hermes. Use the Hermes profiler for JS startup
|
|
324
|
+
traces. Use Xcode Instruments (iOS) and Android Studio Profiler (Android) for native-layer
|
|
325
|
+
profiling. Use React Native DevTools component profiler on a development build (not debug). Only
|
|
326
|
+
data from release-mode profiles is actionable.
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
## Testing Strategy
|
|
331
|
+
|
|
332
|
+
### Testing Pyramid
|
|
333
|
+
|
|
334
|
+
| Layer | Framework | Speed | Quantity | Purpose |
|
|
335
|
+
|-------|-----------|-------|----------|---------|
|
|
336
|
+
| Unit | Jest | ~ms | Many | Hooks, store actions, utilities, validators |
|
|
337
|
+
| Component | React Native Testing Library | ~100ms | Moderate | UI rendering, interactions, state transitions |
|
|
338
|
+
| Integration | Jest + MSW | ~seconds | Few | API integration, navigation flows, form submit |
|
|
339
|
+
| E2E | Maestro (default) / Detox | ~minutes | Critical paths only | Full user flows on real devices |
|
|
340
|
+
|
|
341
|
+
### Frameworks & Tools
|
|
342
|
+
|
|
343
|
+
- **Jest** — default test runner. Use `jest-expo` preset for Expo projects.
|
|
344
|
+
- **React Native Testing Library (RNTL)** — component tests. Prefer `getByRole` and `getByText`
|
|
345
|
+
queries over `testID` — they test behaviour, not implementation.
|
|
346
|
+
- **MSW (Mock Service Worker)** — intercept API calls in integration tests without mocking `fetch`.
|
|
347
|
+
Use `msw/node` server adapter for Jest.
|
|
348
|
+
- **Maestro** — recommended default for E2E. YAML-based, black-box, no app code changes required.
|
|
349
|
+
Test files live in `.maestro/` at project root. New projects, startups, and QA-driven teams
|
|
350
|
+
should default to Maestro.
|
|
351
|
+
- **Detox** — gray-box alternative. Communicates with the app process directly, enabling internal
|
|
352
|
+
state access. Use when you need to assert on React Native internals or your team already has
|
|
353
|
+
significant Detox investment.
|
|
354
|
+
|
|
355
|
+
### What to Test
|
|
356
|
+
|
|
357
|
+
**Always test:**
|
|
358
|
+
- Custom hooks (business logic, data transformations, derived state).
|
|
359
|
+
- Zustand store actions and selectors.
|
|
360
|
+
- Utility functions and validators (pure functions — trivially testable).
|
|
361
|
+
- Form validation schemas (Zod schemas, `@hookform/resolvers` integration).
|
|
362
|
+
- Navigation guards and auth redirect logic.
|
|
363
|
+
- Error boundary fallback rendering.
|
|
364
|
+
|
|
365
|
+
**Skip or deprioritize:**
|
|
366
|
+
- Rendering of third-party library components (e.g., testing that a `Button` renders text).
|
|
367
|
+
- Implementation details (internal state, private functions).
|
|
368
|
+
- Snapshot tests — they break on trivial UI changes and provide no behavioural signal.
|
|
369
|
+
- Platform-level rendering — covered by the React Native engine tests.
|
|
370
|
+
|
|
371
|
+
### Mocking Patterns
|
|
372
|
+
|
|
373
|
+
```ts
|
|
374
|
+
// jest.setup.ts — mock native modules that have no JS implementation in Jest
|
|
375
|
+
jest.mock('@react-native-community/netinfo', () => ({
|
|
376
|
+
addEventListener: jest.fn(() => jest.fn()),
|
|
377
|
+
fetch: jest.fn(() => Promise.resolve({ isConnected: true })),
|
|
378
|
+
}));
|
|
379
|
+
|
|
380
|
+
jest.mock('expo-secure-store', () => ({
|
|
381
|
+
getItemAsync: jest.fn(),
|
|
382
|
+
setItemAsync: jest.fn(),
|
|
383
|
+
deleteItemAsync: jest.fn(),
|
|
384
|
+
}));
|
|
385
|
+
|
|
386
|
+
jest.mock('@react-native-async-storage/async-storage', () =>
|
|
387
|
+
require('@react-native-async-storage/async-storage/jest/async-storage-mock')
|
|
388
|
+
);
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
Use MSW `http` handlers for API-level integration test mocking — avoids coupling tests to
|
|
392
|
+
`fetch` implementation details.
|
|
393
|
+
|
|
394
|
+
### Test File Organization
|
|
395
|
+
|
|
396
|
+
```
|
|
397
|
+
features/
|
|
398
|
+
auth/
|
|
399
|
+
hooks/
|
|
400
|
+
__tests__/
|
|
401
|
+
use-auth.test.ts
|
|
402
|
+
stores/
|
|
403
|
+
__tests__/
|
|
404
|
+
auth-store.test.ts
|
|
405
|
+
components/
|
|
406
|
+
__tests__/
|
|
407
|
+
login-form.test.tsx
|
|
408
|
+
.maestro/
|
|
409
|
+
login_flow.yaml
|
|
410
|
+
checkout_flow.yaml
|
|
411
|
+
jest.config.ts
|
|
412
|
+
jest.setup.ts
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
Co-locate `__tests__/` directories next to source files. E2E flows in `.maestro/` at project root.
|
|
416
|
+
|
|
417
|
+
### Coverage Expectations
|
|
418
|
+
|
|
419
|
+
- **Business logic** (hooks, stores, utilities): 80%+ statement coverage.
|
|
420
|
+
- **UI-only components** (no logic): no coverage target — component tests at this layer test
|
|
421
|
+
rendering, not coverage.
|
|
422
|
+
- Enforce thresholds in `jest.config.ts`:
|
|
423
|
+
|
|
424
|
+
```ts
|
|
425
|
+
// jest.config.ts
|
|
426
|
+
export default {
|
|
427
|
+
preset: 'jest-expo',
|
|
428
|
+
coverageThreshold: {
|
|
429
|
+
'./features/**/*.ts': { statements: 80 },
|
|
430
|
+
},
|
|
431
|
+
};
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
## Performance Considerations
|
|
437
|
+
|
|
438
|
+
### Common Bottlenecks
|
|
439
|
+
|
|
440
|
+
1. **JS thread blocking** — synchronous heavy computation, large JSON parsing on the main thread.
|
|
441
|
+
2. **Large list rendering** — using `ScrollView` or `FlatList` for lists of 50+ items; no cell
|
|
442
|
+
recycling; re-rendering all rows on parent state change.
|
|
443
|
+
3. **Missing memo boundaries** — heavy screen children re-rendering on unrelated state changes.
|
|
444
|
+
4. **Unoptimized images** — full-resolution downloads without caching; no decode-size hints.
|
|
445
|
+
5. **Heavy synchronous module imports** — large dependencies imported at the top-level slow Hermes
|
|
446
|
+
startup; use `require()` inside functions for lazy loading.
|
|
447
|
+
6. **Bridge churn (Legacy Architecture only)** — high-frequency JS↔native data transfer across
|
|
448
|
+
the async bridge. Eliminated by JSI in New Architecture.
|
|
449
|
+
|
|
450
|
+
### Profiling Tools
|
|
451
|
+
|
|
452
|
+
| Tool | What It Measures | When to Use |
|
|
453
|
+
|------|-----------------|-------------|
|
|
454
|
+
| React Native DevTools (component profiler) | Component render count and timing | Find unnecessary re-renders |
|
|
455
|
+
| Flipper | Network requests, layout hierarchy, crashes | General inspection during development |
|
|
456
|
+
| Reactotron | State changes, API calls, async storage | Development-time state/API debugging |
|
|
457
|
+
| Hermes profiler | JS startup trace, function execution time | Slow cold start investigation |
|
|
458
|
+
| Xcode Instruments | CPU, memory, energy impact (iOS) | Native-layer profiling on iOS |
|
|
459
|
+
| Android Studio Profiler | CPU, memory, network, battery (Android) | Native-layer profiling on Android |
|
|
460
|
+
|
|
461
|
+
**Always profile on a Release build.** Debug builds with JS remote debugging run on the host VM,
|
|
462
|
+
not Hermes. Startup times and animation smoothness differ dramatically.
|
|
463
|
+
|
|
464
|
+
### Optimization Patterns
|
|
465
|
+
|
|
466
|
+
- **Hermes AOT compilation** — default since React Native 0.70+. Pre-compiles JS to bytecode at
|
|
467
|
+
build time, reducing startup time and memory usage.
|
|
468
|
+
- **New Architecture JSI** — eliminates bridge JSON serialization for JS↔native calls. Enables
|
|
469
|
+
synchronous native calls. Default in React Native 0.76+ and Expo SDK 53+.
|
|
470
|
+
- **FlashList v2** — cell recycling + adaptive buffer (adjusts based on scroll velocity).
|
|
471
|
+
Requires New Architecture. No `estimatedItemSize` needed. Drop-in `FlatList` replacement with
|
|
472
|
+
significantly lower per-frame render cost for complex items.
|
|
473
|
+
- **Reanimated 4 worklets** — animations run entirely on the UI thread. Declare animation logic
|
|
474
|
+
with `'worklet'` directive; it never touches the JS thread per frame.
|
|
475
|
+
- **`React.memo` + `useCallback`** — wrap list row components in `React.memo`; stabilize callbacks
|
|
476
|
+
passed to rows with `useCallback` to prevent unnecessary row re-renders.
|
|
477
|
+
- **Lazy-load heavy screens** — use `React.lazy()` + `Suspense` or route-level lazy loading in
|
|
478
|
+
Expo Router to defer loading large screen bundles.
|
|
479
|
+
- **`metro.config.js` bundle splitting** — use `createDevServerMiddleware` and `unstable_enablePackageExports`
|
|
480
|
+
for tree-shaking; consider `@expo/metro-config` async bundles for tab-based apps.
|
|
481
|
+
- **Edge-to-edge** — Expo SDK 53 enables `react-native-edge-to-edge` by default for new Android
|
|
482
|
+
projects. Use `react-native-safe-area-context` for inset-aware layouts. Android 16 (API 36)
|
|
483
|
+
makes edge-to-edge mandatory.
|
|
484
|
+
|
|
485
|
+
### Metrics & Thresholds
|
|
486
|
+
|
|
487
|
+
| Metric | Target |
|
|
488
|
+
|--------|--------|
|
|
489
|
+
| TTI — cold start (mid-range device) | < 2 seconds |
|
|
490
|
+
| List scroll frame drops | < 2 per 100 frames |
|
|
491
|
+
| JS bundle size | < 1 MB (warn above 2 MB) |
|
|
492
|
+
| Crash-free sessions | > 99.5% |
|
|
493
|
+
| Memory (idle, mid-range device) | < 200 MB |
|
|
494
|
+
|
|
495
|
+
---
|
|
496
|
+
|
|
497
|
+
## Security Considerations
|
|
498
|
+
|
|
499
|
+
### Secure Storage
|
|
500
|
+
|
|
501
|
+
Never store secrets, tokens, or credentials in `AsyncStorage` — it is unencrypted plain text on
|
|
502
|
+
the device filesystem.
|
|
503
|
+
|
|
504
|
+
| Library | Workflow | Backing | Limit | Biometric |
|
|
505
|
+
|---------|---------|---------|-------|-----------|
|
|
506
|
+
| `expo-secure-store` | Expo managed | iOS Keychain + Android Keystore AES | 2048 bytes per value (iOS) | Limited |
|
|
507
|
+
| `react-native-keychain` | Bare RN | iOS Keychain + Android CipherStorage | No fixed limit | Full (Face ID, Touch ID, biometric prompt) |
|
|
508
|
+
|
|
509
|
+
- Use **`expo-secure-store`** for Expo managed projects — zero config, maintained by Expo team.
|
|
510
|
+
- Use **`react-native-keychain`** for bare React Native or when biometric-gated keychain access
|
|
511
|
+
is required.
|
|
512
|
+
- The `expo-secure-store` 2048-byte iOS limit is sufficient for JWT access tokens. For larger
|
|
513
|
+
payloads (e.g., encrypted blobs), store a pointer/reference to encrypted file storage instead.
|
|
514
|
+
- For encrypted key-value stores with no size limit, `react-native-mmkv` with encryption enabled
|
|
515
|
+
is an alternative for non-credential data.
|
|
516
|
+
|
|
517
|
+
### Network Security
|
|
518
|
+
|
|
519
|
+
- **HTTPS only** in production. Never disable certificate validation (`rejectUnauthorized: false`
|
|
520
|
+
must not appear in production code).
|
|
521
|
+
- **Certificate pinning** via `react-native-ssl-pinning` or a custom fetch interceptor that
|
|
522
|
+
validates server certificates against embedded public keys.
|
|
523
|
+
- Always validate server certificate chains; implement a pin rotation strategy with backup pins
|
|
524
|
+
to avoid bricking deployed apps during certificate renewal.
|
|
525
|
+
|
|
526
|
+
### OTA Updates & Code Integrity
|
|
527
|
+
|
|
528
|
+
- **EAS Update** delivers JS bundle and asset changes OTA in minutes.
|
|
529
|
+
- **Runtime versions** define the JS↔native boundary — bump when any native code changes.
|
|
530
|
+
Deploying a JS bundle to a build with a mismatched runtime version causes crashes.
|
|
531
|
+
- Use EAS Update channels (`production` / `staging`) for staged rollouts. Use
|
|
532
|
+
`--rollout-percentage` for gradual deploys. Use `eas update:edit` to adjust or roll back.
|
|
533
|
+
- **CodePush** is the bare React Native alternative to EAS Update.
|
|
534
|
+
|
|
535
|
+
### Secrets & Environment Configuration
|
|
536
|
+
|
|
537
|
+
- Never embed API keys in the JS bundle — they are extractable from any Hermes bytecode with
|
|
538
|
+
standard tools.
|
|
539
|
+
- Use `app.config.js` for non-sensitive build-time config.
|
|
540
|
+
- Use EAS environment variables (set via `eas secret:create`) for secrets — they are injected
|
|
541
|
+
at build time, not stored in the repo.
|
|
542
|
+
- Proxy sensitive API calls through a backend API. `expo-constants` surfaces EAS env vars at
|
|
543
|
+
runtime for non-secret config values.
|
|
544
|
+
|
|
545
|
+
### Code Obfuscation
|
|
546
|
+
|
|
547
|
+
- Hermes bytecode compiles JS to binary format — not human-readable source, but reversible with
|
|
548
|
+
tooling. Not a substitute for not embedding secrets.
|
|
549
|
+
- For stronger protection: enable `metro-minify-terser` with identifier mangling enabled.
|
|
550
|
+
- `react-native-obfuscate` applies source-level obfuscation before the Metro bundler runs.
|
|
551
|
+
- Upload source maps to Sentry for symbolicated crash reports despite obfuscation.
|
|
552
|
+
|
|
553
|
+
### Input Validation
|
|
554
|
+
|
|
555
|
+
- Use **Zod** at every API response boundary — parse the shape before using the data.
|
|
556
|
+
- Validate deep link params before acting on them — they arrive from outside the app.
|
|
557
|
+
- Use React Hook Form + Zod at form boundaries — never trust unvalidated form data.
|
|
558
|
+
- Use parameterized queries for local SQLite — never concatenate user input into SQL strings.
|
|
559
|
+
|
|
560
|
+
---
|
|
561
|
+
|
|
562
|
+
## Integration Patterns
|
|
563
|
+
|
|
564
|
+
### REST API Integration
|
|
565
|
+
|
|
566
|
+
Use `fetch` (built-in) or `axios` as the HTTP client. Wrap all async server state in
|
|
567
|
+
TanStack Query v5.
|
|
568
|
+
|
|
569
|
+
**Critical: wire `onlineManager` at app startup** (see State Management section). Without this,
|
|
570
|
+
TanStack Query will not respond to network connectivity changes on React Native because it cannot
|
|
571
|
+
detect `navigator.onLine`.
|
|
572
|
+
|
|
573
|
+
```ts
|
|
574
|
+
// services/query-client.ts
|
|
575
|
+
import { QueryClient } from '@tanstack/react-query';
|
|
576
|
+
|
|
577
|
+
export const queryClient = new QueryClient({
|
|
578
|
+
defaultOptions: {
|
|
579
|
+
queries: {
|
|
580
|
+
staleTime: 1000 * 60 * 5, // 5 minutes
|
|
581
|
+
retry: 2,
|
|
582
|
+
refetchOnWindowFocus: true, // works via focusManager wiring
|
|
583
|
+
},
|
|
584
|
+
},
|
|
585
|
+
});
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
For paginated lists, use `useInfiniteQuery` with `maxPages` to cap memory usage on mobile:
|
|
589
|
+
|
|
590
|
+
```ts
|
|
591
|
+
useInfiniteQuery({
|
|
592
|
+
queryKey: ['feed'],
|
|
593
|
+
queryFn: ({ pageParam }) => fetchFeed(pageParam),
|
|
594
|
+
getNextPageParam: (last) => last.nextCursor,
|
|
595
|
+
maxPages: 5, // keep at most 5 pages in memory
|
|
596
|
+
initialPageParam: null,
|
|
597
|
+
});
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
### Forms & Validation
|
|
601
|
+
|
|
602
|
+
**React Hook Form v7** + **Zod v3** + **`@hookform/resolvers/zod`** is the standard stack.
|
|
603
|
+
Use `Controller` for all React Native inputs — RN inputs are uncontrolled by default and require
|
|
604
|
+
the `Controller` bridge to integrate with RHF's ref-based model.
|
|
605
|
+
|
|
606
|
+
```ts
|
|
607
|
+
const schema = z.object({
|
|
608
|
+
email: z.string().email('Enter a valid email'),
|
|
609
|
+
password: z.string().min(8, 'Minimum 8 characters'),
|
|
610
|
+
});
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
Trigger validation `onBlur` for forms with many fields to avoid re-render storms on every
|
|
614
|
+
keystroke. Use `mode: 'onChange'` only when immediate feedback is required (e.g., password
|
|
615
|
+
strength indicator).
|
|
616
|
+
|
|
617
|
+
### Native Modules & Expo Modules API
|
|
618
|
+
|
|
619
|
+
Use the **Expo Modules API** (`expo-modules-core`) to create custom native modules. Define the
|
|
620
|
+
module using the `ExpoModule` class in Swift (iOS) and Kotlin (Android). Prefer Expo packages
|
|
621
|
+
over community packages when an equivalent exists — they are guaranteed to support New Architecture.
|
|
622
|
+
|
|
623
|
+
Check `reactnative.directory` for the `new-arch: true` badge before adopting any community
|
|
624
|
+
package. A package without New Architecture support will block your upgrade path.
|
|
625
|
+
|
|
626
|
+
### Local Persistence
|
|
627
|
+
|
|
628
|
+
| Library | Backing | Use Case | Encrypted | Notes |
|
|
629
|
+
|---------|---------|----------|-----------|-------|
|
|
630
|
+
| `expo-sqlite` v15 | SQLite | Relational data, complex queries | Yes (via extension) | Use `SQLiteProvider` + `useSQLiteContext`; WAL mode default; do NOT use `openDatabase` |
|
|
631
|
+
| `react-native-mmkv` | Key-value (memory-mapped) | Fast reads, store persistence | Yes | Optimal for Zustand persist middleware |
|
|
632
|
+
| `AsyncStorage` | Key-value (filesystem) | Non-sensitive only | No | Not for secrets or tokens |
|
|
633
|
+
|
|
634
|
+
`expo-sqlite` v15 API — always use `SQLiteProvider` and `useSQLiteContext`, not the deprecated
|
|
635
|
+
`openDatabase`:
|
|
636
|
+
|
|
637
|
+
```tsx
|
|
638
|
+
// Correct v15 usage
|
|
639
|
+
import { SQLiteProvider, useSQLiteContext } from 'expo-sqlite';
|
|
640
|
+
|
|
641
|
+
export function App() {
|
|
642
|
+
return (
|
|
643
|
+
<SQLiteProvider databaseName="app.db">
|
|
644
|
+
<FeedScreen />
|
|
645
|
+
</SQLiteProvider>
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
function FeedScreen() {
|
|
650
|
+
const db = useSQLiteContext();
|
|
651
|
+
// db is now available synchronously — WAL mode enabled by default
|
|
652
|
+
}
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
### Push Notifications, Deep Links & Background Work
|
|
656
|
+
|
|
657
|
+
- **Push notifications** — `expo-notifications` for token registration, receiving, and display.
|
|
658
|
+
Register the push token with your backend after user permission is granted.
|
|
659
|
+
- **Deep links** — Expo Router v4 handles universal links and custom scheme deep links natively
|
|
660
|
+
via file-based routing. No additional `Linking` wiring required for in-app routes.
|
|
661
|
+
- **Background work** — `expo-background-fetch` + `expo-task-manager` for periodic background
|
|
662
|
+
tasks (data sync, cache refresh). Background time is OS-limited; design tasks to complete in
|
|
663
|
+
under 30 seconds.
|
|
664
|
+
|
|
665
|
+
---
|
|
666
|
+
|
|
667
|
+
## DevOps & Deployment
|
|
668
|
+
|
|
669
|
+
### Environments & Configuration
|
|
670
|
+
|
|
671
|
+
```js
|
|
672
|
+
// app.config.js — environment-aware build config
|
|
673
|
+
const IS_PROD = process.env.APP_VARIANT === 'production';
|
|
674
|
+
|
|
675
|
+
export default {
|
|
676
|
+
name: IS_PROD ? 'MyApp' : 'MyApp (Dev)',
|
|
677
|
+
ios: { bundleIdentifier: IS_PROD ? 'com.example.myapp' : 'com.example.myapp.dev' },
|
|
678
|
+
android: { package: IS_PROD ? 'com.example.myapp' : 'com.example.myapp.dev' },
|
|
679
|
+
};
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
```json
|
|
683
|
+
// eas.json — build profiles
|
|
684
|
+
{
|
|
685
|
+
"build": {
|
|
686
|
+
"production": {
|
|
687
|
+
"channel": "production",
|
|
688
|
+
"env": { "APP_VARIANT": "production" }
|
|
689
|
+
},
|
|
690
|
+
"preview": {
|
|
691
|
+
"channel": "preview",
|
|
692
|
+
"distribution": "internal",
|
|
693
|
+
"env": { "APP_VARIANT": "preview" }
|
|
694
|
+
},
|
|
695
|
+
"development": {
|
|
696
|
+
"developmentClient": true,
|
|
697
|
+
"distribution": "internal"
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
Set secrets via `eas secret:create --scope project --name API_SECRET --value "..."`. Access at
|
|
704
|
+
runtime via `expo-constants` `expoConfig.extra`.
|
|
705
|
+
|
|
706
|
+
### CI/CD with EAS
|
|
707
|
+
|
|
708
|
+
EAS Workflows (YAML in `.eas/workflows/`) orchestrate the full pipeline:
|
|
709
|
+
|
|
710
|
+
```yaml
|
|
711
|
+
# .eas/workflows/production.yml
|
|
712
|
+
on:
|
|
713
|
+
push:
|
|
714
|
+
branches: [main]
|
|
715
|
+
jobs:
|
|
716
|
+
build-ios:
|
|
717
|
+
type: build
|
|
718
|
+
params:
|
|
719
|
+
platform: ios
|
|
720
|
+
profile: production
|
|
721
|
+
build-android:
|
|
722
|
+
type: build
|
|
723
|
+
params:
|
|
724
|
+
platform: android
|
|
725
|
+
profile: production
|
|
726
|
+
submit:
|
|
727
|
+
type: submit
|
|
728
|
+
needs: [build-ios, build-android]
|
|
729
|
+
params:
|
|
730
|
+
profile: production
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
- **EAS Build** — cloud builds, manages signing credentials, produces `.ipa` and `.apk/.aab`.
|
|
734
|
+
- **EAS Submit** — uploads to App Store Connect and Google Play. Use `--auto-submit` flag on
|
|
735
|
+
`eas build` to trigger submit automatically post-build.
|
|
736
|
+
- **EAS Update** — OTA JS + asset updates. Propagates to users in minutes. Run
|
|
737
|
+
`eas update --channel production --message "Fix login crash"` for emergency patches.
|
|
738
|
+
- Parallel iOS + Android build jobs cut wall-clock CI time roughly in half.
|
|
739
|
+
|
|
740
|
+
### Expo Prebuild / Bare React Native Escape Hatch
|
|
741
|
+
|
|
742
|
+
Run `npx expo prebuild` to generate `ios/` and `android/` directories from the managed project.
|
|
743
|
+
Use prebuild when:
|
|
744
|
+
- A required native library has no Expo Config Plugin.
|
|
745
|
+
- CI requires native build customization beyond what Config Plugins support.
|
|
746
|
+
- Migrating to bare workflow deliberately.
|
|
747
|
+
|
|
748
|
+
**Warning:** prebuild output is not checked into git — regenerate on every native dependency
|
|
749
|
+
change. Committing `ios/` and `android/` is valid only in fully bare projects.
|
|
750
|
+
|
|
751
|
+
### Crash Reporting & Observability
|
|
752
|
+
|
|
753
|
+
- **Sentry** (`@sentry/react-native` + `@sentry/expo`) — crash reporting, performance traces,
|
|
754
|
+
symbolicated stack traces via source maps. Wire `Sentry.init()` as the first statement before
|
|
755
|
+
`registerRootComponent()`.
|
|
756
|
+
- **Firebase Crashlytics** — alternative for Firebase-first stacks.
|
|
757
|
+
- Wire `Sentry.setUser()` after auth to correlate crashes with user accounts.
|
|
758
|
+
|
|
759
|
+
### Store Release Flow
|
|
760
|
+
|
|
761
|
+
- **iOS:** EAS Build → TestFlight (internal test) → External Testing → App Store Review → Release.
|
|
762
|
+
- **Android:** EAS Build → Internal Testing track → Closed Testing → Open Testing → Production.
|
|
763
|
+
- **EAS Submit** automates upload to both stores.
|
|
764
|
+
- Use `autoIncrement: true` in `eas.json` to auto-bump build numbers on each build.
|
|
765
|
+
- OTA updates via EAS Update bypass App Store review for JS-only changes.
|
|
766
|
+
|
|
767
|
+
---
|
|
768
|
+
|
|
769
|
+
## Decision Trees
|
|
770
|
+
|
|
771
|
+
### Expo Managed vs Expo Prebuild vs Bare React Native
|
|
772
|
+
|
|
773
|
+
```
|
|
774
|
+
START: Does the app require a native library without an Expo Config Plugin?
|
|
775
|
+
|
|
|
776
|
+
+-- Yes
|
|
777
|
+
| |
|
|
778
|
+
| +-- Is the library available as a bare-only package?
|
|
779
|
+
| | --> Expo Prebuild (npx expo prebuild — generates ios/ + android/,
|
|
780
|
+
| | use Config Plugins for everything else)
|
|
781
|
+
| |
|
|
782
|
+
| +-- Can you write an Expo Config Plugin or use a community one?
|
|
783
|
+
| --> Stay Expo Managed with the Config Plugin
|
|
784
|
+
|
|
|
785
|
+
+-- No: Does the app need full native project control (custom Podfile, Gradle scripts,
|
|
786
|
+
| CI native customization, proprietary SDKs with no RN wrapper)?
|
|
787
|
+
| |
|
|
788
|
+
| +-- Yes --> Bare React Native (eject fully — own ios/ and android/)
|
|
789
|
+
| |
|
|
790
|
+
| +-- No --> Expo Managed Workflow (default — least maintenance overhead,
|
|
791
|
+
| EAS Build + EAS Update, no native directories to maintain)
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
### Which Navigation Approach?
|
|
795
|
+
|
|
796
|
+
```
|
|
797
|
+
START: Is the project on Expo SDK 52+ (Expo Router v4 available)?
|
|
798
|
+
|
|
|
799
|
+
+-- Yes
|
|
800
|
+
| |
|
|
801
|
+
| +-- Is file-based routing acceptable for the app's navigation structure?
|
|
802
|
+
| | |
|
|
803
|
+
| | +-- Yes --> Expo Router v4 (typed routes, Guarded Groups, universal links built-in)
|
|
804
|
+
| | |
|
|
805
|
+
| | +-- No (e.g., programmatic dynamic routes, complex nested stacks not
|
|
806
|
+
| | expressible as files)
|
|
807
|
+
| | --> React Navigation v7 (imperative, maximum flexibility)
|
|
808
|
+
| |
|
|
809
|
+
| +-- Does the project already use React Navigation with heavy investment?
|
|
810
|
+
| --> React Navigation v7 (migration cost not justified)
|
|
811
|
+
|
|
|
812
|
+
+-- No (legacy project, bare RN, older SDK)
|
|
813
|
+
--> React Navigation v7
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
### Which State Management Tier?
|
|
817
|
+
|
|
818
|
+
```
|
|
819
|
+
START: Where does the data originate?
|
|
820
|
+
|
|
|
821
|
+
+-- From a server / API (fetched async, needs caching, background sync)
|
|
822
|
+
| --> TanStack Query v5
|
|
823
|
+
| useQuery for reads, useMutation for writes
|
|
824
|
+
| Invalidate query cache on successful mutations
|
|
825
|
+
|
|
|
826
|
+
+-- Client-only: shared across multiple screens or persisted across sessions
|
|
827
|
+
| --> Zustand v5
|
|
828
|
+
| useShallow for all object selectors (mandatory in v5)
|
|
829
|
+
| Persist slice with zustand/middleware persist + react-native-mmkv
|
|
830
|
+
|
|
|
831
|
+
+-- Component-local: ephemeral, not shared beyond this component
|
|
832
|
+
--> useState / useReducer
|
|
833
|
+
No store, no context, no query — keep it local
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
### Which Storage Layer?
|
|
837
|
+
|
|
838
|
+
```
|
|
839
|
+
START: Is the data a secret, token, or credential?
|
|
840
|
+
|
|
|
841
|
+
+-- Yes
|
|
842
|
+
| |
|
|
843
|
+
| +-- Expo managed project --> expo-secure-store (Keychain/Keystore, 2048-byte iOS limit)
|
|
844
|
+
| +-- Bare RN / need biometric --> react-native-keychain
|
|
845
|
+
|
|
|
846
|
+
+-- No: Is the data relational or requires complex queries / joins?
|
|
847
|
+
| |
|
|
848
|
+
| +-- Yes --> expo-sqlite v15 (SQLiteProvider + useSQLiteContext, WAL mode default)
|
|
849
|
+
| | Do NOT use openDatabase API
|
|
850
|
+
| |
|
|
851
|
+
| +-- No: High-frequency key-value reads / writes (e.g., store persistence, settings)?
|
|
852
|
+
| |
|
|
853
|
+
| +-- Yes --> react-native-mmkv (memory-mapped, synchronous, encrypted option)
|
|
854
|
+
| |
|
|
855
|
+
| +-- No (infrequent, non-sensitive key-value)
|
|
856
|
+
| --> AsyncStorage (not for secrets — plain text on filesystem)
|
|
857
|
+
```
|
|
858
|
+
|
|
859
|
+
---
|
|
860
|
+
|
|
861
|
+
## Code Examples
|
|
862
|
+
|
|
863
|
+
### 1. Expo Router App Shell with Guarded Groups
|
|
864
|
+
|
|
865
|
+
The root layout wires all providers and uses Expo Router v4 Guarded Groups for auth — no
|
|
866
|
+
`useEffect` redirects needed.
|
|
867
|
+
|
|
868
|
+
```tsx
|
|
869
|
+
// app/_layout.tsx
|
|
870
|
+
import { Slot } from 'expo-router';
|
|
871
|
+
import { QueryClientProvider } from '@tanstack/react-query';
|
|
872
|
+
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
873
|
+
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
|
874
|
+
import { AppState, AppStateStatus } from 'react-native';
|
|
875
|
+
import NetInfo from '@react-native-community/netinfo';
|
|
876
|
+
import { onlineManager, focusManager } from '@tanstack/react-query';
|
|
877
|
+
import { queryClient } from '@/services/query-client';
|
|
878
|
+
|
|
879
|
+
// Wire onlineManager once — TanStack Query cannot use navigator.onLine on RN
|
|
880
|
+
onlineManager.setEventListener((setOnline) =>
|
|
881
|
+
NetInfo.addEventListener((state) => setOnline(!!state.isConnected))
|
|
882
|
+
);
|
|
883
|
+
|
|
884
|
+
// Wire focusManager for foreground refetch
|
|
885
|
+
AppState.addEventListener('change', (status: AppStateStatus) =>
|
|
886
|
+
focusManager.setFocused(status === 'active')
|
|
887
|
+
);
|
|
888
|
+
|
|
889
|
+
export default function RootLayout() {
|
|
890
|
+
return (
|
|
891
|
+
// GestureHandlerRootView must be outermost — it owns the gesture responder context
|
|
892
|
+
<GestureHandlerRootView style={{ flex: 1 }}>
|
|
893
|
+
<SafeAreaProvider>
|
|
894
|
+
<QueryClientProvider client={queryClient}>
|
|
895
|
+
<Slot />
|
|
896
|
+
</QueryClientProvider>
|
|
897
|
+
</SafeAreaProvider>
|
|
898
|
+
</GestureHandlerRootView>
|
|
899
|
+
);
|
|
900
|
+
}
|
|
901
|
+
```
|
|
902
|
+
|
|
903
|
+
```tsx
|
|
904
|
+
// app/(app)/_layout.tsx — Guarded Group: authenticated routes only
|
|
905
|
+
import { Redirect, Stack } from 'expo-router';
|
|
906
|
+
import { useAuthStore } from '@/stores/auth-store';
|
|
907
|
+
|
|
908
|
+
export const unstable_settings = {
|
|
909
|
+
// Anchor route: unauthenticated users land here
|
|
910
|
+
anchor: '/(auth)/login',
|
|
911
|
+
};
|
|
912
|
+
|
|
913
|
+
export function guard(): boolean {
|
|
914
|
+
// guard() runs outside render — use getState(), not a hook
|
|
915
|
+
return useAuthStore.getState().isAuthenticated;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
export default function AppLayout() {
|
|
919
|
+
return <Stack />;
|
|
920
|
+
}
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
### 2. TanStack Query + Zustand Screen Pattern
|
|
924
|
+
|
|
925
|
+
A screen demonstrating all three state tiers in one component.
|
|
926
|
+
|
|
927
|
+
```tsx
|
|
928
|
+
// features/profile/screens/profile-screen.tsx
|
|
929
|
+
import { View, Text, Modal, Pressable } from 'react-native';
|
|
930
|
+
import { useQuery } from '@tanstack/react-query';
|
|
931
|
+
import { useState } from 'react';
|
|
932
|
+
import { useShallow } from 'zustand/react/shallow';
|
|
933
|
+
import { useAuthStore } from '@/stores/auth-store';
|
|
934
|
+
import { fetchUserProfile } from '@/services/user-service';
|
|
935
|
+
import type { UserProfile } from '@/types/user';
|
|
936
|
+
|
|
937
|
+
export function ProfileScreen() {
|
|
938
|
+
// Tier 1 — server state via TanStack Query v5
|
|
939
|
+
// Query key includes userId so cache is scoped per user
|
|
940
|
+
const { userId, token } = useAuthStore(
|
|
941
|
+
useShallow((s) => ({ userId: s.userId, token: s.token })) // useShallow mandatory in v5
|
|
942
|
+
);
|
|
943
|
+
|
|
944
|
+
const { data: profile, isLoading, isError } = useQuery<UserProfile>({
|
|
945
|
+
queryKey: ['profile', userId],
|
|
946
|
+
queryFn: () => fetchUserProfile(userId, token),
|
|
947
|
+
enabled: !!userId, // don't run without a user
|
|
948
|
+
});
|
|
949
|
+
|
|
950
|
+
// Tier 3 — component-local ephemeral state
|
|
951
|
+
const [isEditModalVisible, setEditModalVisible] = useState(false);
|
|
952
|
+
|
|
953
|
+
if (isLoading) return <Text>Loading…</Text>;
|
|
954
|
+
if (isError || !profile) return <Text>Failed to load profile.</Text>;
|
|
955
|
+
|
|
956
|
+
return (
|
|
957
|
+
<View>
|
|
958
|
+
<Text>{profile.displayName}</Text>
|
|
959
|
+
<Pressable onPress={() => setEditModalVisible(true)}>
|
|
960
|
+
<Text>Edit</Text>
|
|
961
|
+
</Pressable>
|
|
962
|
+
<Modal visible={isEditModalVisible} onRequestClose={() => setEditModalVisible(false)}>
|
|
963
|
+
{/* Edit form here */}
|
|
964
|
+
</Modal>
|
|
965
|
+
</View>
|
|
966
|
+
);
|
|
967
|
+
}
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
### 3. React Hook Form + Zod Native Form
|
|
971
|
+
|
|
972
|
+
`Controller` is required for React Native inputs — RN inputs are uncontrolled and must be
|
|
973
|
+
bridged via `Controller` for RHF's ref model.
|
|
974
|
+
|
|
975
|
+
```tsx
|
|
976
|
+
// features/auth/components/login-form.tsx
|
|
977
|
+
import { View, TextInput, Text, Pressable } from 'react-native';
|
|
978
|
+
import { useForm, Controller } from 'react-hook-form';
|
|
979
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
980
|
+
import { z } from 'zod';
|
|
981
|
+
|
|
982
|
+
const loginSchema = z.object({
|
|
983
|
+
email: z.string().email('Enter a valid email address'),
|
|
984
|
+
password: z.string().min(8, 'Password must be at least 8 characters'),
|
|
985
|
+
});
|
|
986
|
+
|
|
987
|
+
type LoginFormValues = z.infer<typeof loginSchema>;
|
|
988
|
+
|
|
989
|
+
interface LoginFormProps {
|
|
990
|
+
onSubmit: (values: LoginFormValues) => Promise<void>;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
export function LoginForm({ onSubmit }: LoginFormProps) {
|
|
994
|
+
const {
|
|
995
|
+
control,
|
|
996
|
+
handleSubmit,
|
|
997
|
+
formState: { errors, isSubmitting },
|
|
998
|
+
} = useForm<LoginFormValues>({
|
|
999
|
+
resolver: zodResolver(loginSchema),
|
|
1000
|
+
mode: 'onBlur', // validate on blur — avoids re-render storm on every keystroke
|
|
1001
|
+
});
|
|
1002
|
+
|
|
1003
|
+
return (
|
|
1004
|
+
<View>
|
|
1005
|
+
<Controller
|
|
1006
|
+
control={control}
|
|
1007
|
+
name="email"
|
|
1008
|
+
render={({ field: { onChange, onBlur, value } }) => (
|
|
1009
|
+
<TextInput
|
|
1010
|
+
onChangeText={onChange}
|
|
1011
|
+
onBlur={onBlur}
|
|
1012
|
+
value={value}
|
|
1013
|
+
autoCapitalize="none"
|
|
1014
|
+
keyboardType="email-address"
|
|
1015
|
+
placeholder="Email"
|
|
1016
|
+
/>
|
|
1017
|
+
)}
|
|
1018
|
+
/>
|
|
1019
|
+
{errors.email && <Text>{errors.email.message}</Text>}
|
|
1020
|
+
|
|
1021
|
+
<Controller
|
|
1022
|
+
control={control}
|
|
1023
|
+
name="password"
|
|
1024
|
+
render={({ field: { onChange, onBlur, value } }) => (
|
|
1025
|
+
<TextInput
|
|
1026
|
+
onChangeText={onChange}
|
|
1027
|
+
onBlur={onBlur}
|
|
1028
|
+
value={value}
|
|
1029
|
+
secureTextEntry
|
|
1030
|
+
placeholder="Password"
|
|
1031
|
+
/>
|
|
1032
|
+
)}
|
|
1033
|
+
/>
|
|
1034
|
+
{errors.password && <Text>{errors.password.message}</Text>}
|
|
1035
|
+
|
|
1036
|
+
<Pressable onPress={handleSubmit(onSubmit)} disabled={isSubmitting}>
|
|
1037
|
+
<Text>{isSubmitting ? 'Signing in…' : 'Sign In'}</Text>
|
|
1038
|
+
</Pressable>
|
|
1039
|
+
</View>
|
|
1040
|
+
);
|
|
1041
|
+
}
|
|
1042
|
+
```
|
|
1043
|
+
|
|
1044
|
+
### 4. FlashList Memoized Row Pattern
|
|
1045
|
+
|
|
1046
|
+
FlashList v2 requires New Architecture (React Native 0.76+ / Expo SDK 53+). No
|
|
1047
|
+
`estimatedItemSize` in v2 — removed entirely. Use FlashList v1 on Legacy Architecture.
|
|
1048
|
+
|
|
1049
|
+
```tsx
|
|
1050
|
+
// features/feed/components/feed-list.tsx
|
|
1051
|
+
import { useCallback, memo } from 'react';
|
|
1052
|
+
import { View, Text, Pressable } from 'react-native';
|
|
1053
|
+
import { FlashList, ListRenderItem } from '@shopify/flash-list';
|
|
1054
|
+
|
|
1055
|
+
interface FeedItem {
|
|
1056
|
+
id: string;
|
|
1057
|
+
title: string;
|
|
1058
|
+
type: 'post' | 'ad'; // heterogeneous list types
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
interface RowProps {
|
|
1062
|
+
item: FeedItem;
|
|
1063
|
+
onPress: (id: string) => void;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// Memoize the row: prevents re-render when parent state changes but item is unchanged
|
|
1067
|
+
const FeedRow = memo(function FeedRow({ item, onPress }: RowProps) {
|
|
1068
|
+
return (
|
|
1069
|
+
<Pressable onPress={() => onPress(item.id)}>
|
|
1070
|
+
<Text>{item.title}</Text>
|
|
1071
|
+
</Pressable>
|
|
1072
|
+
);
|
|
1073
|
+
});
|
|
1074
|
+
|
|
1075
|
+
interface FeedListProps {
|
|
1076
|
+
items: FeedItem[];
|
|
1077
|
+
onItemPress: (id: string) => void;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
export function FeedList({ items, onItemPress }: FeedListProps) {
|
|
1081
|
+
// Stabilize callback — prevents FeedRow memo from being defeated on parent render
|
|
1082
|
+
const handlePress = useCallback((id: string) => onItemPress(id), [onItemPress]);
|
|
1083
|
+
|
|
1084
|
+
// Typed renderItem — ListRenderItem<FeedItem> ensures item type safety
|
|
1085
|
+
const renderItem: ListRenderItem<FeedItem> = useCallback(
|
|
1086
|
+
({ item }) => <FeedRow item={item} onPress={handlePress} />,
|
|
1087
|
+
[handlePress]
|
|
1088
|
+
);
|
|
1089
|
+
|
|
1090
|
+
return (
|
|
1091
|
+
<FlashList
|
|
1092
|
+
data={items}
|
|
1093
|
+
renderItem={renderItem}
|
|
1094
|
+
// getItemType enables per-type recycling pools — items with different types
|
|
1095
|
+
// never share a recycled cell, preventing layout corruption
|
|
1096
|
+
getItemType={(item) => item.type}
|
|
1097
|
+
// v2: estimatedItemSize is gone — no estimates needed
|
|
1098
|
+
// Cell recycling is pixel-perfect via New Architecture synchronous layout
|
|
1099
|
+
keyExtractor={(item) => item.id}
|
|
1100
|
+
/>
|
|
1101
|
+
);
|
|
1102
|
+
}
|
|
1103
|
+
```
|
|
1104
|
+
|
|
1105
|
+
### 5. Secure Token Storage Wrapper
|
|
1106
|
+
|
|
1107
|
+
Abstracts `expo-secure-store` with typed functions. Never use `AsyncStorage` for tokens.
|
|
1108
|
+
The 2048-byte iOS Keychain limit is sufficient for JWT access tokens; store a reference
|
|
1109
|
+
pointer for larger payloads.
|
|
1110
|
+
|
|
1111
|
+
```ts
|
|
1112
|
+
// services/secure-storage.ts
|
|
1113
|
+
import * as SecureStore from 'expo-secure-store';
|
|
1114
|
+
|
|
1115
|
+
const TOKEN_KEY = 'auth_token';
|
|
1116
|
+
const REFRESH_TOKEN_KEY = 'refresh_token';
|
|
1117
|
+
|
|
1118
|
+
// expo-secure-store: iOS Keychain + Android Keystore AES — encrypted at rest
|
|
1119
|
+
// Limit: 2048 bytes per value on iOS (JWT access tokens fit well within this)
|
|
1120
|
+
// For bare RN projects requiring biometric auth, swap to react-native-keychain
|
|
1121
|
+
|
|
1122
|
+
export async function getToken(): Promise<string | null> {
|
|
1123
|
+
return SecureStore.getItemAsync(TOKEN_KEY);
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
export async function setToken(token: string): Promise<void> {
|
|
1127
|
+
// Validate before storing — never persist a malformed token
|
|
1128
|
+
if (!token || token.length === 0) {
|
|
1129
|
+
throw new Error('Cannot store empty token');
|
|
1130
|
+
}
|
|
1131
|
+
await SecureStore.setItemAsync(TOKEN_KEY, token);
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
export async function removeToken(): Promise<void> {
|
|
1135
|
+
await SecureStore.deleteItemAsync(TOKEN_KEY);
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
export async function getRefreshToken(): Promise<string | null> {
|
|
1139
|
+
return SecureStore.getItemAsync(REFRESH_TOKEN_KEY);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
export async function setRefreshToken(token: string): Promise<void> {
|
|
1143
|
+
if (!token || token.length === 0) {
|
|
1144
|
+
throw new Error('Cannot store empty refresh token');
|
|
1145
|
+
}
|
|
1146
|
+
await SecureStore.setItemAsync(REFRESH_TOKEN_KEY, token);
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
export async function clearAllTokens(): Promise<void> {
|
|
1150
|
+
// Clear both tokens atomically on logout
|
|
1151
|
+
await Promise.all([
|
|
1152
|
+
SecureStore.deleteItemAsync(TOKEN_KEY),
|
|
1153
|
+
SecureStore.deleteItemAsync(REFRESH_TOKEN_KEY),
|
|
1154
|
+
]);
|
|
1155
|
+
}
|
|
1156
|
+
```
|
|
1157
|
+
|
|
1158
|
+
---
|
|
1159
|
+
|
|
1160
|
+
*Researched: 2026-03-12 | Sources: [React Native docs](https://reactnative.dev/docs/getting-started), [RN New Architecture](https://reactnative.dev/architecture/landing-page), [RN 0.79 release blog](https://reactnative.dev/blog/2025/04/08/react-native-0.79), [Expo SDK 53 changelog](https://expo.dev/changelog/sdk-53), [Expo Router docs](https://docs.expo.dev/router/introduction/), [Expo Router typed routes](https://docs.expo.dev/router/reference/typed-routes/), [TanStack Query v5 React Native](https://tanstack.com/query/v5/docs/framework/react/react-native), [TanStack Query v5 migration](https://tanstack.com/query/latest/docs/framework/react/guides/migrating-to-v5), [Zustand GitHub](https://github.com/pmndrs/zustand), [FlashList v2 Shopify blog](https://shopify.engineering/flashlist-v2), [FlashList docs](https://shopify.github.io/flash-list/), [Reanimated 4 migration guide](https://docs.swmansion.com/react-native-reanimated/docs/guides/migration-from-3.x/), [Reanimated 4 stable release](https://blog.swmansion.com/reanimated-4-stable-release-the-future-of-react-native-animations-ba68210c3713), [EAS Build docs](https://docs.expo.dev/build/introduction/), [EAS Update docs](https://docs.expo.dev/eas-update/introduction/), [EAS runtime versions](https://docs.expo.dev/eas-update/runtime-versions/), [EAS Workflows](https://docs.expo.dev/eas/workflows/get-started/), [EAS OTA playbook](https://expo.dev/blog/the-production-playbook-for-ota-updates), [Maestro best RN testing frameworks](https://maestro.dev/insights/best-react-native-testing-frameworks), [Detox vs Maestro](https://www.getpanto.ai/blog/detox-vs-maestro), [QA Wolf 2025 E2E frameworks](https://www.qawolf.com/blog/the-best-mobile-e2e-testing-frameworks-in-2025-strengths-tradeoffs-and-use-cases), [AddJam Maestro experience 2026](https://addjam.com/blog/2026-02-18/our-experience-adding-e2e-testing-react-native-maestro/), [expo-secure-store docs](https://docs.expo.dev/versions/latest/sdk/securestore/), [RN security docs](https://reactnative.dev/docs/security), [React Native Testing Library](https://callstack.github.io/react-native-testing-library/), See also: [mobile-app-architecture.md](../architecture/mobile-architecture/mobile-app-architecture.md), [mobile-antipatterns.md](../antipatterns/frontend/mobile-antipatterns.md), [testing-mobile.md](../quality/testing-mobile.md), [security/mobile/](../security/mobile/)*
|