devendra-skill-package 1.0.2
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/.github/skills/boiler-project-generator/SKILL.md +103 -0
- package/.github/skills/boiler-project-generator/assets/templates/android/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/angular/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/flutter/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/ios/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/react/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/api-setup.ts +76 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/component-primary-button.tsx +29 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/env-config.env +4 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/feature-auth-screen.tsx +45 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/feature-dashboard-screen.tsx +45 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/feature-profile-screen.tsx +45 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/hook-use-app-boot.ts +11 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/navigation-setup.tsx +25 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/project-structure.md +24 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/redux-hooks.ts +5 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/redux-session-slice.ts +29 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/redux-store.ts +15 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/theme-index.ts +23 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/utils-helpers.ts +7 -0
- package/.github/skills/boiler-project-generator/metadata.yaml +29 -0
- package/.github/skills/boiler-project-generator/references/android.md +19 -0
- package/.github/skills/boiler-project-generator/references/angular.md +14 -0
- package/.github/skills/boiler-project-generator/references/flutter.md +17 -0
- package/.github/skills/boiler-project-generator/references/ios.md +17 -0
- package/.github/skills/boiler-project-generator/references/react-native.md +103 -0
- package/.github/skills/boiler-project-generator/references/react.md +17 -0
- package/.github/skills/boiler-project-generator/scripts/angular.js +43 -0
- package/.github/skills/boiler-project-generator/scripts/generate-project.mjs +107 -0
- package/.github/skills/boiler-project-generator/scripts/init.js +105 -0
- package/.github/skills/boiler-project-generator/scripts/react-native.js +1041 -0
- package/.github/skills/boiler-project-generator/scripts/react.js +32 -0
- package/.github/skills/clean-architecture-generator/SKILL.md +235 -0
- package/.github/skills/clean-architecture-generator/references/architecture-core.md +126 -0
- package/.github/skills/clean-architecture-generator/references/platform-angular.md +374 -0
- package/.github/skills/clean-architecture-generator/references/platform-flutter.md +366 -0
- package/.github/skills/clean-architecture-generator/references/platform-react-native.md +590 -0
- package/.github/skills/clean-architecture-generator/references/platform-react.md +261 -0
- package/.github/skills/code-test-coverage-analyzer/SKILL.md +1358 -0
- package/.github/skills/code-test-coverage-analyzer/assets/config-questionnaire.md +118 -0
- package/.github/skills/code-test-coverage-analyzer/assets/conflict-resolution-protocol.md +99 -0
- package/.github/skills/code-test-coverage-analyzer/assets/coverage-calculation-rules.md +294 -0
- package/.github/skills/code-test-coverage-analyzer/assets/multi-strategy-search.md +145 -0
- package/.github/skills/code-test-coverage-analyzer/assets/output-file-contracts.md +651 -0
- package/.github/skills/code-test-coverage-analyzer/assets/status-definitions.md +273 -0
- package/.github/skills/code-test-coverage-analyzer/examples/example-invocation.md +208 -0
- package/.github/skills/code-test-coverage-analyzer/references/android-patterns.md +229 -0
- package/.github/skills/code-test-coverage-analyzer/references/angular-patterns.md +177 -0
- package/.github/skills/code-test-coverage-analyzer/references/backend-patterns.md +383 -0
- package/.github/skills/code-test-coverage-analyzer/references/flutter-patterns.md +254 -0
- package/.github/skills/code-test-coverage-analyzer/references/ios-patterns.md +228 -0
- package/.github/skills/code-test-coverage-analyzer/references/react-native-patterns.md +431 -0
- package/.github/skills/code-test-coverage-analyzer/references/react-patterns.md +206 -0
- package/.github/skills/code-test-coverage-analyzer/scripts/detect-platform.ps1 +261 -0
- package/.github/skills/code-test-coverage-analyzer/scripts/scan-repository.ps1 +242 -0
- package/.github/skills/code-test-coverage-analyzer/templates/brd-compliance-row.md +107 -0
- package/.github/skills/code-test-coverage-analyzer/templates/test-case-row.md +130 -0
- package/.github/skills/code-test-coverage-analyzer/templates/user-story-compliance-row.md +170 -0
- package/.github/skills/custom-font-integrator/SKILL.md +373 -0
- package/.github/skills/custom-font-integrator/assets/FontTestScreen.js +150 -0
- package/.github/skills/custom-font-integrator/references/example-info-plist.xml +21 -0
- package/.github/skills/custom-font-integrator/scripts/setup-fonts.sh +99 -0
- package/.github/skills/encryption-decryption-utility/Instructions.md +282 -0
- package/.github/skills/encryption-decryption-utility/README.md +222 -0
- package/.github/skills/encryption-decryption-utility/SKILL.md +645 -0
- package/.github/skills/encryption-decryption-utility/assets/encryption-config-template.json +13 -0
- package/.github/skills/encryption-decryption-utility/assets/platform-matrix.json +180 -0
- package/.github/skills/encryption-decryption-utility/metadata.yaml +51 -0
- package/.github/skills/encryption-decryption-utility/references/android-crypto.md +157 -0
- package/.github/skills/encryption-decryption-utility/references/angular-crypto.md +130 -0
- package/.github/skills/encryption-decryption-utility/references/approach-1-webcrypto.md +95 -0
- package/.github/skills/encryption-decryption-utility/references/approach-2-rsa-aes-cbc.md +111 -0
- package/.github/skills/encryption-decryption-utility/references/approach-3-aes-sharedkey.md +114 -0
- package/.github/skills/encryption-decryption-utility/references/architecture.md +233 -0
- package/.github/skills/encryption-decryption-utility/references/backend-alignment.md +506 -0
- package/.github/skills/encryption-decryption-utility/references/dependency-guidance.md +234 -0
- package/.github/skills/encryption-decryption-utility/references/ec-implementation.md +832 -0
- package/.github/skills/encryption-decryption-utility/references/error-handling-playbook.md +234 -0
- package/.github/skills/encryption-decryption-utility/references/flutter-crypto.md +105 -0
- package/.github/skills/encryption-decryption-utility/references/ios-crypto.md +338 -0
- package/.github/skills/encryption-decryption-utility/references/native-rsa-implementation.md +373 -0
- package/.github/skills/encryption-decryption-utility/references/node-crypto.md +86 -0
- package/.github/skills/encryption-decryption-utility/references/react-crypto.md +105 -0
- package/.github/skills/encryption-decryption-utility/references/react-native-crypto.md +1001 -0
- package/.github/skills/encryption-decryption-utility/references/react-web-crypto.md +668 -0
- package/.github/skills/encryption-decryption-utility/references/security-checklist.md +176 -0
- package/.github/skills/encryption-decryption-utility/references/web-crypto.md +74 -0
- package/.github/skills/encryption-decryption-utility/scripts/detect.js +128 -0
- package/.github/skills/encryption-decryption-utility/scripts/install.js +1713 -0
- package/.github/skills/encryption-decryption-utility/scripts/setup.js +800 -0
- package/.github/skills/encryption-decryption-utility/template.md +241 -0
- package/.github/skills/encryption-decryption-utility/templates/config-questionnaire.md +467 -0
- package/.github/skills/encryption-decryption-utility/templates/delivery-checklist.md +206 -0
- package/.github/skills/encryption-decryption-utility/templates/implementation-plan.md +304 -0
- package/.github/skills/feature-generator/SKILL.MD +2741 -0
- package/.github/skills/feature-generator/assets/fingerprint-schema.json +385 -0
- package/.github/skills/feature-generator/assets/spec-template.md +172 -0
- package/.github/skills/feature-generator/assets/transform-rules.json +82 -0
- package/.github/skills/feature-generator/references/api-envelope-patterns.md +327 -0
- package/.github/skills/feature-generator/references/screen-logic-patterns.md +399 -0
- package/.github/skills/feature-generator/references/state-library-patterns.md +464 -0
- package/.github/skills/figma-ui-mapper/README.md +34 -0
- package/.github/skills/figma-ui-mapper/SKILL.md +101 -0
- package/.github/skills/figma-ui-mapper/assets/templates/component-map-template.md +30 -0
- package/.github/skills/figma-ui-mapper/assets/templates/generation-report-template.md +49 -0
- package/.github/skills/figma-ui-mapper/metadata.yaml +22 -0
- package/.github/skills/figma-ui-mapper/references/android.md +11 -0
- package/.github/skills/figma-ui-mapper/references/angular.md +18 -0
- package/.github/skills/figma-ui-mapper/references/capacitor.md +18 -0
- package/.github/skills/figma-ui-mapper/references/common.md +23 -0
- package/.github/skills/figma-ui-mapper/references/expo.md +18 -0
- package/.github/skills/figma-ui-mapper/references/flutter.md +18 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/angular/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/capacitor/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/expo/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/flutter/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/ionic/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/kotlin-multiplatform/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/native-android/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/native-ios/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/next/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/nuxt/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/react/rules.md +19 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/react-native/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/svelte/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/vue/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/ionic.md +18 -0
- package/.github/skills/figma-ui-mapper/references/ios.md +11 -0
- package/.github/skills/figma-ui-mapper/references/kotlin-multiplatform.md +18 -0
- package/.github/skills/figma-ui-mapper/references/native-android.md +18 -0
- package/.github/skills/figma-ui-mapper/references/native-ios.md +18 -0
- package/.github/skills/figma-ui-mapper/references/next.md +18 -0
- package/.github/skills/figma-ui-mapper/references/nuxt.md +18 -0
- package/.github/skills/figma-ui-mapper/references/react-figma-guideline.md +21 -0
- package/.github/skills/figma-ui-mapper/references/react-native.md +18 -0
- package/.github/skills/figma-ui-mapper/references/react.md +18 -0
- package/.github/skills/figma-ui-mapper/references/script-usage.md +46 -0
- package/.github/skills/figma-ui-mapper/references/svelte.md +18 -0
- package/.github/skills/figma-ui-mapper/references/vue.md +18 -0
- package/.github/skills/figma-ui-mapper/references/workflow.md +74 -0
- package/.github/skills/figma-ui-mapper/scripts/generate-page-through-figma.js +635 -0
- package/.github/skills/figma-ui-mapper/scripts/generate-react-page-through-figma.js +4 -0
- package/.github/skills/frontend-task-breakdown/SKILL.md +734 -0
- package/.github/skills/md-file-converter/SKILL.md +291 -0
- package/.github/skills/md-file-converter/config/formats.json +114 -0
- package/.github/skills/md-file-converter/extractors/extract-docx.sh +120 -0
- package/.github/skills/md-file-converter/extractors/extract-eml.py +108 -0
- package/.github/skills/md-file-converter/extractors/extract-pdf.sh +97 -0
- package/.github/skills/md-file-converter/extractors/extract-pptx.py +143 -0
- package/.github/skills/md-file-converter/extractors/extract-xlsx.py +175 -0
- package/.github/skills/md-file-converter/scripts/check-tools.sh +47 -0
- package/.github/skills/md-file-converter/scripts/extract-archive.sh +83 -0
- package/.github/skills/md-file-converter/scripts/extract-ocr.sh +77 -0
- package/.github/skills/md-file-converter/scripts/get-metadata.sh +57 -0
- package/.github/skills/md-file-converter/templates/output.md +73 -0
- package/.github/skills/md-file-converter/templates/unsupported-object.md +6 -0
- package/.github/skills/mockAPI-contract-generator/SKILL.md +607 -0
- package/.github/skills/test-case-validator/CHANGELOG.md +155 -0
- package/.github/skills/test-case-validator/CONFIGURATION.md +130 -0
- package/.github/skills/test-case-validator/EXAMPLES.md +50 -0
- package/.github/skills/test-case-validator/QUALITY_SCORING.md +98 -0
- package/.github/skills/test-case-validator/README.md +111 -0
- package/.github/skills/test-case-validator/REPORT_TEMPLATE.md +116 -0
- package/.github/skills/test-case-validator/SKILL.md +209 -0
- package/.github/skills/test-case-validator/SYSTEM_PROMPT.md +127 -0
- package/.github/skills/test-case-validator/TRACEABILITY_ENGINE.md +152 -0
- package/.github/skills/test-case-validator/VALIDATION_RULES.md +411 -0
- package/.github/skills/test-case-validator/config/default.config.yaml +62 -0
- package/.github/skills/test-case-validator/docs/ARCHITECTURE.md +66 -0
- package/.github/skills/test-case-validator/docs/EXTENDING.md +85 -0
- package/.github/skills/test-case-validator/docs/FAQ.md +88 -0
- package/.github/skills/test-case-validator/docs/VERSIONING.md +55 -0
- package/.github/skills/test-case-validator/examples/example-1-manual-qa-ecommerce/input-brd.md +43 -0
- package/.github/skills/test-case-validator/examples/example-1-manual-qa-ecommerce/input-test-cases.csv +9 -0
- package/.github/skills/test-case-validator/examples/example-1-manual-qa-ecommerce/input-user-story.md +29 -0
- package/.github/skills/test-case-validator/examples/example-1-manual-qa-ecommerce/output-report.md +303 -0
- package/.github/skills/test-case-validator/examples/example-2-automated-frontend-suite/input-spec.md +43 -0
- package/.github/skills/test-case-validator/examples/example-2-automated-frontend-suite/input-test-files-index.md +56 -0
- package/.github/skills/test-case-validator/examples/example-2-automated-frontend-suite/output-report.md +274 -0
- package/.github/skills/test-case-validator/prompts/00-applicability-resolution.md +61 -0
- package/.github/skills/test-case-validator/prompts/01-extraction-user-story.md +58 -0
- package/.github/skills/test-case-validator/prompts/02-extraction-brd.md +64 -0
- package/.github/skills/test-case-validator/prompts/03-extraction-test-cases.md +92 -0
- package/.github/skills/test-case-validator/prompts/04-traceability-matrix.md +41 -0
- package/.github/skills/test-case-validator/prompts/05-coverage-analysis.md +46 -0
- package/.github/skills/test-case-validator/prompts/06-missing-scenario-detection.md +32 -0
- package/.github/skills/test-case-validator/prompts/07-duplicate-detection.md +36 -0
- package/.github/skills/test-case-validator/prompts/08-risk-analysis.md +39 -0
- package/.github/skills/test-case-validator/prompts/09-gated-wiring-validation.md +53 -0
- package/.github/skills/test-case-validator/prompts/10-observation-report.md +56 -0
- package/.github/skills/test-case-validator/prompts/11-final-report-assembly.md +57 -0
- package/.github/skills/test-case-validator/templates/coverage-table.md +25 -0
- package/.github/skills/test-case-validator/templates/executive-summary.md +38 -0
- package/.github/skills/test-case-validator/templates/final-summary-block.md +87 -0
- package/.github/skills/test-case-validator/templates/observation-entry.md +41 -0
- package/.github/skills/test-case-validator/templates/rtm-row.md +25 -0
- package/.github/skills/validation-engine-builder/README.md +50 -0
- package/.github/skills/validation-engine-builder/SKILL.md +241 -0
- package/.github/skills/validation-engine-builder/references/core.md +158 -0
- package/.github/skills/validation-engine-builder/references/platform-angular.md +166 -0
- package/.github/skills/validation-engine-builder/references/platform-flutter.md +118 -0
- package/.github/skills/validation-engine-builder/references/platform-react-native.md +170 -0
- package/.github/skills/validation-engine-builder/references/platform-react.md +94 -0
- package/.github/skills/validation-engine-builder/scripts/apply-to-project.js +80 -0
- package/.github/skills/validation-engine-builder/scripts/generate-angular.js +271 -0
- package/.github/skills/validation-engine-builder/scripts/generate-flutter.js +298 -0
- package/.github/skills/validation-engine-builder/scripts/generate-react-native.js +217 -0
- package/.github/skills/validation-engine-builder/scripts/generate-react.js +163 -0
- package/.github/skills/validation-engine-builder/scripts/install-deps.js +53 -0
- package/.github/skills/validation-engine-builder/scripts/shared/detect-platform.js +68 -0
- package/.github/skills/validation-engine-builder/scripts/shared/npm-deps.js +103 -0
- package/.github/skills/validation-engine-builder/scripts/shared/rhf-hook.js +43 -0
- package/.github/skills/validation-engine-builder/scripts/shared/ts-messages-regex.js +62 -0
- package/.github/skills/validation-engine-builder/scripts/shared/util.js +44 -0
- package/.github/skills/validation-engine-builder/scripts/shared/yup-schemas.js +80 -0
- package/.github/skills/validation-engine-builder/template.md +73 -0
- package/.github/skills/validation-engine-builder/templates/messages.json +22 -0
- package/.github/skills/validation-engine-builder/templates/regex.json +16 -0
- package/.github/skills/validation-engine-builder/templates/schemas.json +48 -0
- package/bin/index.js +145 -0
- package/package.json +22 -0
- package/readme.md +0 -0
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
# Platform File — React Native
|
|
2
|
+
|
|
3
|
+
Read `architecture-core.md` first. This file supplies the React Native-specific implementation of
|
|
4
|
+
the universal layers: folder tree, DI mechanism, state/HTTP/storage/navigation patterns, naming
|
|
5
|
+
extensions, and anti-patterns. Everything under "Stack Fingerprint Defaults" applies **only** when
|
|
6
|
+
Phase 1 detection (see `SKILL.md`) found nothing in the target repo — if the repo already uses a
|
|
7
|
+
different state library, HTTP client, storage, or navigation library, generate against what's
|
|
8
|
+
already there instead.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Stack Fingerprint Defaults (greenfield only)
|
|
13
|
+
|
|
14
|
+
| Concern | Detect first (search for) | Default if nothing detected |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| Language | — | TypeScript, strict mode |
|
|
17
|
+
| State management | `createSlice(`, `create(` from `'zustand'`, `makeAutoObservable(`/`observable(` (mobx), `useReducer`+`createContext` | Redux Toolkit (`createSlice`, `createAsyncThunk`) + RTK Query (`createApi`) |
|
|
18
|
+
| HTTP client | `axios.create(`, `fetch(`, `createApi(`, React Query hooks | Axios with interceptors |
|
|
19
|
+
| Local storage | `MMKV`, `AsyncStorage`, `react-native-keychain` | MMKV (`SafeMMKV` wrapper) for general data; Keychain for secrets/tokens |
|
|
20
|
+
| Navigation | `@react-navigation/native`, `expo-router`, `react-native-navigation` (Wix) | React Navigation v6+, native-stack |
|
|
21
|
+
| DI mechanism | existing `di/[feature]Container.ts` or `di/[feature]DI.ts` files | manual DI — Pattern A or B below |
|
|
22
|
+
| Test runner | `jest.config.*`, test script in `package.json` | Jest |
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Folder Tree
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
src/
|
|
30
|
+
├── app/ # App bootstrap — orchestration only
|
|
31
|
+
│ ├── AppWrapper.tsx # Root component, cache hydration, offline overlay
|
|
32
|
+
│ ├── index.ts
|
|
33
|
+
│ ├── NetworkMonitor.tsx
|
|
34
|
+
│ ├── NoInternetScreen.tsx
|
|
35
|
+
│ ├── useNetworkStatus.ts
|
|
36
|
+
│ ├── di/
|
|
37
|
+
│ │ ├── container.ts # Global DI container — wires all feature containers
|
|
38
|
+
│ │ └── index.ts
|
|
39
|
+
│ ├── environment/
|
|
40
|
+
│ │ ├── authConfig.ts # OAuth / auth provider config
|
|
41
|
+
│ │ ├── env.ts # Environment variable accessors (no raw process.env)
|
|
42
|
+
│ │ └── index.ts
|
|
43
|
+
│ ├── offline/
|
|
44
|
+
│ │ ├── OfflineOverlayController.tsx
|
|
45
|
+
│ │ ├── offlineOverlayTypes.ts
|
|
46
|
+
│ │ └── index.ts
|
|
47
|
+
│ ├── routing/
|
|
48
|
+
│ │ ├── AppNavigator.tsx # Top-level (decides auth vs main)
|
|
49
|
+
│ │ ├── RootNavigator.tsx # Authenticated root
|
|
50
|
+
│ │ ├── MainTabsNavigator.tsx
|
|
51
|
+
│ │ ├── TabsNavigator.tsx
|
|
52
|
+
│ │ └── index.ts
|
|
53
|
+
│ └── state/
|
|
54
|
+
│ └── store.ts # Redux store configuration
|
|
55
|
+
│
|
|
56
|
+
├── core/ # Infrastructure — shared by all features
|
|
57
|
+
│ ├── api/
|
|
58
|
+
│ │ ├── axiosClient.ts # Singleton Axios instance + interceptors + SSL pinning
|
|
59
|
+
│ │ ├── apiErrorHandler.ts
|
|
60
|
+
│ │ ├── AppError.ts # Typed error class hierarchy
|
|
61
|
+
│ │ ├── tokenProvider.ts # Token getter/setter (prevents circular deps)
|
|
62
|
+
│ │ ├── unauthorizedHandler.ts
|
|
63
|
+
│ │ ├── requestUtils.ts # Header builders, nonce generation
|
|
64
|
+
│ │ └── index.ts
|
|
65
|
+
│ ├── cache/
|
|
66
|
+
│ │ ├── baseApiSlice.ts # Single createApi instance for the whole app
|
|
67
|
+
│ │ ├── axiosBaseQuery.ts
|
|
68
|
+
│ │ ├── CacheConfig.ts
|
|
69
|
+
│ │ ├── hydrateCacheOnStart.ts
|
|
70
|
+
│ │ ├── persistenceMiddleware.ts
|
|
71
|
+
│ │ └── persistenceRegistry.ts
|
|
72
|
+
│ ├── config/
|
|
73
|
+
│ │ ├── constants.ts
|
|
74
|
+
│ │ └── index.ts
|
|
75
|
+
│ ├── device/
|
|
76
|
+
│ │ ├── smsComposer.ts
|
|
77
|
+
│ │ └── index.ts
|
|
78
|
+
│ ├── error/
|
|
79
|
+
│ │ ├── errorHandler.ts
|
|
80
|
+
│ │ └── index.ts
|
|
81
|
+
│ ├── logging/
|
|
82
|
+
│ │ ├── loggerService.ts
|
|
83
|
+
│ │ ├── auditLogger.ts
|
|
84
|
+
│ │ ├── correlationId.ts
|
|
85
|
+
│ │ └── index.ts
|
|
86
|
+
│ ├── permissions/
|
|
87
|
+
│ │ ├── permissionsService.ts
|
|
88
|
+
│ │ └── index.ts
|
|
89
|
+
│ ├── resilience/
|
|
90
|
+
│ │ ├── offlineModeService.ts
|
|
91
|
+
│ │ ├── resilienceService.ts
|
|
92
|
+
│ │ └── index.ts
|
|
93
|
+
│ ├── security/
|
|
94
|
+
│ │ ├── EncryptionService.ts
|
|
95
|
+
│ │ ├── DeviceSecurityCheck.ts
|
|
96
|
+
│ │ ├── entitlementService.ts
|
|
97
|
+
│ │ ├── SSLPinningAdapter.ts
|
|
98
|
+
│ │ └── index.ts
|
|
99
|
+
│ ├── storage/
|
|
100
|
+
│ │ ├── mmkvStorageService.ts
|
|
101
|
+
│ │ ├── SafeMMKV.ts
|
|
102
|
+
│ │ ├── keychainService.ts
|
|
103
|
+
│ │ ├── authTokenStorage.ts
|
|
104
|
+
│ │ ├── storageService.ts
|
|
105
|
+
│ │ └── index.ts
|
|
106
|
+
│ ├── store/
|
|
107
|
+
│ │ └── hooks.ts # useAppDispatch, useAppSelector
|
|
108
|
+
│ ├── streaming/
|
|
109
|
+
│ │ ├── websocket.ts
|
|
110
|
+
│ │ └── index.ts
|
|
111
|
+
│ └── analytics/
|
|
112
|
+
│ ├── eventTracker.ts
|
|
113
|
+
│ ├── pushNotificationService.ts
|
|
114
|
+
│ └── index.ts
|
|
115
|
+
│
|
|
116
|
+
├── features/ # One folder per feature, fully self-contained
|
|
117
|
+
│ └── [feature-name]/
|
|
118
|
+
│ ├── domain/ # Pure TypeScript — ZERO framework imports
|
|
119
|
+
│ │ ├── entities/
|
|
120
|
+
│ │ ├── usecases/
|
|
121
|
+
│ │ ├── repositories/ # Interfaces only (I-prefixed)
|
|
122
|
+
│ │ └── index.ts
|
|
123
|
+
│ ├── data/
|
|
124
|
+
│ │ ├── api/ # Feature-specific Axios API client class
|
|
125
|
+
│ │ ├── dtos/ # Raw API response shapes
|
|
126
|
+
│ │ ├── mappers/ # mapTo pure functions
|
|
127
|
+
│ │ ├── repositories/ # Concrete implementations
|
|
128
|
+
│ │ ├── cache/ # Optional feature cache helpers
|
|
129
|
+
│ │ ├── config/ # Feature API base URLs, endpoint paths
|
|
130
|
+
│ │ ├── mock/ # Static mock responses (dev/test only)
|
|
131
|
+
│ │ └── index.ts
|
|
132
|
+
│ ├── presentation/
|
|
133
|
+
│ │ ├── screens/
|
|
134
|
+
│ │ ├── components/
|
|
135
|
+
│ │ ├── hooks/
|
|
136
|
+
│ │ ├── navigation/ # Typed route param lists + navigators
|
|
137
|
+
│ │ ├── state/ # Redux slices, thunks, selectors (or detected equivalent)
|
|
138
|
+
│ │ ├── viewmodels/
|
|
139
|
+
│ │ ├── styles/
|
|
140
|
+
│ │ ├── config/ # Use case singleton instances, constants
|
|
141
|
+
│ │ ├── utils/
|
|
142
|
+
│ │ ├── validation/
|
|
143
|
+
│ │ ├── providers/
|
|
144
|
+
│ │ └── index.ts
|
|
145
|
+
│ ├── di/
|
|
146
|
+
│ │ └── [Feature]Container.ts
|
|
147
|
+
│ ├── assets/
|
|
148
|
+
│ ├── __tests__/
|
|
149
|
+
│ └── index.ts # Public API — barrel exports everything public
|
|
150
|
+
│
|
|
151
|
+
├── shared/
|
|
152
|
+
│ └── theme/ # Design tokens only — NO logic, NO components
|
|
153
|
+
│
|
|
154
|
+
├── services/
|
|
155
|
+
│ └── [ExternalServiceHelper].ts
|
|
156
|
+
│
|
|
157
|
+
├── constants/
|
|
158
|
+
│ ├── [eventNames].ts
|
|
159
|
+
│ └── [propertyKeys].ts
|
|
160
|
+
│
|
|
161
|
+
└── types/
|
|
162
|
+
├── env.d.ts
|
|
163
|
+
└── thirdParty.d.ts
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Monorepo variant (Yarn Workspaces) — only when the target repo already uses one
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
packages/
|
|
170
|
+
├── common-domain/ # Pure TypeScript: BaseEntity, BaseUseCase, Result monad, error types
|
|
171
|
+
├── common-data/ # HTTP infrastructure, storage adapters, base repository impl
|
|
172
|
+
└── common-presentation/ # Reusable UI: components, theme, hooks, icons
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Dependency Rules (RN-specific detail on top of architecture-core.md)
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
packages/common-domain ← no external deps
|
|
181
|
+
packages/common-data ← imports common-domain only
|
|
182
|
+
packages/common-presentation ← imports common-domain, common-data
|
|
183
|
+
|
|
184
|
+
core/ ← imports packages/* only; MUST NOT import features/*
|
|
185
|
+
features/[x]/domain/ ← imports common-domain ONLY; zero React/Redux/Axios
|
|
186
|
+
features/[x]/data/ ← imports core/, common-data, own domain/
|
|
187
|
+
features/[x]/presentation/ ← imports core/, packages/*, own domain/, own data/
|
|
188
|
+
features/[x]/di/ ← imports all layers of own feature + core/
|
|
189
|
+
app/ ← imports all features/* (orchestration only)
|
|
190
|
+
shared/ ← no imports from features/ or core/
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## TypeScript Path Aliases
|
|
194
|
+
|
|
195
|
+
Read `tsconfig.json` for existing paths before inventing any. If none exist, propose (and confirm
|
|
196
|
+
with the user before writing):
|
|
197
|
+
|
|
198
|
+
```json
|
|
199
|
+
{
|
|
200
|
+
"compilerOptions": {
|
|
201
|
+
"baseUrl": "./src",
|
|
202
|
+
"paths": {
|
|
203
|
+
"@/*": ["./*"],
|
|
204
|
+
"@core/*": ["./core/*"],
|
|
205
|
+
"@features/*": ["./features/*"],
|
|
206
|
+
"@app": ["./app/index"],
|
|
207
|
+
"@app/*": ["./app/*"],
|
|
208
|
+
"@shared/*": ["./shared/*"],
|
|
209
|
+
"@services/*": ["./services/*"]
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Mirror any alias exactly in `babel.config.js` via the `module-resolver` plugin.
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Naming Extensions (RN-specific, on top of architecture-core.md)
|
|
220
|
+
|
|
221
|
+
| Artifact | Convention | Example |
|
|
222
|
+
|----------|------------|---------|
|
|
223
|
+
| Feature folder | kebab-case | `portfolio-summary`, `user-profile` |
|
|
224
|
+
| Entity file | PascalCase | `HoldingEntity.ts` |
|
|
225
|
+
| Repository interface | `I` + PascalCase + `Repository` | `IHoldingRepository.ts` |
|
|
226
|
+
| Repository implementation | PascalCase + `RepositoryImpl` | `HoldingRepositoryImpl.ts` |
|
|
227
|
+
| Use case file | PascalCase + `UseCase` | `GetHoldingSummaryUseCase.ts` |
|
|
228
|
+
| API client class | PascalCase + `ApiClient` | `HoldingApiClient.ts` |
|
|
229
|
+
| DTO file | PascalCase + `DTO` | `HoldingSummaryDTO.ts` |
|
|
230
|
+
| Mapper function | `mapTo` prefix | `mapHoldingDTOToDomain` |
|
|
231
|
+
| Redux slice file | camelCase + `Slice` | `holdingSlice.ts` |
|
|
232
|
+
| Redux thunks file | camelCase + `Thunks` | `holdingThunks.ts` |
|
|
233
|
+
| Selectors file | camelCase + `Selectors` (optional) | `holdingSelectors.ts` |
|
|
234
|
+
| Screen file | PascalCase + `Screen` | `HoldingDetailScreen.tsx` |
|
|
235
|
+
| Hook file | `use` + PascalCase | `useHoldingSummary.ts` |
|
|
236
|
+
| ViewModel hook | `use` + PascalCase + `ViewModel` | `useHoldingViewModel.ts` |
|
|
237
|
+
| Navigator param list | PascalCase + `StackParamList` | `HoldingStackParamList` |
|
|
238
|
+
| Route names file | camelCase + `Routes` | `holdingRoutes.ts` |
|
|
239
|
+
| DI container (getter pattern) | camelCase + `Container` | `holdingContainer.ts` |
|
|
240
|
+
| DI container (factory pattern) | camelCase + `DI` | `holdingDI.ts` |
|
|
241
|
+
| Selector function | `select` + PascalCase | `selectHoldingTotal` |
|
|
242
|
+
| Mock file | `.mock.ts` suffix or `/mock/` folder | `holdingResponse.mock.ts` |
|
|
243
|
+
| Test file | `.test.ts` or `.spec.ts` | `HoldingRepositoryImpl.test.ts` |
|
|
244
|
+
| ThunkExtra interface | PascalCase + `ThunkExtra` | `HoldingThunkExtra` |
|
|
245
|
+
| Loading state | `'idle' \| 'loading' \| 'succeeded' \| 'failed'` string union | never a `boolean` |
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## DI Pattern A — Lazy Getter Object (most features)
|
|
250
|
+
|
|
251
|
+
```ts
|
|
252
|
+
// src/features/[feature]/di/[feature]Container.ts
|
|
253
|
+
import { [Feature]ApiClient } from '../data/api/[Feature]ApiClient';
|
|
254
|
+
import { [Feature]RepositoryImpl } from '../data/repositories/[Feature]RepositoryImpl';
|
|
255
|
+
import { Get[Item]UseCase } from '../domain/usecases/Get[Item]UseCase';
|
|
256
|
+
|
|
257
|
+
let _apiClient: [Feature]ApiClient | null = null;
|
|
258
|
+
let _repo: [Feature]RepositoryImpl | null = null;
|
|
259
|
+
|
|
260
|
+
function getApiClient(): [Feature]ApiClient {
|
|
261
|
+
if (!_apiClient) _apiClient = new [Feature]ApiClient();
|
|
262
|
+
return _apiClient;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function getRepository(): [Feature]RepositoryImpl {
|
|
266
|
+
if (!_repo) _repo = new [Feature]RepositoryImpl(getApiClient());
|
|
267
|
+
return _repo;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export const [feature]Container = {
|
|
271
|
+
get get[Item]UseCase() {
|
|
272
|
+
return new Get[Item]UseCase(getRepository());
|
|
273
|
+
},
|
|
274
|
+
};
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## DI Pattern B — Factory Function (features with multiple ThunkExtra types)
|
|
278
|
+
|
|
279
|
+
```ts
|
|
280
|
+
// src/features/[feature]/di/[feature]DI.ts
|
|
281
|
+
import { [Feature]DataSource } from '../data/datasources/[Feature]DataSource';
|
|
282
|
+
import { [Feature]RepositoryImpl } from '../data/repositories/[Feature]RepositoryImpl';
|
|
283
|
+
import { Get[Item]UseCase } from '../domain/useCases/[Feature]UseCases';
|
|
284
|
+
import type { [Feature]ThunkExtra } from '../presentation/state/[concern]Thunks';
|
|
285
|
+
|
|
286
|
+
export function create[Feature]ThunkExtra(): [Feature]ThunkExtra {
|
|
287
|
+
const dataSource = new [Feature]DataSource();
|
|
288
|
+
const repository = new [Feature]RepositoryImpl(dataSource);
|
|
289
|
+
const get[Item]UseCase = new Get[Item]UseCase(repository);
|
|
290
|
+
return { get[Item]UseCase };
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Auth — Special Case
|
|
295
|
+
|
|
296
|
+
Auth does not use either container pattern. Creates module-level singletons in
|
|
297
|
+
`presentation/config/useCaseInstances.ts` and re-exports from `app/di/container.ts`.
|
|
298
|
+
|
|
299
|
+
**Token getter pattern (prevents circular imports):**
|
|
300
|
+
|
|
301
|
+
```ts
|
|
302
|
+
// src/core/api/tokenProvider.ts
|
|
303
|
+
let _getToken: (() => string | null) | null = null;
|
|
304
|
+
export const setTokenGetter = (fn: () => string | null) => { _getToken = fn; };
|
|
305
|
+
export const getToken = (): string | null => _getToken?.() ?? null;
|
|
306
|
+
|
|
307
|
+
// Auth feature registers during session restore:
|
|
308
|
+
setTokenGetter(() => authCache.getAccessToken());
|
|
309
|
+
|
|
310
|
+
// Axios interceptor reads it:
|
|
311
|
+
const token = getToken();
|
|
312
|
+
if (token) config.headers.Authorization = `Bearer ${token}`;
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
## Redux Store Configuration
|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
// src/app/state/store.ts
|
|
321
|
+
import { configureStore } from '@reduxjs/toolkit';
|
|
322
|
+
import { setupListeners } from '@reduxjs/toolkit/query';
|
|
323
|
+
import { baseApiSlice } from '@core/cache/baseApiSlice';
|
|
324
|
+
import { createPersistenceMiddleware } from '@core/cache/persistenceMiddleware';
|
|
325
|
+
import persistenceRegistry from '@core/cache/persistenceRegistry';
|
|
326
|
+
import { [feature]Reducer } from '@features/[feature]/presentation/state';
|
|
327
|
+
import { [feature]Container } from '@features/[feature]/di/[Feature]Container';
|
|
328
|
+
|
|
329
|
+
export const store = configureStore({
|
|
330
|
+
reducer: {
|
|
331
|
+
[feature]: [feature]Reducer,
|
|
332
|
+
[baseApiSlice.reducerPath]: baseApiSlice.reducer,
|
|
333
|
+
},
|
|
334
|
+
middleware: (getDefault) =>
|
|
335
|
+
getDefault({
|
|
336
|
+
thunk: { extraArgument: { ...[feature]Container } },
|
|
337
|
+
serializableCheck: { ignoredActions: [], ignoredPaths: [] },
|
|
338
|
+
})
|
|
339
|
+
.concat(baseApiSlice.middleware)
|
|
340
|
+
.concat(createPersistenceMiddleware(persistenceRegistry)),
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
setupListeners(store.dispatch);
|
|
344
|
+
|
|
345
|
+
export type RootState = ReturnType<typeof store.getState>;
|
|
346
|
+
export type AppDispatch = typeof store.dispatch;
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## Redux Slice Pattern
|
|
350
|
+
|
|
351
|
+
```ts
|
|
352
|
+
// src/features/[feature]/presentation/state/[concern]Slice.ts
|
|
353
|
+
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
|
354
|
+
import { fetch[Concern] } from './[concern]Thunks';
|
|
355
|
+
import type { [Concern]Entity } from '../../domain/entities';
|
|
356
|
+
|
|
357
|
+
export type LoadingState = 'idle' | 'loading' | 'succeeded' | 'failed';
|
|
358
|
+
|
|
359
|
+
interface [Concern]State {
|
|
360
|
+
data: [Concern]Entity | null;
|
|
361
|
+
loading: LoadingState;
|
|
362
|
+
error: string | null;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const initialState: [Concern]State = { data: null, loading: 'idle', error: null };
|
|
366
|
+
|
|
367
|
+
const [concern]Slice = createSlice({
|
|
368
|
+
name: '[feature]/[concern]',
|
|
369
|
+
initialState,
|
|
370
|
+
reducers: {
|
|
371
|
+
reset[Concern]: () => initialState,
|
|
372
|
+
set[Concern]: (state, action: PayloadAction<[Concern]Entity>) => {
|
|
373
|
+
state.data = action.payload;
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
extraReducers: (builder) => {
|
|
377
|
+
builder
|
|
378
|
+
.addCase(fetch[Concern].pending, (state) => { state.loading = 'loading'; state.error = null; })
|
|
379
|
+
.addCase(fetch[Concern].fulfilled, (state, action) => { state.loading = 'succeeded'; state.data = action.payload; })
|
|
380
|
+
.addCase(fetch[Concern].rejected, (state, action) => { state.loading = 'failed'; state.error = action.error.message ?? 'Unknown error'; });
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
export const { reset[Concern], set[Concern] } = [concern]Slice.actions;
|
|
385
|
+
export const [concern]Reducer = [concern]Slice.reducer;
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
## Redux Thunk Pattern
|
|
389
|
+
|
|
390
|
+
```ts
|
|
391
|
+
// src/features/[feature]/presentation/state/[concern]Thunks.ts
|
|
392
|
+
import { createAsyncThunk } from '@reduxjs/toolkit';
|
|
393
|
+
import type { Get[Concern]UseCase } from '../../domain/usecases/Get[Concern]UseCase';
|
|
394
|
+
|
|
395
|
+
// ThunkExtra interface defined HERE — not in the DI container file
|
|
396
|
+
export interface [Feature]ThunkExtra {
|
|
397
|
+
get[Concern]UseCase: Get[Concern]UseCase;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
export const fetch[Concern] = createAsyncThunk(
|
|
401
|
+
'[feature]/fetch[Concern]',
|
|
402
|
+
async (args: [RequestArgs], thunkAPI) => {
|
|
403
|
+
const { get[Concern]UseCase } = thunkAPI.extra as [Feature]ThunkExtra;
|
|
404
|
+
return get[Concern]UseCase.execute(args);
|
|
405
|
+
},
|
|
406
|
+
);
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
## Selector Pattern
|
|
410
|
+
|
|
411
|
+
```ts
|
|
412
|
+
// [concern]Selectors.ts — raw state reads
|
|
413
|
+
import type { RootState } from '@app/state/store';
|
|
414
|
+
|
|
415
|
+
export const select[Concern]Data = (state: RootState) => state.[feature].[concern].data;
|
|
416
|
+
export const select[Concern]Loading = (state: RootState) => state.[feature].[concern].loading;
|
|
417
|
+
export const select[Concern]Error = (state: RootState) => state.[feature].[concern].error;
|
|
418
|
+
|
|
419
|
+
// [concern]ViewModelSelectors.ts — composed selectors for screens
|
|
420
|
+
import { createSelector } from '@reduxjs/toolkit';
|
|
421
|
+
export const select[Concern]ViewModel = createSelector(
|
|
422
|
+
[select[Concern]Data, select[Concern]Loading, select[Concern]Error],
|
|
423
|
+
(data, loading, error) => ({ data, loading, error }),
|
|
424
|
+
);
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
## RTK Query Endpoint Pattern
|
|
428
|
+
|
|
429
|
+
```ts
|
|
430
|
+
// src/features/[feature]/data/api/[feature]ApiSlice.ts
|
|
431
|
+
import { baseApiSlice } from '@core/cache/baseApiSlice';
|
|
432
|
+
import type { [Item]DTO } from '../dtos/[Item]DTO';
|
|
433
|
+
import type { [Item]Entity } from '../../domain/entities';
|
|
434
|
+
import { map[Item]DTOToDomain } from '../mappers/[feature]Mappers';
|
|
435
|
+
|
|
436
|
+
export const [feature]ApiSlice = baseApiSlice.injectEndpoints({
|
|
437
|
+
endpoints: (build) => ({
|
|
438
|
+
get[Item]: build.query<[Item]Entity, [RequestArgs]>({
|
|
439
|
+
query: (args) => ({ url: '/[endpoint]', method: 'POST', data: args }),
|
|
440
|
+
transformResponse: (raw: [Item]DTO) => map[Item]DTOToDomain(raw),
|
|
441
|
+
keepUnusedDataFor: 300,
|
|
442
|
+
}),
|
|
443
|
+
}),
|
|
444
|
+
overrideExisting: false,
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
export const { useGet[Item]Query } = [feature]ApiSlice;
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
## Domain Layer Pattern
|
|
451
|
+
|
|
452
|
+
```ts
|
|
453
|
+
// domain/repositories/I[Feature]Repository.ts
|
|
454
|
+
export interface I[Feature]Repository {
|
|
455
|
+
get[Item](args: [RequestArgs]): Promise<[Item]Entity>;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// domain/usecases/Get[Item]UseCase.ts
|
|
459
|
+
export class Get[Item]UseCase {
|
|
460
|
+
constructor(private readonly repository: I[Feature]Repository) {}
|
|
461
|
+
async execute(args: [RequestArgs]): Promise<[Item]Entity> {
|
|
462
|
+
return this.repository.get[Item](args);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
## Data Layer Pattern
|
|
468
|
+
|
|
469
|
+
```ts
|
|
470
|
+
// data/api/[Feature]ApiClient.ts
|
|
471
|
+
import { axiosClient } from '@core/api/axiosClient';
|
|
472
|
+
|
|
473
|
+
export class [Feature]ApiClient {
|
|
474
|
+
async get[Item](args: [RequestArgs]): Promise<[Item]DTO> {
|
|
475
|
+
const response = await axiosClient.post('/[endpoint]', args);
|
|
476
|
+
return response.data;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// data/repositories/[Feature]RepositoryImpl.ts
|
|
481
|
+
import type { I[Feature]Repository } from '../../domain/repositories/I[Feature]Repository';
|
|
482
|
+
import type { [Feature]ApiClient } from '../api/[Feature]ApiClient';
|
|
483
|
+
import { map[Item]DTOToDomain } from '../mappers/[feature]Mappers';
|
|
484
|
+
|
|
485
|
+
export class [Feature]RepositoryImpl implements I[Feature]Repository {
|
|
486
|
+
constructor(private readonly apiClient: [Feature]ApiClient) {}
|
|
487
|
+
async get[Item](args: [RequestArgs]): Promise<[Item]Entity> {
|
|
488
|
+
const dto = await this.apiClient.get[Item](args);
|
|
489
|
+
return map[Item]DTOToDomain(dto);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
## Navigation Pattern
|
|
495
|
+
|
|
496
|
+
```ts
|
|
497
|
+
// presentation/navigation/[feature]Routes.ts
|
|
498
|
+
import type { NativeStackScreenProps } from '@react-navigation/native-stack';
|
|
499
|
+
|
|
500
|
+
export type [Feature]StackParamList = {
|
|
501
|
+
[Feature]Main: undefined;
|
|
502
|
+
[Feature]Detail: { id: string; entrySource?: 'list' | 'search' };
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
export type [Feature]MainScreenProps = NativeStackScreenProps<[Feature]StackParamList, '[Feature]Main'>;
|
|
506
|
+
export type [Feature]DetailScreenProps = NativeStackScreenProps<[Feature]StackParamList, '[Feature]Detail'>;
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
Tab bar visibility: call `getFocusedRouteNameFromRoute()` in `MainTabsNavigator` to hide the tab bar
|
|
510
|
+
when a detail screen is focused.
|
|
511
|
+
|
|
512
|
+
## Typed Store Hooks
|
|
513
|
+
|
|
514
|
+
```ts
|
|
515
|
+
// src/core/store/hooks.ts
|
|
516
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
517
|
+
import type { RootState, AppDispatch } from '@app/state/store';
|
|
518
|
+
|
|
519
|
+
export const useAppDispatch = () => useDispatch<AppDispatch>();
|
|
520
|
+
export const useAppSelector = <T>(selector: (state: RootState) => T): T => useSelector(selector);
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
---
|
|
524
|
+
|
|
525
|
+
## Offline Resilience Rules
|
|
526
|
+
|
|
527
|
+
1. RTK Query global: `refetchOnReconnect: true` + call `setupListeners(store.dispatch)` after store creation.
|
|
528
|
+
2. Mutating operations when `NetInfo.isConnected === false` → enqueue in offline queue service.
|
|
529
|
+
3. Offline queue persists to MMKV (survives app kills).
|
|
530
|
+
4. `OfflineOverlayController` intercepts Android hardware back button to dismiss overlay, not navigate.
|
|
531
|
+
5. On reconnect, queue flushes automatically — UI does not trigger it.
|
|
532
|
+
|
|
533
|
+
## Security Rules (mobile-specific — on top of architecture-core.md)
|
|
534
|
+
|
|
535
|
+
1. All tokens in MMKV with AES-256 encryption — never AsyncStorage.
|
|
536
|
+
2. Every outbound request signed with nonce (UUID v4) and correlation ID header.
|
|
537
|
+
3. Authorization header: `Bearer <token>`.
|
|
538
|
+
4. Device security check (root/jailbreak) runs at app boot before any network calls.
|
|
539
|
+
5. SSL pinning applied at the Axios adapter layer — never `rejectUnauthorized: false`.
|
|
540
|
+
6. Encryption keys managed via native Keychain/Keystore — never in the JS bundle.
|
|
541
|
+
|
|
542
|
+
---
|
|
543
|
+
|
|
544
|
+
## Anti-Patterns (RN-specific, on top of architecture-core.md)
|
|
545
|
+
|
|
546
|
+
- **Never** use `useDispatch` / `useSelector` directly — always use `useAppDispatch` / `useAppSelector`.
|
|
547
|
+
- **Never** use a `boolean` for loading state in a Redux slice — use `'idle' | 'loading' | 'succeeded' | 'failed'`.
|
|
548
|
+
- **Never** define `ThunkExtra` inside the DI container file — define it in the thunks file alongside the thunks that use it.
|
|
549
|
+
- **Never** call `.get[Feature]HookDependencies()` from inside a thunk — cast `thunkAPI.extra` directly to the `ThunkExtra` type.
|
|
550
|
+
- **Never** use the class-based `getInstance()` singleton pattern for feature DI containers — use module-level lazy variables with getter objects (Pattern A) or factory functions (Pattern B).
|
|
551
|
+
- **Never** create use cases as module-level singletons in a feature container (except auth) — use ES6 getter properties that create a fresh instance per access.
|
|
552
|
+
- **Never** store secrets or tokens in `AsyncStorage` — use MMKV with AES-256 encryption or Keychain/Keystore.
|
|
553
|
+
- **Never** store encryption keys in the JS bundle — use native Keychain/Keystore.
|
|
554
|
+
- **Never** bypass SSL pinning in production code — even for testing.
|
|
555
|
+
- **Never** use `rejectUnauthorized: false` in Axios config.
|
|
556
|
+
|
|
557
|
+
---
|
|
558
|
+
|
|
559
|
+
## Worked Example
|
|
560
|
+
|
|
561
|
+
**Request**: "Add a watchlist feature with ability to fetch and display watchlist items."
|
|
562
|
+
|
|
563
|
+
**Artifacts**:
|
|
564
|
+
- Domain: `WatchlistItemEntity`, `IWatchlistRepository`, `GetWatchlistUseCase`
|
|
565
|
+
- Data: `WatchlistApiClient`, `WatchlistItemDTO`, `mapWatchlistDTOToDomain`, `WatchlistRepositoryImpl`
|
|
566
|
+
- Presentation: `watchlistSlice`, `watchlistThunks`, `watchlistSelectors`, `WatchlistScreen`
|
|
567
|
+
- DI: `watchlistContainer` (Pattern A)
|
|
568
|
+
|
|
569
|
+
**Output**:
|
|
570
|
+
```
|
|
571
|
+
Created:
|
|
572
|
+
src/features/watchlist/domain/entities/WatchlistItemEntity.ts
|
|
573
|
+
src/features/watchlist/domain/repositories/IWatchlistRepository.ts
|
|
574
|
+
src/features/watchlist/domain/usecases/GetWatchlistUseCase.ts
|
|
575
|
+
src/features/watchlist/data/api/WatchlistApiClient.ts
|
|
576
|
+
src/features/watchlist/data/dtos/WatchlistItemDTO.ts
|
|
577
|
+
src/features/watchlist/data/mappers/watchlistMappers.ts
|
|
578
|
+
src/features/watchlist/data/repositories/WatchlistRepositoryImpl.ts
|
|
579
|
+
src/features/watchlist/presentation/state/watchlistSlice.ts
|
|
580
|
+
src/features/watchlist/presentation/state/watchlistThunks.ts
|
|
581
|
+
src/features/watchlist/presentation/state/watchlistSelectors.ts
|
|
582
|
+
src/features/watchlist/presentation/screens/WatchlistScreen.tsx
|
|
583
|
+
src/features/watchlist/di/watchlistContainer.ts
|
|
584
|
+
src/features/watchlist/index.ts
|
|
585
|
+
|
|
586
|
+
Updated:
|
|
587
|
+
src/app/state/store.ts (added reducer + container to extraArgument)
|
|
588
|
+
|
|
589
|
+
Dependency check: PASS — no layer boundary violations detected
|
|
590
|
+
```
|