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,2741 @@
|
|
|
1
|
+
# feature-generator
|
|
2
|
+
|
|
3
|
+
## Skill Metadata
|
|
4
|
+
|
|
5
|
+
```yaml
|
|
6
|
+
name: feature-generator
|
|
7
|
+
version: 3.1.0
|
|
8
|
+
description: >
|
|
9
|
+
Supports two generation modes: single-screen and multi-screen module.
|
|
10
|
+
In both modes: asks 5 mandatory questions (design source, user stories, test
|
|
11
|
+
cases, business logic, API details + mapping), crawls the repository to build
|
|
12
|
+
an Architecture Fingerprint, then generates every layer of production-ready
|
|
13
|
+
React Native TypeScript code that fits the repository as-is.
|
|
14
|
+
In module mode: collects Q2–Q5 per screen, deduplicates shared entities,
|
|
15
|
+
generates one DI bootstrap for the whole module, and registers all screens
|
|
16
|
+
in the navigator in a single pass.
|
|
17
|
+
After generation, runs a post-completion validation phase that checks file
|
|
18
|
+
existence, imports, mapping coverage, business logic, test cases, user stories,
|
|
19
|
+
and code quality — auto-fixing every issue before reporting completion.
|
|
20
|
+
priority: high
|
|
21
|
+
auto-trigger:
|
|
22
|
+
- "generate feature from spec"
|
|
23
|
+
- "create feature from md"
|
|
24
|
+
- "build screen from spec"
|
|
25
|
+
- "md-driven feature"
|
|
26
|
+
- "spec to feature"
|
|
27
|
+
- "generate module from spec"
|
|
28
|
+
- "build module from md"
|
|
29
|
+
- "create module"
|
|
30
|
+
- "multi-screen feature"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Assets and References
|
|
36
|
+
|
|
37
|
+
| File | Purpose |
|
|
38
|
+
|---|---|
|
|
39
|
+
| `assets/spec-template.md` | Blank fillable spec template for users. Copy and fill before invoking the skill. |
|
|
40
|
+
| `assets/fingerprint-schema.json` | JSON Schema (draft-07) for the Architecture Fingerprint produced in Phase 0.K. Use to validate the fingerprint before proceeding to Phase 1. |
|
|
41
|
+
| `assets/transform-rules.json` | Configurable map of response mapping transform keywords to TypeScript expressions. Read in Phase 7.2. |
|
|
42
|
+
| `references/api-envelope-patterns.md` | Full code templates for every `apiLib` + `envelopeStyle` combination. Read in Phase 4.6. |
|
|
43
|
+
| `references/state-library-patterns.md` | Full code templates for Zustand, MobX, Context API, and RTK direct-import. Read in Phase 5 when `stateLib` is not `redux-toolkit/thunk-extra`. |
|
|
44
|
+
| `references/screen-logic-patterns.md` | React Native JSX implementations for every screen logic pattern. Read in Phase 7.3. |
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Overview
|
|
49
|
+
|
|
50
|
+
This skill turns a human-authored Markdown specification into production-ready
|
|
51
|
+
React Native TypeScript code that fits the repository as-is — not as assumed.
|
|
52
|
+
|
|
53
|
+
**Core philosophy**: Every decision about folder layout, import paths, state
|
|
54
|
+
management, API calling convention, naming style, and navigation must come from
|
|
55
|
+
reading the actual repository. Nothing is assumed. The skill crawls first and
|
|
56
|
+
codes second.
|
|
57
|
+
|
|
58
|
+
### Generation Modes
|
|
59
|
+
|
|
60
|
+
This skill operates in one of two modes, determined during PHASE -1 Q1:
|
|
61
|
+
|
|
62
|
+
| Mode | When to use | What gets generated |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| **single** | One screen, one API, self-contained | All layers for one screen |
|
|
65
|
+
| **module** | Multiple screens that share a feature folder (e.g. FD Booking with Details, Payment, Confirmation screens) | All layers for every screen + one shared DI bootstrap for the module + shared entities deduplicated across screens + all screens registered in navigator in one pass |
|
|
66
|
+
|
|
67
|
+
**Module folder structure (example — `fdBooking` module with 3 screens):**
|
|
68
|
+
```
|
|
69
|
+
features/
|
|
70
|
+
fdBooking/
|
|
71
|
+
domain/
|
|
72
|
+
models/ ← shared entities used by more than one screen live here
|
|
73
|
+
FdBookingItem.ts
|
|
74
|
+
repositories/ ← one repo interface per screen
|
|
75
|
+
useCases/ ← one use case per screen
|
|
76
|
+
data/
|
|
77
|
+
dtos/ ← one DTO per screen
|
|
78
|
+
mappers/ ← one mapper per screen
|
|
79
|
+
datasources/ ← one datasource per screen + shared endpoints + mockConfig
|
|
80
|
+
state/ ← one slice per screen (or one shared slice if confirmed by user)
|
|
81
|
+
store/
|
|
82
|
+
selectors/
|
|
83
|
+
presentation/
|
|
84
|
+
screens/
|
|
85
|
+
FdDetailsScreen/
|
|
86
|
+
PaymentScreen/
|
|
87
|
+
ConfirmationScreen/
|
|
88
|
+
diBootstrap.ts ← ONE file for the whole module (not per-screen)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
In single mode, the module folder contains exactly one screen and one of each layer file.
|
|
92
|
+
|
|
93
|
+
### Input
|
|
94
|
+
|
|
95
|
+
All inputs are collected via the **Mandatory User Interview (PHASE -1)** below.
|
|
96
|
+
Do NOT proceed to PHASE 0 until all 5 questions have been answered and confirmed.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## PHASE -1 — Mandatory User Interview
|
|
101
|
+
|
|
102
|
+
> **This phase is REQUIRED. Do NOT skip or reorder any question.**
|
|
103
|
+
> Do NOT assume, infer, or hallucinate any answer to any question.
|
|
104
|
+
> If the user's answer is incomplete, ambiguous, or missing required fields — ask a targeted follow-up before moving on.
|
|
105
|
+
> All 5 questions MUST be answered before proceeding to PHASE 0.
|
|
106
|
+
> Proceed ONLY after you have explicit answers (or an explicit WARN-acknowledged skip) for every question.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
### QUESTION 1 — Generation Mode & Design Source
|
|
111
|
+
|
|
112
|
+
Ask the user:
|
|
113
|
+
|
|
114
|
+
> "Are you generating a **single screen** or a **module** (multiple screens that belong to the same feature)?
|
|
115
|
+
>
|
|
116
|
+
> - **Single screen** — one screen, one API endpoint
|
|
117
|
+
> - **Module** — multiple screens under one feature folder (e.g. FD Booking: Details screen + Payment screen + Confirmation screen)
|
|
118
|
+
>
|
|
119
|
+
> Please also tell me the name(s):"
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
#### If mode = single:
|
|
124
|
+
|
|
125
|
+
Ask:
|
|
126
|
+
> "What is the PascalCase name for the new screen? (e.g. `TransactionHistory`, `FundDetail`, `UserProfile`)
|
|
127
|
+
>
|
|
128
|
+
> Also provide the design source:
|
|
129
|
+
> - **Spec MD files** — file path(s) to filled spec Markdown files
|
|
130
|
+
> - **Existing screen paths** — relative paths to existing screens to use as UI structure reference
|
|
131
|
+
>
|
|
132
|
+
> Provide at least one source."
|
|
133
|
+
|
|
134
|
+
**Collect:**
|
|
135
|
+
```
|
|
136
|
+
GENERATION_MODE: single
|
|
137
|
+
SCREEN_NAME: <PascalCase name>
|
|
138
|
+
DESIGN_SOURCE_TYPE: md-file | screen-path | mixed
|
|
139
|
+
SPEC_MD_FILES: [path1, path2, ...] or []
|
|
140
|
+
SCREEN_REF_PATHS: [path1, path2, ...] or []
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Behaviour: proceed through Q2–Q5 once (for this single screen), then PHASE 0 → PHASE 1–9.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
#### If mode = module:
|
|
148
|
+
|
|
149
|
+
Ask:
|
|
150
|
+
> "What is the PascalCase name of the module? (e.g. `FdBooking`, `Portfolio`, `Onboarding`)
|
|
151
|
+
>
|
|
152
|
+
> How many screens does this module contain? List each screen name (PascalCase) and its design source.
|
|
153
|
+
> For each screen provide:
|
|
154
|
+
> - Screen name (PascalCase)
|
|
155
|
+
> - Spec MD file path (optional)
|
|
156
|
+
> - Existing screen ref path (optional)
|
|
157
|
+
>
|
|
158
|
+
> You must provide at least one source (MD file or screen ref) per screen."
|
|
159
|
+
|
|
160
|
+
**Collect:**
|
|
161
|
+
```
|
|
162
|
+
GENERATION_MODE: module
|
|
163
|
+
MODULE_NAME: <PascalCase module name — e.g. FdBooking>
|
|
164
|
+
SCREENS: [
|
|
165
|
+
{
|
|
166
|
+
screenName: <PascalCase>,
|
|
167
|
+
specMdFiles: [path, ...] or [],
|
|
168
|
+
screenRefPaths: [path, ...] or []
|
|
169
|
+
},
|
|
170
|
+
...
|
|
171
|
+
]
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
FATAL if `MODULE_NAME` not provided — re-ask.
|
|
175
|
+
FATAL if `SCREENS` list is empty — re-ask.
|
|
176
|
+
FATAL if any screen has neither `specMdFiles` nor `screenRefPaths` — re-ask for that screen.
|
|
177
|
+
|
|
178
|
+
Behaviour:
|
|
179
|
+
- **MD spec files**: read in PHASE 1 as the primary spec source per screen. The `Screen Name` field inside the MD overrides the interview screen name only if different.
|
|
180
|
+
- **Screen paths**: read in PHASE 0 step 0.L. Mirror component structure, StyleSheet, imports. Do NOT generate new layout — replicate existing pattern.
|
|
181
|
+
- Q2–Q5 are collected **per screen** (see below).
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
> **IMPORTANT for both modes**: Figma URLs are NOT supported. Do not use any Figma MCP tools.
|
|
186
|
+
> If the user provides a Figma URL, ask them to provide an existing screen path or a filled spec MD file instead.
|
|
187
|
+
|
|
188
|
+
FATAL if no source provided for any screen — re-ask until resolved.
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
> ### Module mode — Q2 through Q5 are collected PER SCREEN
|
|
193
|
+
>
|
|
194
|
+
> When `GENERATION_MODE = module`, after Q1 you MUST loop through each screen in
|
|
195
|
+
> `SCREENS[]` and ask Q2–Q5 for that screen before moving to the next.
|
|
196
|
+
>
|
|
197
|
+
> Display a clear heading before each screen's questions:
|
|
198
|
+
> ```
|
|
199
|
+
> ─── Screen N / Total: <ScreenName> ────────────────────────────────
|
|
200
|
+
> ```
|
|
201
|
+
>
|
|
202
|
+
> After collecting Q2–Q5 for ALL screens, proceed to the PHASE -1 Completion Gate.
|
|
203
|
+
> Do NOT skip any screen. Do NOT run PHASE 0 until every screen is fully answered.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
### QUESTION 2 — User Stories
|
|
208
|
+
|
|
209
|
+
Ask the user:
|
|
210
|
+
|
|
211
|
+
> "Please provide the user stories for this feature. You can:
|
|
212
|
+
> - Paste user story text directly (e.g. 'As a user, I want to see my transactions so that I can track my spending')
|
|
213
|
+
> - Provide a file path to a user stories Markdown file
|
|
214
|
+
>
|
|
215
|
+
> User stories define WHAT the feature must do from the user's perspective and drive screen logic generation."
|
|
216
|
+
|
|
217
|
+
**Collect:**
|
|
218
|
+
```
|
|
219
|
+
USER_STORIES_SOURCE: inline | file | none
|
|
220
|
+
USER_STORIES_FILE: <path or null>
|
|
221
|
+
USER_STORIES_TEXT: <inline text or null>
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Parse into structured entries:
|
|
225
|
+
```
|
|
226
|
+
USER_STORIES: [
|
|
227
|
+
{ id: "US-1", role: "<who>", action: "<wants to>", benefit: "<so that>" },
|
|
228
|
+
...
|
|
229
|
+
]
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Use user stories to:
|
|
233
|
+
- Drive screen logic completeness check in PHASE 7.3
|
|
234
|
+
- Validate implementation coverage in PHASE 10.6
|
|
235
|
+
|
|
236
|
+
WARN if not provided — log `WARN: No user stories provided. Screen logic will be derived from the spec MD file and Q5 API mapping only.` and continue.
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
### QUESTION 3 — Test Cases
|
|
241
|
+
|
|
242
|
+
Ask the user:
|
|
243
|
+
|
|
244
|
+
> "Please provide the test cases for this feature. You can:
|
|
245
|
+
> - Paste test case descriptions directly (e.g. 'When user has no transactions, show empty state')
|
|
246
|
+
> - Provide a file path to a test cases Markdown file
|
|
247
|
+
> - Provide paths to existing test files (*.test.ts, *.spec.ts)
|
|
248
|
+
>
|
|
249
|
+
> Test cases define expected behaviour and edge cases the implementation must satisfy."
|
|
250
|
+
|
|
251
|
+
**Collect:**
|
|
252
|
+
```
|
|
253
|
+
TEST_CASES_SOURCE: inline | file | test-files | none
|
|
254
|
+
TEST_CASES_FILE: <path or null>
|
|
255
|
+
TEST_CASES_TEXT: <inline text or null>
|
|
256
|
+
TEST_FILE_PATHS: [path1, ...] or []
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Parse into structured entries:
|
|
260
|
+
```
|
|
261
|
+
TEST_CASES: [
|
|
262
|
+
{ id: "TC-1", given: "...", when: "...", then: "...", type: "happy | edge | error" },
|
|
263
|
+
...
|
|
264
|
+
]
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Use test cases to:
|
|
268
|
+
- Validate business logic guard clauses in PHASE 3.3
|
|
269
|
+
- Validate screen logic completeness in PHASE 7.3
|
|
270
|
+
- Drive PHASE 10.5 post-completion checks
|
|
271
|
+
|
|
272
|
+
WARN if not provided — log `WARN: No test cases provided. Post-completion validation will skip TC coverage.` and continue.
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
### QUESTION 4 — Business Logic
|
|
277
|
+
|
|
278
|
+
Ask the user:
|
|
279
|
+
|
|
280
|
+
> "Please describe ALL business logic rules for this feature. Include:
|
|
281
|
+
> - Validation rules (e.g. 'Amount must be greater than 0')
|
|
282
|
+
> - Domain constraints (e.g. 'Only show active accounts')
|
|
283
|
+
> - Derived values (e.g. 'Calculate net return as current value minus invested amount')
|
|
284
|
+
> - Error conditions (e.g. 'If Status is not success, treat the response as an error')
|
|
285
|
+
> - Any conditional behaviour driven by data values
|
|
286
|
+
>
|
|
287
|
+
> Be as specific and exhaustive as possible. Each rule becomes a guard clause in the use case. Do not leave rules implicit — state them explicitly."
|
|
288
|
+
|
|
289
|
+
**Collect:**
|
|
290
|
+
```
|
|
291
|
+
BUSINESS_LOGIC_RULES: [
|
|
292
|
+
"<rule 1>",
|
|
293
|
+
"<rule 2>",
|
|
294
|
+
...
|
|
295
|
+
]
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
FATAL if not provided AND no spec MD file from Q1 contains a Business Logic section. Re-ask until at least one rule is given.
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
### QUESTION 5 — API Details & Response Mapping
|
|
303
|
+
|
|
304
|
+
Ask the user:
|
|
305
|
+
|
|
306
|
+
> "Please provide the complete API details for this feature:
|
|
307
|
+
>
|
|
308
|
+
> **A. API Endpoint**
|
|
309
|
+
> - URL (e.g. `/api/v1/portfolio/transactions`)
|
|
310
|
+
> - HTTP Method (GET / POST / PUT / PATCH / DELETE)
|
|
311
|
+
>
|
|
312
|
+
> **B. Request Body**
|
|
313
|
+
> - Paste the exact JSON request body with field names and example values
|
|
314
|
+
> - Use the EXACT field names the API expects (including casing — PascalCase, camelCase, snake_case)
|
|
315
|
+
>
|
|
316
|
+
> **C. Response Shape**
|
|
317
|
+
> - Paste the exact JSON response structure with field names and example values
|
|
318
|
+
> - Include nested objects and arrays exactly as the API returns them
|
|
319
|
+
>
|
|
320
|
+
> **D. Response-to-UI Mapping**
|
|
321
|
+
> - For each response field you want displayed, describe WHERE and HOW it appears in the UI
|
|
322
|
+
> - Format: `ResponseField` → description of UI location and any formatting
|
|
323
|
+
> - Example: `Data[].FundName` → primary title of each list row
|
|
324
|
+
> - Example: `Data[].Amount` → below title, formatted as Indian currency (₹)
|
|
325
|
+
> - Example: `Data[].TransactionID` → unique key only, not displayed
|
|
326
|
+
>
|
|
327
|
+
> **E. Screen Logic**
|
|
328
|
+
> - Describe what happens on screen mount, user interactions, navigation, and conditional rendering
|
|
329
|
+
> - Example: 'Fetch data on screen mount'
|
|
330
|
+
> - Example: 'Support pull-to-refresh'
|
|
331
|
+
> - Example: 'Navigate to DetailScreen when user taps a row'
|
|
332
|
+
> - Example: 'Show empty state message when list is empty'
|
|
333
|
+
> - Example: 'Show loading skeleton while fetching'
|
|
334
|
+
>
|
|
335
|
+
> Provide the actual field names from the API exactly as they appear. Do not rename or guess."
|
|
336
|
+
|
|
337
|
+
**Collect:**
|
|
338
|
+
```
|
|
339
|
+
API_URL: <endpoint path>
|
|
340
|
+
HTTP_METHOD: GET | POST | PUT | PATCH | DELETE
|
|
341
|
+
REQUEST_BODY: <JSON object — exact API field names>
|
|
342
|
+
RESPONSE_SHAPE: <JSON object — exact API field names and example values>
|
|
343
|
+
RESPONSE_MAPPINGS: [
|
|
344
|
+
{ responseField: "Data[].FundName", uiLocation: "primary title of each row", transform: null },
|
|
345
|
+
{ responseField: "Data[].Amount", uiLocation: "below title", transform: "format as Indian currency" },
|
|
346
|
+
...
|
|
347
|
+
]
|
|
348
|
+
SCREEN_LOGIC: [
|
|
349
|
+
"<behavior 1>",
|
|
350
|
+
"<behavior 2>",
|
|
351
|
+
...
|
|
352
|
+
]
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
FATAL if `API_URL` or `HTTP_METHOD` not provided — re-ask until both are given.
|
|
356
|
+
WARN if `RESPONSE_SHAPE` not provided — generate empty entity and log WARN.
|
|
357
|
+
WARN if `RESPONSE_MAPPINGS` not provided — generate screen without field bindings and log WARN.
|
|
358
|
+
WARN if `SCREEN_LOGIC` not provided — generate screen with fetch-on-mount only and log WARN.
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
### PHASE -1 Completion Gate
|
|
363
|
+
|
|
364
|
+
Before proceeding to PHASE 0, confirm all items:
|
|
365
|
+
|
|
366
|
+
**Single mode checklist:**
|
|
367
|
+
```
|
|
368
|
+
[ ] Q1: SCREEN_NAME provided AND at least one design source provided (MD spec file or existing screen path — NO Figma)
|
|
369
|
+
[ ] Q2: User stories collected — or WARN logged if skipped
|
|
370
|
+
[ ] Q3: Test cases collected — or WARN logged if skipped
|
|
371
|
+
[ ] Q4: At least one business logic rule provided
|
|
372
|
+
[ ] Q5: API URL, HTTP method, response shape, and mappings provided
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
**Module mode checklist (repeat per screen):**
|
|
376
|
+
```
|
|
377
|
+
[ ] Q1: MODULE_NAME provided AND SCREENS[] list has at least one entry, each with a design source
|
|
378
|
+
For each screen in SCREENS[]:
|
|
379
|
+
[ ] Q2: User stories collected for <ScreenName> — or WARN logged
|
|
380
|
+
[ ] Q3: Test cases collected for <ScreenName> — or WARN logged
|
|
381
|
+
[ ] Q4: At least one business logic rule for <ScreenName>
|
|
382
|
+
[ ] Q5: API URL, HTTP method, response shape, and mappings for <ScreenName>
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
- If any FATAL condition is unmet: stop and re-ask only that specific question (and screen if module mode).
|
|
386
|
+
- If only WARNs are outstanding: proceed with WARN flags recorded.
|
|
387
|
+
- Do NOT proceed if any question is unanswered without a WARN or FATAL resolution.
|
|
388
|
+
|
|
389
|
+
**Merge all collected answers into FeatureSpec(s)** used in PHASE 1.2.
|
|
390
|
+
|
|
391
|
+
- In **single** mode: one `FeatureSpec` is built. Interview answers take precedence over spec MD file values.
|
|
392
|
+
- In **module** mode: one `FeatureSpec` is built **per screen**. Each screen's interview answers take precedence over its spec MD file. The module name is stored as `MODULE_NAME` and is shared across all specs.
|
|
393
|
+
|
|
394
|
+
`SCREEN_LOGIC` from Q5E is merged into `spec.screenLogic[]` and is the primary source for Phases 5 and 7.3.
|
|
395
|
+
`BUSINESS_LOGIC_RULES` from Q4 is merged into `spec.businessLogic[]` and is the primary source for Phase 3.3.
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
## PHASE 0 — Repository Intelligence
|
|
400
|
+
|
|
401
|
+
> **Run this phase completely before touching the spec file.**
|
|
402
|
+
> Every path, import, and naming decision in all later phases is derived from
|
|
403
|
+
> the fingerprint built here. If detection of any dimension is uncertain,
|
|
404
|
+
> record it as `[INFERRED]` and log a WARN — never silently guess.
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
### 0.A — Locate App Root
|
|
409
|
+
|
|
410
|
+
Do NOT assume the source lives in `src/`. Crawl the repository root looking
|
|
411
|
+
for the file that anchors the React Native app entry point.
|
|
412
|
+
|
|
413
|
+
**Algorithm — run in order, stop at first match:**
|
|
414
|
+
|
|
415
|
+
```
|
|
416
|
+
1. Find files named: App.tsx, App.ts, App.jsx, App.js, index.tsx, index.ts,
|
|
417
|
+
index.js (in that priority)
|
|
418
|
+
2. Among matches, pick the one whose directory ALSO contains at least one of:
|
|
419
|
+
package.json tsconfig.json babel.config.js metro.config.js
|
|
420
|
+
3. The directory of that anchor file is APP_ROOT
|
|
421
|
+
4. Walk up from APP_ROOT to find the nearest package.json whose
|
|
422
|
+
"dependencies" or "devDependencies" contains "react-native"
|
|
423
|
+
5. The directory of that package.json is REPO_ROOT
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
Record:
|
|
427
|
+
```
|
|
428
|
+
REPO_ROOT: <absolute path>
|
|
429
|
+
APP_ROOT: <absolute path> (may equal REPO_ROOT or be a subfolder like src/, app/, mobile/)
|
|
430
|
+
APP_ROOT_RELATIVE: <path relative to REPO_ROOT>
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
FATAL if no React Native anchor can be found.
|
|
434
|
+
|
|
435
|
+
---
|
|
436
|
+
|
|
437
|
+
### 0.B — Detect Feature Organisation Style
|
|
438
|
+
|
|
439
|
+
Scan every directory under `APP_ROOT` (max depth: 6 levels). Do not look for
|
|
440
|
+
specific folder names — count and classify what actually exists.
|
|
441
|
+
|
|
442
|
+
**Classification algorithm:**
|
|
443
|
+
|
|
444
|
+
```
|
|
445
|
+
FEATURE_FIRST clues:
|
|
446
|
+
- One directory per product feature at the same level
|
|
447
|
+
- Each feature directory contains at least two of:
|
|
448
|
+
screens/, components/, hooks/, services/, store/, state/, api/
|
|
449
|
+
- Examples found: features/, modules/, pages/, domains/
|
|
450
|
+
|
|
451
|
+
LAYER_FIRST clues:
|
|
452
|
+
- Root-level directories named after layers, not features
|
|
453
|
+
- Look for: screens/, components/, redux/, store/, services/, hooks/
|
|
454
|
+
existing at the same level under APP_ROOT
|
|
455
|
+
- Features are identified by file name prefix (e.g., AuthScreen.tsx)
|
|
456
|
+
|
|
457
|
+
EXPO_ROUTER clues:
|
|
458
|
+
- Directory named: app/ containing (tabs)/ or route files
|
|
459
|
+
- File-system based routing patterns: [id].tsx, (auth)/ groups
|
|
460
|
+
|
|
461
|
+
DOMAIN_DRIVEN clues:
|
|
462
|
+
- Top-level: domain/, data/, presentation/ at same level
|
|
463
|
+
|
|
464
|
+
ATOMIC clues:
|
|
465
|
+
- Top-level: atoms/, molecules/, organisms/ at same level
|
|
466
|
+
|
|
467
|
+
MONOREPO clues:
|
|
468
|
+
- packages/ directory with multiple sub-packages each containing package.json
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
If multiple signals are mixed (common in real projects), record the dominant
|
|
472
|
+
one and all secondary patterns:
|
|
473
|
+
|
|
474
|
+
```
|
|
475
|
+
FEATURE_STYLE: feature-first ← dominant
|
|
476
|
+
FEATURE_STYLE_SECONDARY: layer-mixed ← if some layers leak to root
|
|
477
|
+
FEATURES_ROOT: <path relative to APP_ROOT where feature directories live>
|
|
478
|
+
FEATURE_DIR_SAMPLE: [portfolio, auth, dashboard, ...] ← 3-5 example names
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
**Greenfield / fallback detection:**
|
|
482
|
+
|
|
483
|
+
Run the following decision tree after the normal classification above:
|
|
484
|
+
|
|
485
|
+
```
|
|
486
|
+
STEP 1 — Check for existing layer-first directories under APP_ROOT:
|
|
487
|
+
Look for ANY of these directory names at the top level of APP_ROOT:
|
|
488
|
+
screens/, screen/, views/, pages/
|
|
489
|
+
store/, redux/, state/
|
|
490
|
+
services/, api/, network/
|
|
491
|
+
|
|
492
|
+
If 2 or more of the above exist:
|
|
493
|
+
→ LAYER_FIRST_EXISTING: true
|
|
494
|
+
→ FEATURE_STYLE: layer-first (override any earlier classification)
|
|
495
|
+
→ GREENFIELD: false
|
|
496
|
+
Record the actual folder names found:
|
|
497
|
+
LAYER_FIRST_SCREENS_DIR: <actual name, e.g. screens/>
|
|
498
|
+
LAYER_FIRST_SERVICES_DIR: <actual name, e.g. services/>
|
|
499
|
+
LAYER_FIRST_STATE_DIR: <actual name, e.g. store/>
|
|
500
|
+
LAYER_FIRST_STATE_SLICES_DIR: <actual subfolder for slices, e.g. store/slices/ or store/>
|
|
501
|
+
Log: INFO: Layer-first project structure detected. Files will be placed in existing layer directories.
|
|
502
|
+
|
|
503
|
+
STEP 2 — If LAYER_FIRST_EXISTING is false AND FEATURE_DIR_SAMPLE is empty:
|
|
504
|
+
→ GREENFIELD: true
|
|
505
|
+
→ FEATURE_STYLE: feature-first
|
|
506
|
+
→ FEATURES_ROOT: features/
|
|
507
|
+
→ FEATURE_DIR_SAMPLE: []
|
|
508
|
+
Log: INFO: No existing feature structure detected. Falling back to canonical clean-architecture default.
|
|
509
|
+
|
|
510
|
+
STEP 3 — Otherwise:
|
|
511
|
+
→ GREENFIELD: false
|
|
512
|
+
→ Use FEATURE_STYLE and FEATURES_ROOT as classified above.
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
In LAYER_FIRST_EXISTING mode:
|
|
516
|
+
- Phase 0.C layer scanning is adapted — scan the detected layer directories, NOT feature sub-folders.
|
|
517
|
+
- Phase 0.K.1 applies the LAYER_FIRST default map.
|
|
518
|
+
- `FEATURES_ROOT` is set to `null` — there is no single features root.
|
|
519
|
+
|
|
520
|
+
In GREENFIELD mode:
|
|
521
|
+
- Phase 0.C layer scanning is skipped entirely.
|
|
522
|
+
- Phase 0.K.1 applies the FEATURE_FIRST default map.
|
|
523
|
+
- Log: `INFO: No existing feature structure detected. Falling back to canonical default layer map.`
|
|
524
|
+
|
|
525
|
+
---
|
|
526
|
+
|
|
527
|
+
### 0.C — Infer Architectural Layers Per Feature
|
|
528
|
+
|
|
529
|
+
Scan **ALL** detected feature directories, not just one. Walk each and map its
|
|
530
|
+
layer structure. When the same layer uses different folder names across features
|
|
531
|
+
(e.g., `domain/models` vs `domain/entities`), record all variants and emit a WARN.
|
|
532
|
+
|
|
533
|
+
Also detect the DI wiring pattern used by each feature.
|
|
534
|
+
|
|
535
|
+
**Layer inference algorithm (run for every feature directory):**
|
|
536
|
+
|
|
537
|
+
```
|
|
538
|
+
For each subfolder in a feature directory:
|
|
539
|
+
- Contains interfaces/types only → DOMAIN or TYPES layer
|
|
540
|
+
- Contains *Repository* files → REPOSITORY layer (domain or data)
|
|
541
|
+
- Contains *UseCase* or *Service* files → USE_CASE layer
|
|
542
|
+
- Contains *Slice* or *Store* or *Reducer* → STATE layer
|
|
543
|
+
- Contains *DataSource* or *Api* or *Remote* → DATA_SOURCE layer
|
|
544
|
+
- Contains *Screen* or *Page* or *View* → PRESENTATION layer
|
|
545
|
+
- Contains *Mapper* or *Transformer* → MAPPER layer
|
|
546
|
+
- Contains *.mock.json* or mock files → MOCK layer
|
|
547
|
+
- Contains *Selector* or *Query* → SELECTOR layer
|
|
548
|
+
- Named 'di/' and contains *container* or *bootstrap* → DI layer
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
**DI pattern detection (run per feature):**
|
|
552
|
+
|
|
553
|
+
```
|
|
554
|
+
Look for a file at feature root named diBootstrap.ts or diBootstrap.js:
|
|
555
|
+
→ DI_STYLE: per-feature-bootstrap
|
|
556
|
+
Record: bootstrapFile = "diBootstrap.ts"
|
|
557
|
+
Note: exports named singleton instances consumed via ThunkExtra or direct import
|
|
558
|
+
|
|
559
|
+
Look for a directory named di/ whose files contain class.*Container or getInstance(:
|
|
560
|
+
→ DI_STYLE: per-feature-container
|
|
561
|
+
Record: containerFile = "di/container.ts" (or actual path)
|
|
562
|
+
Record: containerClass = detected class name (e.g. "ReportsContainer")
|
|
563
|
+
|
|
564
|
+
Look for a top-level shared DI / IoC file outside all feature directories:
|
|
565
|
+
→ DI_STYLE: global
|
|
566
|
+
|
|
567
|
+
If none found:
|
|
568
|
+
→ DI_STYLE: none
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
If features use different DI styles, record all. When generating, use the style
|
|
572
|
+
of the existing feature most similar to the new one.
|
|
573
|
+
|
|
574
|
+
Record actual folder names, not assumed names. When names differ across features,
|
|
575
|
+
record the dominant value AND all alternatives:
|
|
576
|
+
|
|
577
|
+
```
|
|
578
|
+
LAYER_MAP: {
|
|
579
|
+
domain: "domain"
|
|
580
|
+
domainModels: "domain/models" (variants: ["domain/models","domain/entities"])
|
|
581
|
+
domainRepo: "domain/repositories"
|
|
582
|
+
useCases: "domain/useCases" (variants: ["domain/usecases","domain/useCases"])
|
|
583
|
+
data: "data"
|
|
584
|
+
dataSource: "data/datasources" (variants: ["data/datasources","data/api"])
|
|
585
|
+
repoImpl: "data/repositories" (variants: ["data/repositories","data/repositoryImpl"])
|
|
586
|
+
dtos: "data/dtos" (or null when absent in all features)
|
|
587
|
+
mappers: "data/mappers"
|
|
588
|
+
mocks: "data/datasources/__mocks__" (or nearest mock folder)
|
|
589
|
+
state: "state" (or null when absent — e.g. Context-API features)
|
|
590
|
+
stateStore: "state/store" (or null)
|
|
591
|
+
selectors: "state/selectors" (or null)
|
|
592
|
+
presentation: "presentation"
|
|
593
|
+
screens: "presentation/screens"
|
|
594
|
+
di: "di" (or null — only present for per-feature-container style)
|
|
595
|
+
}
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
If a layer is absent in ALL scanned features, mark it `null` and skip generation.
|
|
599
|
+
|
|
600
|
+
**Selecting the representative layer map for generation:**
|
|
601
|
+
|
|
602
|
+
```
|
|
603
|
+
When LAYER_MAP has variants:
|
|
604
|
+
1. If the new feature name or spec hints at resemblance to an existing feature, use
|
|
605
|
+
that feature's exact layer paths.
|
|
606
|
+
2. Otherwise, use the layer paths from the most complete feature (most non-null layers).
|
|
607
|
+
3. Log a WARN for every layer where a variant was not chosen.
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
**Greenfield / Layer-first override:**
|
|
611
|
+
|
|
612
|
+
If `GREENFIELD = true`:
|
|
613
|
+
- Skip all layer scanning above.
|
|
614
|
+
- Set every LAYER_MAP field to `[DEFAULT]` placeholder.
|
|
615
|
+
- Phase 0.K.1-A will replace them with the canonical feature-first defaults.
|
|
616
|
+
- Do NOT emit WARNs for missing layers.
|
|
617
|
+
|
|
618
|
+
If `LAYER_FIRST_EXISTING = true`:
|
|
619
|
+
- Skip feature-directory layer scanning above (there are no feature sub-folders to scan).
|
|
620
|
+
- Instead, read 2–3 existing files from each detected layer directory
|
|
621
|
+
(`LAYER_FIRST_SCREENS_DIR`, `LAYER_FIRST_SERVICES_DIR`, `LAYER_FIRST_STATE_DIR`) to
|
|
622
|
+
detect naming conventions (file name style, export style, import paths).
|
|
623
|
+
- Set LAYER_MAP fields using the detected actual folder names.
|
|
624
|
+
- Phase 0.K.1-B will fill any remaining `[DEFAULT]` placeholders.
|
|
625
|
+
- Do NOT emit WARNs for absent domain/repository/useCase layers — they are intentionally absent.
|
|
626
|
+
|
|
627
|
+
**Depth calculation:** Count directory segments from `APP_ROOT` to the deepest
|
|
628
|
+
layer folder. This determines the import prefix needed to reach shared
|
|
629
|
+
utilities.
|
|
630
|
+
|
|
631
|
+
```
|
|
632
|
+
Example:
|
|
633
|
+
APP_ROOT = src/
|
|
634
|
+
datasource file = src/features/portfolio/data/datasources/File.ts
|
|
635
|
+
Depth from APP_ROOT = 4 (features → portfolio → data → datasources)
|
|
636
|
+
Import prefix to APP_ROOT = ../../../../
|
|
637
|
+
|
|
638
|
+
Record:
|
|
639
|
+
MAX_LAYER_DEPTH: 4
|
|
640
|
+
IMPORT_PREFIX_TO_APP_ROOT: ../../../../
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
---
|
|
644
|
+
|
|
645
|
+
### 0.D — Locate Shared / Common Utilities
|
|
646
|
+
|
|
647
|
+
Do NOT assume a `shared/` folder. Scan for directories used by multiple
|
|
648
|
+
features as imports.
|
|
649
|
+
|
|
650
|
+
**Algorithm:**
|
|
651
|
+
|
|
652
|
+
```
|
|
653
|
+
1. Pick 3-5 feature files from Step 0.C
|
|
654
|
+
2. Read their import statements
|
|
655
|
+
3. Identify import paths that:
|
|
656
|
+
- Appear in more than one feature
|
|
657
|
+
- Are NOT from node_modules
|
|
658
|
+
- Point to a common ancestor directory
|
|
659
|
+
4. That common ancestor directory is SHARED_ROOT
|
|
660
|
+
5. Record the actual folder name (shared, common, core, utils, lib, etc.)
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
Record:
|
|
664
|
+
```
|
|
665
|
+
SHARED_ROOT: <path relative to APP_ROOT> e.g. shared/, common/, core/
|
|
666
|
+
SHARED_ROOT_ALIAS: <alias if used> e.g. @shared, @/shared, ~/shared, or null
|
|
667
|
+
SHARED_API_DIR: <subfolder under SHARED_ROOT containing API client>
|
|
668
|
+
SHARED_TYPES_DIR: <subfolder under SHARED_ROOT containing shared types>
|
|
669
|
+
SHARED_STORE_DIR: <subfolder under SHARED_ROOT containing store config>
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
For each layer file generated later, compute:
|
|
673
|
+
```
|
|
674
|
+
IMPORT_PREFIX_TO_SHARED = repeat("../", depth_of_file_from_APP_ROOT) + SHARED_ROOT
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
If `SHARED_ROOT_ALIAS` is non-null, use the alias directly instead of relative
|
|
678
|
+
path (see 0.G for alias detection).
|
|
679
|
+
|
|
680
|
+
---
|
|
681
|
+
|
|
682
|
+
### 0.E — API Layer Discovery
|
|
683
|
+
|
|
684
|
+
Do NOT look in a specific folder. Search the entire repository.
|
|
685
|
+
|
|
686
|
+
**Search for API client instantiation patterns:**
|
|
687
|
+
|
|
688
|
+
```
|
|
689
|
+
Pattern A — Axios:
|
|
690
|
+
Search: axios.create(
|
|
691
|
+
Files found → AXIOS_CLIENT_FILES
|
|
692
|
+
|
|
693
|
+
Pattern B — Fetch wrapper:
|
|
694
|
+
Search: fetch( OR window.fetch(
|
|
695
|
+
Combined with response.json() → FETCH_WRAPPER_FILES
|
|
696
|
+
|
|
697
|
+
Pattern C — RTK Query:
|
|
698
|
+
Search: createApi(
|
|
699
|
+
Files found → RTK_QUERY_FILES
|
|
700
|
+
|
|
701
|
+
Pattern D — React Query / TanStack Query:
|
|
702
|
+
Search: useQuery( OR useMutation( OR QueryClient
|
|
703
|
+
Files found → REACT_QUERY_FILES
|
|
704
|
+
|
|
705
|
+
Pattern E — Apollo / GraphQL:
|
|
706
|
+
Search: ApolloClient OR gql` OR useQuery from @apollo
|
|
707
|
+
Files found → APOLLO_FILES
|
|
708
|
+
|
|
709
|
+
Pattern F — Other (ky, superagent, got):
|
|
710
|
+
Search: import ky OR require('ky') etc.
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
For the detected API library:
|
|
714
|
+
- Record the exact file path(s) of the shared client instance
|
|
715
|
+
- Read those files and extract: base URL, headers, interceptors, retry logic,
|
|
716
|
+
token injection pattern
|
|
717
|
+
- Record the exact import statement used by existing feature files to reach the
|
|
718
|
+
client
|
|
719
|
+
|
|
720
|
+
**Envelope / Request Shape Detection:**
|
|
721
|
+
|
|
722
|
+
Read 3-5 existing API call sites. Examine the request body structure passed to
|
|
723
|
+
the client:
|
|
724
|
+
|
|
725
|
+
```
|
|
726
|
+
If body is wrapped: { mb: { operationId, rq: { reqdata: {} } } }
|
|
727
|
+
→ ENVELOPE_STYLE: mb-operationId
|
|
728
|
+
→ ENVELOPE_BUILDER: detect if a buildEnvelope() helper exists or is inlined
|
|
729
|
+
|
|
730
|
+
If body is plain: { key: value, key2: value2 }
|
|
731
|
+
→ ENVELOPE_STYLE: none
|
|
732
|
+
|
|
733
|
+
If body follows another wrapper pattern → record exact shape
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
**Return type pattern detection:**
|
|
737
|
+
|
|
738
|
+
Read 3–5 existing repository interface files. Examine the return types of their methods:
|
|
739
|
+
|
|
740
|
+
```
|
|
741
|
+
If return types are Promise<{ ok: boolean; data?: T; error?: string }>
|
|
742
|
+
→ RETURN_TYPE_STYLE: result-wrapper
|
|
743
|
+
Record: RESULT_TYPE_IMPORT = <exact import string for the Result type>
|
|
744
|
+
|
|
745
|
+
If return types are Promise<CustomNamedType> where CustomNamedType is defined in the
|
|
746
|
+
same feature's domain layer (e.g. Promise<PortfolioSummaryResult>):
|
|
747
|
+
→ RETURN_TYPE_STYLE: custom-type
|
|
748
|
+
Record: no shared Result import needed
|
|
749
|
+
|
|
750
|
+
If return types are direct: Promise<T[]> or Promise<T> with no wrapper:
|
|
751
|
+
→ RETURN_TYPE_STYLE: direct-promise
|
|
752
|
+
Record: no wrapper — throws on error instead of returning { ok: false }
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
This field controls how Phase 3.2, 4.7, and 5.1 generate the repository interface,
|
|
756
|
+
implementation, and thunk error handling respectively.
|
|
757
|
+
|
|
758
|
+
**Mock / Stub Detection:**
|
|
759
|
+
|
|
760
|
+
```
|
|
761
|
+
Search for: mockEnabled OR globalMockEnabled OR mock.config OR msw OR nock
|
|
762
|
+
If found → record file path, exact shape, evaluation order
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
Record:
|
|
766
|
+
```
|
|
767
|
+
API_LIB: axios | fetch | rtk-query | react-query | apollo | other
|
|
768
|
+
API_CLIENT_FILE: <exact path to shared client file>
|
|
769
|
+
API_CLIENT_IMPORT: <exact import string used by feature files>
|
|
770
|
+
ENVELOPE_STYLE: mb-operationId | mb-operationid-lowercase | none | <other>
|
|
771
|
+
ENVELOPE_BUILDER: <function name and file, or "inline">
|
|
772
|
+
MOCK_SYSTEM: mockConfig | msw | none
|
|
773
|
+
MOCK_CONFIG_FILE: <path or null>
|
|
774
|
+
MOCK_GLOBAL_FLAG: globalMockEnabled | <other field name> | null
|
|
775
|
+
MOCK_DELAY_FIELD: simulatedDelayMs | <other field name> | null
|
|
776
|
+
MOCK_JSON_FOLDER: <path pattern for mock JSON files, relative to datasource>
|
|
777
|
+
RETURN_TYPE_STYLE: result-wrapper | custom-type | direct-promise
|
|
778
|
+
RESULT_TYPE_IMPORT: <exact import string for Result type, or null>
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
---
|
|
782
|
+
|
|
783
|
+
### 0.F — State Management Discovery
|
|
784
|
+
|
|
785
|
+
Search the entire repository (not just specific folders).
|
|
786
|
+
|
|
787
|
+
**Search for state library signatures:**
|
|
788
|
+
|
|
789
|
+
```
|
|
790
|
+
createSlice( → redux-toolkit
|
|
791
|
+
createAsyncThunk( → redux-toolkit
|
|
792
|
+
configureStore( → redux-toolkit (store config)
|
|
793
|
+
createReducer( → redux-toolkit or plain redux
|
|
794
|
+
create( → zustand (if imported from 'zustand')
|
|
795
|
+
makeAutoObservable( → mobx
|
|
796
|
+
observable( → mobx
|
|
797
|
+
useReducer( → context-api (if paired with createContext)
|
|
798
|
+
createContext( → context-api
|
|
799
|
+
QueryClient → react-query / tanstack-query
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
Record all matches. If multiple libraries exist, note which features use which.
|
|
803
|
+
For the dominant library, detect the injection/sharing pattern:
|
|
804
|
+
|
|
805
|
+
**For Redux Toolkit — detect thunk injection style:**
|
|
806
|
+
|
|
807
|
+
```
|
|
808
|
+
Search async thunks for:
|
|
809
|
+
Pattern A (ThunkExtra): async (params, { extra, rejectWithValue }) => {
|
|
810
|
+
const { someUseCase } = extra as { ... }
|
|
811
|
+
→ THUNK_PATTERN: thunk-extra
|
|
812
|
+
|
|
813
|
+
Pattern B (Direct import): import { someUseCase } from '...'
|
|
814
|
+
async (params, { rejectWithValue }) => {
|
|
815
|
+
→ THUNK_PATTERN: direct-import
|
|
816
|
+
|
|
817
|
+
Pattern C (RTK Query): endpoints builder pattern
|
|
818
|
+
→ THUNK_PATTERN: rtk-query
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
**For Redux — locate the store configuration:**
|
|
822
|
+
|
|
823
|
+
```
|
|
824
|
+
Search for configureStore( call
|
|
825
|
+
Read that file to find:
|
|
826
|
+
- middleware / extra argument → confirm ThunkExtra injection
|
|
827
|
+
- reducer map keys → these become slice name constraints
|
|
828
|
+
- The RootState type definition file / export
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
Record:
|
|
832
|
+
```
|
|
833
|
+
STATE_LIB: redux-toolkit | zustand | mobx | context-api | react-query | other
|
|
834
|
+
THUNK_PATTERN: thunk-extra | direct-import | rtk-query | n/a
|
|
835
|
+
STORE_CONFIG_FILE: <path to configureStore call>
|
|
836
|
+
ROOT_STATE_TYPE_FILE: <path where RootState is exported>
|
|
837
|
+
ROOT_STATE_IMPORT: <exact import string to reach RootState from a selector file>
|
|
838
|
+
SLICE_NAME_STYLE: camelCase | kebab-case (from existing slice name strings)
|
|
839
|
+
REDUCER_MAP_KEYS: [portfolio, auth, ...] (existing keys in combineReducers/configureStore)
|
|
840
|
+
```
|
|
841
|
+
|
|
842
|
+
---
|
|
843
|
+
|
|
844
|
+
### 0.G — Import Alias Detection
|
|
845
|
+
|
|
846
|
+
Import aliases are critical — generated code MUST use the same alias style,
|
|
847
|
+
otherwise it will fail to compile.
|
|
848
|
+
|
|
849
|
+
**Search in order:**
|
|
850
|
+
|
|
851
|
+
```
|
|
852
|
+
1. tsconfig.json or tsconfig.base.json → paths: { "@/*": ["./src/*"], ... }
|
|
853
|
+
2. babel.config.js or babel.config.ts → module-resolver plugin aliases
|
|
854
|
+
3. metro.config.js → resolver.extraNodeModules or resolver.alias
|
|
855
|
+
4. package.json → imports field (Node.js subpath imports)
|
|
856
|
+
5. jsconfig.json → paths
|
|
857
|
+
```
|
|
858
|
+
|
|
859
|
+
For each alias found, record:
|
|
860
|
+
```
|
|
861
|
+
ALIAS_MAP: {
|
|
862
|
+
"@": "src/",
|
|
863
|
+
"@shared": "src/shared/",
|
|
864
|
+
"~": "./",
|
|
865
|
+
"@app": "app/",
|
|
866
|
+
...
|
|
867
|
+
}
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
Then, for each shared import generated later: prefer the longest matching alias
|
|
871
|
+
over a relative path. If no alias matches, use the computed relative path from
|
|
872
|
+
Step 0.D.
|
|
873
|
+
|
|
874
|
+
Record:
|
|
875
|
+
```
|
|
876
|
+
ALIASES_DETECTED: true | false
|
|
877
|
+
ALIAS_MAP: { ... } or {}
|
|
878
|
+
PREFER_ALIASES: true | false (true if existing feature files use aliases)
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
---
|
|
882
|
+
|
|
883
|
+
### 0.H — Navigation Discovery
|
|
884
|
+
|
|
885
|
+
```
|
|
886
|
+
Search for:
|
|
887
|
+
@react-navigation/native → REACT_NAVIGATION, detect version from package.json
|
|
888
|
+
expo-router → EXPO_ROUTER
|
|
889
|
+
react-native-navigation (wix) → WIX_NAVIGATION
|
|
890
|
+
react-native-screens → (supporting library, not primary)
|
|
891
|
+
|
|
892
|
+
For React Navigation — detect version:
|
|
893
|
+
package.json "@react-navigation/native": "^6.x" → v6
|
|
894
|
+
package.json "@react-navigation/native": "^5.x" → v5
|
|
895
|
+
package.json "@react-navigation/native": "^7.x" → v7
|
|
896
|
+
|
|
897
|
+
For React Navigation — detect stack type used:
|
|
898
|
+
Search: createNativeStackNavigator OR createStackNavigator OR createBottomTabNavigator
|
|
899
|
+
Read 1-2 screen registration files to see how screens are added
|
|
900
|
+
|
|
901
|
+
For Expo Router — detect routing pattern:
|
|
902
|
+
Read app/ directory structure to understand route groups
|
|
903
|
+
```
|
|
904
|
+
|
|
905
|
+
Record:
|
|
906
|
+
```
|
|
907
|
+
NAV_LIB: react-navigation | expo-router | wix-navigation | none
|
|
908
|
+
NAV_VERSION: 6 | 5 | 7 | n/a
|
|
909
|
+
NAV_STACK_TYPE: native-stack | stack | bottom-tab | n/a
|
|
910
|
+
NAV_SCREEN_FILE: <path to main navigator file where screens are registered>
|
|
911
|
+
NAV_PARAMS_PATTERN: <how params are typed — RootStackParamList, RouteParams, etc.>
|
|
912
|
+
```
|
|
913
|
+
|
|
914
|
+
---
|
|
915
|
+
|
|
916
|
+
### 0.I — Type System & Code Quality Discovery
|
|
917
|
+
|
|
918
|
+
```
|
|
919
|
+
Read tsconfig.json:
|
|
920
|
+
compilerOptions.strict → TS_STRICT: true | false
|
|
921
|
+
compilerOptions.noImplicitAny → NO_IMPLICIT_ANY: true | false
|
|
922
|
+
compilerOptions.strictNullChecks → STRICT_NULL_CHECKS: true | false
|
|
923
|
+
|
|
924
|
+
Read .eslintrc.* or eslint.config.*:
|
|
925
|
+
Record any rules that affect generated code:
|
|
926
|
+
- no-explicit-any
|
|
927
|
+
- @typescript-eslint/explicit-function-return-type
|
|
928
|
+
- import/order
|
|
929
|
+
|
|
930
|
+
Read .prettierrc or prettier.config.*:
|
|
931
|
+
Record: singleQuote, tabWidth, trailingComma, semi
|
|
932
|
+
|
|
933
|
+
Read package.json scripts:
|
|
934
|
+
Detect test runner: jest | vitest | mocha
|
|
935
|
+
Detect test pattern: *.test.ts | *.spec.ts | __tests__/*.ts
|
|
936
|
+
```
|
|
937
|
+
|
|
938
|
+
Record:
|
|
939
|
+
```
|
|
940
|
+
TS_STRICT: true | false
|
|
941
|
+
NO_IMPLICIT_ANY: true | false
|
|
942
|
+
STRICT_NULL_CHECKS: true | false
|
|
943
|
+
EXPLICIT_RETURN_TYPE: true | false (requires function return types)
|
|
944
|
+
QUOTE_STYLE: single | double
|
|
945
|
+
TRAILING_COMMA: es5 | all | none
|
|
946
|
+
SEMI: true | false
|
|
947
|
+
TAB_WIDTH: 2 | 4
|
|
948
|
+
TEST_RUNNER: jest | vitest | none
|
|
949
|
+
TEST_FILE_PATTERN: *.test.ts | *.spec.ts | __tests__/
|
|
950
|
+
```
|
|
951
|
+
|
|
952
|
+
---
|
|
953
|
+
|
|
954
|
+
### 0.J — Naming Convention Discovery
|
|
955
|
+
|
|
956
|
+
Detect naming conventions from existing files — never assume.
|
|
957
|
+
|
|
958
|
+
```
|
|
959
|
+
Read file names in FEATURES_ROOT:
|
|
960
|
+
All lowercase + hyphens: portfolioSlice.ts, portfolio-slice.ts
|
|
961
|
+
→ FILE_NAME_STYLE: camelCase | kebab-case | PascalCase
|
|
962
|
+
|
|
963
|
+
Read class names in datasource files:
|
|
964
|
+
class PortfolioApiDataSource → CLASS_STYLE: PascalCase
|
|
965
|
+
|
|
966
|
+
Read interface/type names:
|
|
967
|
+
interface IPortfolioRepository → INTERFACE_PREFIX: I
|
|
968
|
+
interface PortfolioRepository → INTERFACE_PREFIX: none
|
|
969
|
+
|
|
970
|
+
Read existing operationId strings in envelope calls:
|
|
971
|
+
'GetPortfolioSummary' → OPERATION_ID_STYLE: PascalCase with Get prefix
|
|
972
|
+
'portfolio/fetchSummary' → OPERATION_ID_STYLE: path-style
|
|
973
|
+
|
|
974
|
+
Read mock JSON file names:
|
|
975
|
+
portfolioSummary.mock.json → MOCK_JSON_STYLE: camelCase.mock.json
|
|
976
|
+
portfolio_summary_mock.json → MOCK_JSON_STYLE: snake_case_mock.json
|
|
977
|
+
|
|
978
|
+
Read feature folder names:
|
|
979
|
+
portfolio/, transactionHistory/ → FEATURE_FOLDER_STYLE: camelCase
|
|
980
|
+
transaction-history/ → FEATURE_FOLDER_STYLE: kebab-case
|
|
981
|
+
```
|
|
982
|
+
|
|
983
|
+
Record:
|
|
984
|
+
```
|
|
985
|
+
FILE_NAME_STYLE: camelCase | kebab-case | PascalCase
|
|
986
|
+
CLASS_STYLE: PascalCase
|
|
987
|
+
INTERFACE_PREFIX: I | none
|
|
988
|
+
OPERATION_ID_STYLE: PascalCase-Get | path-style | camelCase
|
|
989
|
+
MOCK_JSON_STYLE: <detected pattern>
|
|
990
|
+
FEATURE_FOLDER_STYLE: camelCase | kebab-case
|
|
991
|
+
```
|
|
992
|
+
|
|
993
|
+
---
|
|
994
|
+
|
|
995
|
+
### 0.K — Build the Architecture Fingerprint
|
|
996
|
+
|
|
997
|
+
Consolidate all Phase 0 findings into a single, structured fingerprint. This
|
|
998
|
+
fingerprint is the **only** source of truth for all file paths, import strings,
|
|
999
|
+
naming, and patterns used in Phases 1–7.
|
|
1000
|
+
|
|
1001
|
+
```json
|
|
1002
|
+
{
|
|
1003
|
+
"repoRoot": "<absolute path>",
|
|
1004
|
+
"appRoot": "<absolute path>",
|
|
1005
|
+
"appRootRelative": "src/",
|
|
1006
|
+
|
|
1007
|
+
"featureStyle": "feature-first | layer-first | expo-router | domain-driven | atomic",
|
|
1008
|
+
"featuresRoot": "<path relative to appRoot where features live>",
|
|
1009
|
+
"featureDirSample": ["portfolio", "auth", "dashboard"],
|
|
1010
|
+
|
|
1011
|
+
"layerMap": {
|
|
1012
|
+
"domain": "<actual folder name or null>",
|
|
1013
|
+
"domainModels": "<actual path segment or null>",
|
|
1014
|
+
"domainRepo": "<actual path segment or null>",
|
|
1015
|
+
"useCases": "<actual path segment or null>",
|
|
1016
|
+
"data": "<actual folder name or null>",
|
|
1017
|
+
"dataSource": "<actual path segment or null>",
|
|
1018
|
+
"dtos": "<actual path segment or null>",
|
|
1019
|
+
"mappers": "<actual path segment or null>",
|
|
1020
|
+
"mocks": "<actual path segment or null>",
|
|
1021
|
+
"state": "<actual folder name or null>",
|
|
1022
|
+
"stateStore": "<actual path segment or null>",
|
|
1023
|
+
"selectors": "<actual path segment or null>",
|
|
1024
|
+
"presentation": "<actual folder name or null>",
|
|
1025
|
+
"screens": "<actual path segment or null>"
|
|
1026
|
+
},
|
|
1027
|
+
|
|
1028
|
+
"maxLayerDepth": 4,
|
|
1029
|
+
"importPrefixToAppRoot": "../../../../",
|
|
1030
|
+
|
|
1031
|
+
"sharedRoot": "<path relative to appRoot>",
|
|
1032
|
+
"sharedRootAlias": "@shared | null",
|
|
1033
|
+
"sharedApiDir": "<path relative to appRoot>",
|
|
1034
|
+
"sharedTypesDir": "<path relative to appRoot>",
|
|
1035
|
+
"sharedStoreDir": "<path relative to appRoot>",
|
|
1036
|
+
|
|
1037
|
+
"aliasesDetected": true,
|
|
1038
|
+
"aliasMap": { "@": "src/", "@shared": "src/shared/" },
|
|
1039
|
+
"preferAliases": true,
|
|
1040
|
+
|
|
1041
|
+
"apiLib": "axios | fetch | rtk-query | react-query | apollo | other",
|
|
1042
|
+
"apiClientFile": "<absolute path>",
|
|
1043
|
+
"apiClientImport": "<exact import string>",
|
|
1044
|
+
"envelopeStyle": "mb-operationId | none | <other>",
|
|
1045
|
+
"envelopeBuilder": "<function name> | inline | null",
|
|
1046
|
+
"mockSystem": "mockConfig | msw | none",
|
|
1047
|
+
"mockConfigFile": "<path or null>",
|
|
1048
|
+
"mockGlobalFlag": "globalMockEnabled | <other> | null",
|
|
1049
|
+
"mockDelayField": "simulatedDelayMs | <other> | null",
|
|
1050
|
+
"mockJsonFolder": "<path pattern relative to datasource file>",
|
|
1051
|
+
|
|
1052
|
+
"stateLib": "redux-toolkit | zustand | mobx | context-api | react-query | other",
|
|
1053
|
+
"thunkPattern": "thunk-extra | direct-import | rtk-query | n/a",
|
|
1054
|
+
"storeConfigFile": "<path>",
|
|
1055
|
+
"rootStateTypeFile": "<path>",
|
|
1056
|
+
"rootStateImport": "<exact import string>",
|
|
1057
|
+
"sliceNameStyle": "camelCase | kebab-case",
|
|
1058
|
+
"reducerMapKeys": ["portfolio", "auth"],
|
|
1059
|
+
"reducerKey": "<sliceName — single mode only; null in module mode (each screen derives its own reducerKey in Phase 1.4)>",
|
|
1060
|
+
|
|
1061
|
+
"navLib": "react-navigation | expo-router | wix-navigation | none",
|
|
1062
|
+
"navVersion": "6 | 5 | 7 | n/a",
|
|
1063
|
+
"navStackType": "native-stack | stack | bottom-tab | n/a",
|
|
1064
|
+
"navScreenFile": "<path>",
|
|
1065
|
+
"navParamsPattern": "<RootStackParamList type name>",
|
|
1066
|
+
|
|
1067
|
+
"tsStrict": true,
|
|
1068
|
+
"noImplicitAny": true,
|
|
1069
|
+
"explicitReturnType": false,
|
|
1070
|
+
"quoteStyle": "single",
|
|
1071
|
+
"trailingComma": "es5",
|
|
1072
|
+
"semi": true,
|
|
1073
|
+
"tabWidth": 2,
|
|
1074
|
+
"testRunner": "jest | vitest | none",
|
|
1075
|
+
"testFilePattern": "*.test.ts",
|
|
1076
|
+
|
|
1077
|
+
"fileNameStyle": "camelCase | kebab-case | PascalCase",
|
|
1078
|
+
"classStyle": "PascalCase",
|
|
1079
|
+
"interfacePrefix": "I | none",
|
|
1080
|
+
"operationIdStyle": "PascalCase-Get | path-style | camelCase",
|
|
1081
|
+
"mockJsonStyle": "<pattern>",
|
|
1082
|
+
"featureFolderStyle": "camelCase | kebab-case",
|
|
1083
|
+
|
|
1084
|
+
"diStyle": "per-feature-bootstrap | per-feature-container | global | none",
|
|
1085
|
+
"diBootstrapFile": "<path relative to feature root, or null>",
|
|
1086
|
+
"diContainerFile": "<path relative to feature root, or null>",
|
|
1087
|
+
"diContainerClass": "<class name, or null>",
|
|
1088
|
+
|
|
1089
|
+
"returnTypeStyle": "result-wrapper | custom-type | direct-promise",
|
|
1090
|
+
"resultTypeImport": "<exact import string for Result type, or null>",
|
|
1091
|
+
"repoReturnWrapper": "<custom type name if returnTypeStyle=custom-type, or null>",
|
|
1092
|
+
|
|
1093
|
+
"layerMapVariants": {
|
|
1094
|
+
"domainModels": ["domain/models"],
|
|
1095
|
+
"dataSource": ["data/datasources"],
|
|
1096
|
+
"repoImpl": ["data/repositories"]
|
|
1097
|
+
},
|
|
1098
|
+
|
|
1099
|
+
"screenRefPatterns": [
|
|
1100
|
+
{
|
|
1101
|
+
"file": "<relative path>",
|
|
1102
|
+
"scrollContainer": "FlatList | ScrollView",
|
|
1103
|
+
"styleNames": ["container", "header", "listItem"],
|
|
1104
|
+
"sharedComponents": ["AppHeader", "PrimaryButton"],
|
|
1105
|
+
"navHook": "useNavigation | useRouter | none",
|
|
1106
|
+
"stateHooks": ["useSelector", "useDispatch"],
|
|
1107
|
+
"earlyReturnOrder": ["isLoading", "isAuthError", "error"],
|
|
1108
|
+
"listItemLayout": "<brief description>"
|
|
1109
|
+
}
|
|
1110
|
+
]
|
|
1111
|
+
}
|
|
1112
|
+
```
|
|
1113
|
+
|
|
1114
|
+
Print the complete fingerprint before proceeding. If any field is `[INFERRED]`,
|
|
1115
|
+
it must be visually flagged. Do not proceed to Phase 1 if more than 3
|
|
1116
|
+
FATAL-tier fields are unresolved.
|
|
1117
|
+
|
|
1118
|
+
---
|
|
1119
|
+
|
|
1120
|
+
### 0.K.1 — Default Fingerprint (No Existing Structure)
|
|
1121
|
+
|
|
1122
|
+
Apply this section when `GREENFIELD = true` OR `LAYER_FIRST_EXISTING = true`.
|
|
1123
|
+
Choose the appropriate sub-section based on the flag.
|
|
1124
|
+
|
|
1125
|
+
---
|
|
1126
|
+
|
|
1127
|
+
#### A — Feature-First Default (`GREENFIELD = true`)
|
|
1128
|
+
|
|
1129
|
+
No project structure exists at all. Generate a canonical clean-architecture
|
|
1130
|
+
feature-first layout under a `features/` root.
|
|
1131
|
+
|
|
1132
|
+
```json
|
|
1133
|
+
{
|
|
1134
|
+
"featuresRoot": "features",
|
|
1135
|
+
"featureFolderStyle": "camelCase",
|
|
1136
|
+
"fileNameStyle": "camelCase",
|
|
1137
|
+
"featureStyle": "feature-first",
|
|
1138
|
+
|
|
1139
|
+
"layerMap": {
|
|
1140
|
+
"domain": "domain",
|
|
1141
|
+
"domainModels": "domain/models",
|
|
1142
|
+
"domainRepo": "domain/repositories",
|
|
1143
|
+
"useCases": "domain/useCases",
|
|
1144
|
+
"data": "data",
|
|
1145
|
+
"dataSource": "data/datasources",
|
|
1146
|
+
"dtos": "data/dtos",
|
|
1147
|
+
"mappers": "data/mappers",
|
|
1148
|
+
"mocks": "data/datasources",
|
|
1149
|
+
"repoImpl": "data/repositories",
|
|
1150
|
+
"state": "state",
|
|
1151
|
+
"stateStore": "state/store",
|
|
1152
|
+
"selectors": "state/selectors",
|
|
1153
|
+
"presentation": "presentation",
|
|
1154
|
+
"screens": "presentation/screens",
|
|
1155
|
+
"di": null
|
|
1156
|
+
},
|
|
1157
|
+
|
|
1158
|
+
"diStyle": "per-feature-bootstrap",
|
|
1159
|
+
"diBootstrapFile": "diBootstrap.ts",
|
|
1160
|
+
"stateLib": "redux-toolkit",
|
|
1161
|
+
"thunkPattern": "thunk-extra",
|
|
1162
|
+
"returnTypeStyle": "result-wrapper",
|
|
1163
|
+
"resultTypeImport": null,
|
|
1164
|
+
"sliceNameStyle": "camelCase",
|
|
1165
|
+
"navLib": "react-navigation",
|
|
1166
|
+
"navVersion": "6",
|
|
1167
|
+
"navStackType": "native-stack",
|
|
1168
|
+
"apiLib": "axios",
|
|
1169
|
+
"envelopeStyle": "none",
|
|
1170
|
+
"mockSystem": "none",
|
|
1171
|
+
"mockGlobalFlag": null,
|
|
1172
|
+
"mockDelayField": null,
|
|
1173
|
+
"mockJsonFolder": "data/datasources",
|
|
1174
|
+
"tsStrict": true,
|
|
1175
|
+
"noImplicitAny": true,
|
|
1176
|
+
"explicitReturnType": false,
|
|
1177
|
+
"quoteStyle": "single",
|
|
1178
|
+
"trailingComma": "es5",
|
|
1179
|
+
"semi": true,
|
|
1180
|
+
"tabWidth": 2,
|
|
1181
|
+
"interfacePrefix": "none",
|
|
1182
|
+
"classStyle": "PascalCase",
|
|
1183
|
+
"operationIdStyle": "camelCase",
|
|
1184
|
+
"mockJsonStyle": "camelCase.mock.json",
|
|
1185
|
+
"aliasesDetected": false,
|
|
1186
|
+
"aliasMap": {},
|
|
1187
|
+
"preferAliases": false
|
|
1188
|
+
}
|
|
1189
|
+
```
|
|
1190
|
+
|
|
1191
|
+
Folder layout produced (example — `fdBooking` module):
|
|
1192
|
+
```
|
|
1193
|
+
features/
|
|
1194
|
+
fdBooking/
|
|
1195
|
+
domain/
|
|
1196
|
+
models/ ← entity interfaces
|
|
1197
|
+
repositories/ ← repository interfaces
|
|
1198
|
+
useCases/ ← use cases
|
|
1199
|
+
data/
|
|
1200
|
+
dtos/ ← DTOs
|
|
1201
|
+
mappers/ ← mappers
|
|
1202
|
+
datasources/ ← datasources, endpoints, mock JSON
|
|
1203
|
+
repositories/ ← repository implementations
|
|
1204
|
+
state/
|
|
1205
|
+
store/ ← Redux slices
|
|
1206
|
+
selectors/ ← selectors
|
|
1207
|
+
presentation/
|
|
1208
|
+
screens/ ← screen components
|
|
1209
|
+
diBootstrap.ts ← DI bootstrap (one per module)
|
|
1210
|
+
```
|
|
1211
|
+
|
|
1212
|
+
Log: `INFO: Greenfield defaults applied. Generated structure will follow canonical clean-architecture layout.`
|
|
1213
|
+
|
|
1214
|
+
---
|
|
1215
|
+
|
|
1216
|
+
#### B — Layer-First Default (`LAYER_FIRST_EXISTING = true`)
|
|
1217
|
+
|
|
1218
|
+
The project already has flat layer directories (e.g. `screens/`, `store/`, `services/`).
|
|
1219
|
+
No feature-scoped sub-folders exist. Place every generated file inside the existing
|
|
1220
|
+
layer directories, grouped only by a per-screen sub-folder under `screens/`.
|
|
1221
|
+
|
|
1222
|
+
All paths below are relative to `APP_ROOT`. Substitute the ACTUAL folder names
|
|
1223
|
+
detected in Phase 0.B (e.g. if services is named `api/`, use `api/` not `services/`).
|
|
1224
|
+
|
|
1225
|
+
```json
|
|
1226
|
+
{
|
|
1227
|
+
"featuresRoot": null,
|
|
1228
|
+
"featureFolderStyle": "PascalCase",
|
|
1229
|
+
"fileNameStyle": "PascalCase",
|
|
1230
|
+
"featureStyle": "layer-first",
|
|
1231
|
+
|
|
1232
|
+
"layerMap": {
|
|
1233
|
+
"domain": null,
|
|
1234
|
+
"domainModels": null,
|
|
1235
|
+
"domainRepo": null,
|
|
1236
|
+
"useCases": null,
|
|
1237
|
+
"data": null,
|
|
1238
|
+
"dataSource": "<LAYER_FIRST_SERVICES_DIR>",
|
|
1239
|
+
"dtos": null,
|
|
1240
|
+
"mappers": null,
|
|
1241
|
+
"mocks": null,
|
|
1242
|
+
"repoImpl": null,
|
|
1243
|
+
"state": "<LAYER_FIRST_STATE_DIR>",
|
|
1244
|
+
"stateStore": "<LAYER_FIRST_STATE_SLICES_DIR>",
|
|
1245
|
+
"selectors": null,
|
|
1246
|
+
"presentation": "<LAYER_FIRST_SCREENS_DIR>",
|
|
1247
|
+
"screens": "<LAYER_FIRST_SCREENS_DIR>",
|
|
1248
|
+
"di": null
|
|
1249
|
+
},
|
|
1250
|
+
|
|
1251
|
+
"diStyle": "none",
|
|
1252
|
+
"diBootstrapFile": null,
|
|
1253
|
+
"stateLib": "redux-toolkit",
|
|
1254
|
+
"thunkPattern": "direct-import",
|
|
1255
|
+
"returnTypeStyle": "direct-promise",
|
|
1256
|
+
"resultTypeImport": null,
|
|
1257
|
+
"sliceNameStyle": "camelCase",
|
|
1258
|
+
"navLib": "react-navigation",
|
|
1259
|
+
"navVersion": "6",
|
|
1260
|
+
"navStackType": "native-stack",
|
|
1261
|
+
"apiLib": "axios",
|
|
1262
|
+
"envelopeStyle": "none",
|
|
1263
|
+
"mockSystem": "none",
|
|
1264
|
+
"mockGlobalFlag": null,
|
|
1265
|
+
"mockDelayField": null,
|
|
1266
|
+
"mockJsonFolder": null,
|
|
1267
|
+
"tsStrict": true,
|
|
1268
|
+
"noImplicitAny": true,
|
|
1269
|
+
"explicitReturnType": false,
|
|
1270
|
+
"quoteStyle": "single",
|
|
1271
|
+
"trailingComma": "es5",
|
|
1272
|
+
"semi": true,
|
|
1273
|
+
"tabWidth": 2,
|
|
1274
|
+
"interfacePrefix": "none",
|
|
1275
|
+
"classStyle": "PascalCase",
|
|
1276
|
+
"operationIdStyle": "camelCase",
|
|
1277
|
+
"mockJsonStyle": null,
|
|
1278
|
+
"aliasesDetected": false,
|
|
1279
|
+
"aliasMap": {},
|
|
1280
|
+
"preferAliases": false
|
|
1281
|
+
}
|
|
1282
|
+
```
|
|
1283
|
+
|
|
1284
|
+
**Layer-first generation rules** (override Phases 3–7 defaults):
|
|
1285
|
+
|
|
1286
|
+
```
|
|
1287
|
+
Phase 3 (Domain layer) — SKIP entirely. domain*, useCases* paths are null.
|
|
1288
|
+
Do NOT generate entity interfaces, repository interfaces, or use cases.
|
|
1289
|
+
|
|
1290
|
+
Phase 4 (Data layer) — Generate ONLY the service file:
|
|
1291
|
+
File: <LAYER_FIRST_SERVICES_DIR>/<featureName>Service.ts
|
|
1292
|
+
(e.g. services/fdBookingService.ts)
|
|
1293
|
+
Pattern: plain async functions exported directly (no class wrapper).
|
|
1294
|
+
Imports the shared API client (detected in Phase 0.E) directly.
|
|
1295
|
+
One exported function per API endpoint in the spec.
|
|
1296
|
+
Response types exported as named TypeScript interfaces in the same file.
|
|
1297
|
+
Skip: DTOs, mappers, mock JSON, endpoint constants, repoImpl.
|
|
1298
|
+
|
|
1299
|
+
Phase 5 (State layer) — Generate ONLY the slice file:
|
|
1300
|
+
File: <LAYER_FIRST_STATE_SLICES_DIR>/<featureSliceName>Slice.ts
|
|
1301
|
+
(e.g. store/slices/fdBookingSlice.ts)
|
|
1302
|
+
Thunk pattern: direct-import — import service functions at the top of the
|
|
1303
|
+
slice file, call them directly inside createAsyncThunk (no ThunkExtra).
|
|
1304
|
+
Return type: direct-promise — service throws on error; thunk wraps with
|
|
1305
|
+
rejectWithValue.
|
|
1306
|
+
One slice per module (not per screen). Name: <featureName>Slice.
|
|
1307
|
+
Skip: selectors file (select via useSelector inline in screen).
|
|
1308
|
+
|
|
1309
|
+
Phase 6 (DI Bootstrap) — SKIP entirely. diStyle is none.
|
|
1310
|
+
|
|
1311
|
+
Phase 7 (Presentation) — Generate screen folder inside existing screens dir:
|
|
1312
|
+
Folder: <LAYER_FIRST_SCREENS_DIR>/<ScreenName>/
|
|
1313
|
+
Files:
|
|
1314
|
+
<ScreenName>.tsx ← main screen component
|
|
1315
|
+
<ScreenName>Style.ts ← StyleSheet (if reference screen has a style file)
|
|
1316
|
+
<ScreenName>Tokens.ts ← design tokens (only if reference screen has one)
|
|
1317
|
+
Import slice from: <relative path to LAYER_FIRST_STATE_SLICES_DIR>/<featureSliceName>Slice
|
|
1318
|
+
Import service types from: <relative path to LAYER_FIRST_SERVICES_DIR>/<featureName>Service
|
|
1319
|
+
```
|
|
1320
|
+
|
|
1321
|
+
Folder layout produced (example — new `TransactionHistory` screen in this project):
|
|
1322
|
+
```
|
|
1323
|
+
src/
|
|
1324
|
+
screens/
|
|
1325
|
+
TransactionHistory/
|
|
1326
|
+
TransactionHistory.tsx ← screen component
|
|
1327
|
+
TransactionHistoryStyle.ts ← StyleSheet
|
|
1328
|
+
store/
|
|
1329
|
+
slices/
|
|
1330
|
+
transactionHistorySlice.ts ← Redux slice + thunk
|
|
1331
|
+
services/
|
|
1332
|
+
transactionHistoryService.ts ← API functions + response types
|
|
1333
|
+
```
|
|
1334
|
+
|
|
1335
|
+
Log: `INFO: Layer-first project structure detected. Files placed in existing layer directories.`
|
|
1336
|
+
|
|
1337
|
+
---
|
|
1338
|
+
|
|
1339
|
+
### 0.L — Read Screen Reference Files
|
|
1340
|
+
|
|
1341
|
+
*Run this step only if `SCREEN_REF_PATHS` from PHASE -1 Q1 is non-empty.*
|
|
1342
|
+
|
|
1343
|
+
For each path in `SCREEN_REF_PATHS`:
|
|
1344
|
+
|
|
1345
|
+
1. Read the file
|
|
1346
|
+
2. Extract and record:
|
|
1347
|
+
- **Component structure**: function signature, props interface shape, return type
|
|
1348
|
+
- **StyleSheet shape**: names and categories of styles defined (e.g. `container`, `header`, `listItem`)
|
|
1349
|
+
- **Imports used**: which RN components, shared components, hooks, and utilities are imported
|
|
1350
|
+
- **FlatList vs ScrollView**: which scroll container is used; `keyExtractor` pattern
|
|
1351
|
+
- **Navigation pattern**: which hook is imported, how `navigation.navigate()` is called
|
|
1352
|
+
- **State hooks used**: `useSelector`, `useState`, `useMemo`, `useCallback`, `useEffect` patterns
|
|
1353
|
+
- **Conditional renders**: early return pattern for loading, error, auth error
|
|
1354
|
+
- **List item JSX structure**: how each row is laid out (columns, rows, nested Views)
|
|
1355
|
+
|
|
1356
|
+
Record as:
|
|
1357
|
+
```
|
|
1358
|
+
SCREEN_REF_PATTERNS: [
|
|
1359
|
+
{
|
|
1360
|
+
file: "<path>",
|
|
1361
|
+
scrollContainer: "FlatList | ScrollView",
|
|
1362
|
+
styleNames: ["container", "header", ...],
|
|
1363
|
+
sharedComponents: ["AppHeader", "PrimaryButton", ...],
|
|
1364
|
+
navHook: "useNavigation | useRouter | none",
|
|
1365
|
+
stateHooks: ["useSelector", "useDispatch", ...],
|
|
1366
|
+
earlyReturnOrder: ["isLoading", "isAuthError", "error"],
|
|
1367
|
+
listItemLayout: "<brief description>"
|
|
1368
|
+
},
|
|
1369
|
+
...
|
|
1370
|
+
]
|
|
1371
|
+
```
|
|
1372
|
+
|
|
1373
|
+
Add `screenRefPatterns` to the fingerprint:
|
|
1374
|
+
```json
|
|
1375
|
+
"screenRefPatterns": [{ "file": "...", "scrollContainer": "...", ... }]
|
|
1376
|
+
```
|
|
1377
|
+
|
|
1378
|
+
**Use in PHASE 7**: When `screenRefPatterns` is non-empty, the generated screen component MUST:
|
|
1379
|
+
- Use the same scroll container type (`FlatList` vs `ScrollView`)
|
|
1380
|
+
- Mirror the same early-return order (loading → authError → error)
|
|
1381
|
+
- Import and use the same shared components found in the reference screen
|
|
1382
|
+
- Apply the same StyleSheet naming conventions
|
|
1383
|
+
- Use the same navigation hook and call pattern
|
|
1384
|
+
|
|
1385
|
+
If multiple reference screens conflict on any dimension, use the pattern from the first screen and log a WARN.
|
|
1386
|
+
|
|
1387
|
+
---
|
|
1388
|
+
|
|
1389
|
+
## PHASE 1 — Parse Input Spec Files
|
|
1390
|
+
|
|
1391
|
+
### 1.1 — Locate Spec Files
|
|
1392
|
+
|
|
1393
|
+
Accept spec files as: file paths, dropped files, or inline Markdown.
|
|
1394
|
+
|
|
1395
|
+
**In module mode**: parse one spec file per screen in `SCREENS[]`. The result
|
|
1396
|
+
is a list of `FeatureSpec` objects (one per screen). All later phases loop over
|
|
1397
|
+
this list.
|
|
1398
|
+
|
|
1399
|
+
### 1.2 — Extract Spec Fields
|
|
1400
|
+
|
|
1401
|
+
```typescript
|
|
1402
|
+
interface FeatureSpec {
|
|
1403
|
+
screenName: string; // e.g. "TransactionHistory"
|
|
1404
|
+
featureFolderName: string; // TOP-LEVEL folder: derived from MODULE_NAME (module) or screenName (single)
|
|
1405
|
+
moduleName: string | null; // MODULE_NAME if mode=module; null if mode=single
|
|
1406
|
+
apiUrl: string;
|
|
1407
|
+
httpMethod: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
1408
|
+
requestBody: Record<string, unknown>;
|
|
1409
|
+
responseShape: Record<string, unknown>;
|
|
1410
|
+
businessLogic: string[];
|
|
1411
|
+
screenLogic: string[];
|
|
1412
|
+
responseMappings: ResponseMapping[];
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
interface ResponseMapping {
|
|
1416
|
+
responseField: string; // dot-notation: "data.user.name"
|
|
1417
|
+
uiLocation: string; // "displayed in the Name label"
|
|
1418
|
+
transform?: string; // "format as currency", "show as percentage"
|
|
1419
|
+
}
|
|
1420
|
+
```
|
|
1421
|
+
|
|
1422
|
+
**Precedence rule for `screenName`:**
|
|
1423
|
+
1. `SCREEN_NAME` (single) or `SCREENS[N].screenName` (module) from PHASE -1 Q1 — highest priority
|
|
1424
|
+
2. `## Screen Name` field inside the spec MD file — used only if Q1 name was not given
|
|
1425
|
+
3. FATAL if neither source provides a name for any screen
|
|
1426
|
+
|
|
1427
|
+
If a spec MD file's `## Screen Name` conflicts with the Q1 name, use Q1 and log a WARN.
|
|
1428
|
+
|
|
1429
|
+
**Merge rule:** PHASE -1 answers (Q2–Q5) for the matching screen are merged into its `FeatureSpec`.
|
|
1430
|
+
Interview answers take precedence over MD file values for every matching field.
|
|
1431
|
+
|
|
1432
|
+
### 1.3 — Validate Spec
|
|
1433
|
+
|
|
1434
|
+
Run for each `FeatureSpec` in the list:
|
|
1435
|
+
|
|
1436
|
+
FATAL if missing: `screenName`, `apiUrl`, `httpMethod`.
|
|
1437
|
+
WARN if missing: `responseShape` (generate empty entity), `requestBody`
|
|
1438
|
+
(assume `{}`), `responseMappings`, `businessLogic`, `screenLogic`.
|
|
1439
|
+
|
|
1440
|
+
### 1.4 — Derive Names (using fingerprint conventions)
|
|
1441
|
+
|
|
1442
|
+
Run for each `FeatureSpec`. All derived names below are **per screen**:
|
|
1443
|
+
|
|
1444
|
+
| Derived value | Rule |
|
|
1445
|
+
|---|---|
|
|
1446
|
+
| `featureFolderName` | **module mode**: apply `fingerprint.featureFolderStyle` to `MODULE_NAME`. **single mode**: apply to `screenName`. This is the TOP-LEVEL feature folder — shared by all screens in a module. |
|
|
1447
|
+
| `EntityName` | `screenName + "Item"` if response is array, else `screenName` |
|
|
1448
|
+
| `operationId` | Apply `fingerprint.operationIdStyle` to `screenName` |
|
|
1449
|
+
| `sliceName` | Apply `fingerprint.sliceNameStyle` to `screenName` |
|
|
1450
|
+
| `reducerKey` | Same value as `sliceName`. Used as the key in the store's reducer map and in selectors (`state.<reducerKey>`). Must not collide with existing keys in `fingerprint.reducerMapKeys` — if it does, append `"Feature"` suffix. |
|
|
1451
|
+
| `thunkName` | `"fetch" + screenName + "Thunk"` |
|
|
1452
|
+
| `useCaseName` | `"Fetch" + screenName + "UseCase"` |
|
|
1453
|
+
| `repoInterfaceName` | `fingerprint.interfacePrefix + screenName + "Repository"` |
|
|
1454
|
+
| `dataSourceClassName` | `screenName + "ApiDataSource"` |
|
|
1455
|
+
| `repoImplClassName` | `screenName + "RepositoryImpl"` |
|
|
1456
|
+
| `mockJsonFileName` | Apply `fingerprint.mockJsonStyle` to camelCase of `screenName` |
|
|
1457
|
+
| `endpointConstantName` | SCREAMING_SNAKE_CASE of `screenName` |
|
|
1458
|
+
| `screenComponentName` | `screenName + "Screen"` |
|
|
1459
|
+
|
|
1460
|
+
All file names: apply `fingerprint.fileNameStyle`.
|
|
1461
|
+
|
|
1462
|
+
### 1.5 — Shared Entity Deduplication (module mode only)
|
|
1463
|
+
|
|
1464
|
+
*Skip this step in single mode.*
|
|
1465
|
+
|
|
1466
|
+
After deriving names for all screens:
|
|
1467
|
+
|
|
1468
|
+
1. Compare `EntityName` across all screens in the module.
|
|
1469
|
+
2. If two or more screens share the same `EntityName` (same response shape for the same entity):
|
|
1470
|
+
- Mark that entity as **shared**: `EntityName.isShared = true`
|
|
1471
|
+
- Its domain model file path moves from the per-screen layer to the module's shared models folder:
|
|
1472
|
+
`FEATURE_DIR/domain/models/shared/<EntityName>.ts`
|
|
1473
|
+
- All screens that reference it import from that shared path instead of their own domain folder.
|
|
1474
|
+
- Generate the shared entity file **once** — do NOT duplicate it.
|
|
1475
|
+
3. Log: `SHARED ENTITY: <EntityName> used by [ScreenA, ScreenB] → generated at shared path`
|
|
1476
|
+
|
|
1477
|
+
---
|
|
1478
|
+
|
|
1479
|
+
## PHASE 2 — Resolve All Output Paths
|
|
1480
|
+
|
|
1481
|
+
Before generating any file, compute every output path using only fingerprint
|
|
1482
|
+
values. Store as `OutputPaths`.
|
|
1483
|
+
|
|
1484
|
+
**In module mode**, `FEATURE_DIR` is the SAME for all screens:
|
|
1485
|
+
```
|
|
1486
|
+
FEATURE_DIR = fingerprint.featuresRoot + "/" + featureFolderName (derived from MODULE_NAME)
|
|
1487
|
+
```
|
|
1488
|
+
|
|
1489
|
+
All screen-specific files go INSIDE `FEATURE_DIR` using per-screen names.
|
|
1490
|
+
The DI bootstrap and mockConfig are module-level — one per module, not per screen.
|
|
1491
|
+
|
|
1492
|
+
```
|
|
1493
|
+
FEATURE_DIR = fingerprint.featuresRoot + "/" + featureFolderName
|
|
1494
|
+
|
|
1495
|
+
# Per-screen paths (resolve once per FeatureSpec):
|
|
1496
|
+
PerScreenOutputPaths[screenName] = {
|
|
1497
|
+
domainModel: (isShared)
|
|
1498
|
+
FEATURE_DIR + "/" + fingerprint.layerMap.domainModels + "/shared/" + EntityName + ".ts"
|
|
1499
|
+
(not shared)
|
|
1500
|
+
FEATURE_DIR + "/" + fingerprint.layerMap.domainModels + "/" + EntityName + ".ts"
|
|
1501
|
+
domainRepo: FEATURE_DIR + "/" + fingerprint.layerMap.domainRepo + "/" + repoInterfaceName + ".ts"
|
|
1502
|
+
useCase: FEATURE_DIR + "/" + fingerprint.layerMap.useCases + "/Fetch" + screenName + "UseCase.ts"
|
|
1503
|
+
dto: FEATURE_DIR + "/" + fingerprint.layerMap.dtos + "/" + screenName + "Dto.ts"
|
|
1504
|
+
mapper: FEATURE_DIR + "/" + fingerprint.layerMap.mappers + "/" + screenName + "Mapper.ts"
|
|
1505
|
+
mockJson: FEATURE_DIR + "/" + fingerprint.layerMap.mocks + "/" + mockJsonFileName
|
|
1506
|
+
endpoints: FEATURE_DIR + "/" + fingerprint.layerMap.dataSource + "/" + screenName + "Endpoints.ts"
|
|
1507
|
+
dataSource: FEATURE_DIR + "/" + fingerprint.layerMap.dataSource + "/" + dataSourceClassName + ".ts"
|
|
1508
|
+
repoImpl: FEATURE_DIR + "/" + fingerprint.layerMap.data + "/repositories/" + repoImplClassName + ".ts"
|
|
1509
|
+
slice: FEATURE_DIR + "/" + fingerprint.layerMap.stateStore + "/" + sliceName + "Slice.ts"
|
|
1510
|
+
selectors: FEATURE_DIR + "/" + fingerprint.layerMap.selectors + "/" + screenName + "Selectors.ts"
|
|
1511
|
+
screen: FEATURE_DIR + "/" + fingerprint.layerMap.screens + "/" + screenComponentName + "/" + screenComponentName + ".tsx"
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
# Module-level paths (ONE per module, shared across all screens):
|
|
1515
|
+
ModuleOutputPaths = {
|
|
1516
|
+
mockConfig: FEATURE_DIR + "/" + fingerprint.layerMap.dataSource + "/mockConfig.ts"
|
|
1517
|
+
diBootstrap: FEATURE_DIR + "/diBootstrap.ts" (or global DI file if fingerprint shows a global one)
|
|
1518
|
+
}
|
|
1519
|
+
```
|
|
1520
|
+
|
|
1521
|
+
If any `fingerprint.layerMap.*` is `null`, skip that file and log a WARN.
|
|
1522
|
+
|
|
1523
|
+
**Import path computation for each file:**
|
|
1524
|
+
|
|
1525
|
+
For any file at depth D from `fingerprint.appRoot`, the prefix to reach
|
|
1526
|
+
`fingerprint.sharedRoot` is:
|
|
1527
|
+
```
|
|
1528
|
+
If fingerprint.preferAliases && fingerprint.sharedRootAlias != null:
|
|
1529
|
+
USE alias directly → e.g. "@shared/types/Result"
|
|
1530
|
+
Else:
|
|
1531
|
+
prefix = repeat("../", D) + fingerprint.sharedRoot
|
|
1532
|
+
→ e.g. "../../../../shared"
|
|
1533
|
+
```
|
|
1534
|
+
|
|
1535
|
+
Similarly for cross-layer imports within the feature, compute relative paths
|
|
1536
|
+
from the actual file depths — never hardcode `../../domain`.
|
|
1537
|
+
|
|
1538
|
+
---
|
|
1539
|
+
|
|
1540
|
+
> ### Phases 3–7 execution order
|
|
1541
|
+
>
|
|
1542
|
+
> **Single mode**: run Phases 3 → 4 → 5 → 6 → 7 once.
|
|
1543
|
+
>
|
|
1544
|
+
> **Module mode**: run the following loop:
|
|
1545
|
+
> ```
|
|
1546
|
+
> FOR each screen in SCREENS[] (in the order provided by the user):
|
|
1547
|
+
> Run Phase 3 for this screen (skip 3.1 if EntityName.isShared and already generated)
|
|
1548
|
+
> Run Phase 4 for this screen
|
|
1549
|
+
> Run Phase 5 for this screen
|
|
1550
|
+
> Run Phase 7 for this screen
|
|
1551
|
+
> END FOR
|
|
1552
|
+
> Then run Phase 6 ONCE for the whole module (all screens wired into one diBootstrap)
|
|
1553
|
+
> ```
|
|
1554
|
+
>
|
|
1555
|
+
> Phase 6 is intentionally **after** the per-screen loop in module mode so that
|
|
1556
|
+
> all use-case exports are known before the single DI bootstrap is written.
|
|
1557
|
+
|
|
1558
|
+
---
|
|
1559
|
+
|
|
1560
|
+
## PHASE 3 — Generate Domain Layer
|
|
1561
|
+
|
|
1562
|
+
### 3.1 — Entity Interface
|
|
1563
|
+
|
|
1564
|
+
File: `PerScreenOutputPaths[screenName].domainModel`
|
|
1565
|
+
|
|
1566
|
+
*In module mode: if `EntityName.isShared = true` and this entity was already generated
|
|
1567
|
+
for a previous screen, SKIP this step — do not regenerate the shared entity file.*
|
|
1568
|
+
|
|
1569
|
+
- Parse every key from `responseShape` recursively
|
|
1570
|
+
- Infer TypeScript types from values
|
|
1571
|
+
- All fields `readonly` (if `fingerprint.tsStrict === true`)
|
|
1572
|
+
- Optional `?` for fields noted as potentially absent
|
|
1573
|
+
- Never `any` — use `unknown` for opaque shapes
|
|
1574
|
+
- JSDoc comments for fields referenced in `responseMappings`
|
|
1575
|
+
- If `fingerprint.explicitReturnType === true`, add return types to any functions
|
|
1576
|
+
|
|
1577
|
+
### 3.2 — Repository Interface
|
|
1578
|
+
|
|
1579
|
+
File: `PerScreenOutputPaths[screenName].domainRepo`
|
|
1580
|
+
|
|
1581
|
+
- `Fetch<ScreenName>Params` interface: one property per `requestBody` field, camelCased
|
|
1582
|
+
- Return type is driven by `fingerprint.returnTypeStyle`:
|
|
1583
|
+
|
|
1584
|
+
```
|
|
1585
|
+
result-wrapper → Promise<Result<EntityName[]>>
|
|
1586
|
+
Import Result from fingerprint.resultTypeImport
|
|
1587
|
+
|
|
1588
|
+
custom-type → Promise<<ScreenName>Result>
|
|
1589
|
+
Define <ScreenName>Result inline in this file as a named interface
|
|
1590
|
+
containing the domain entities; match the shape used in existing
|
|
1591
|
+
repository interfaces in the project
|
|
1592
|
+
|
|
1593
|
+
direct-promise → Promise<EntityName[]>
|
|
1594
|
+
No wrapper — the implementation throws on error
|
|
1595
|
+
```
|
|
1596
|
+
|
|
1597
|
+
- Zero framework imports
|
|
1598
|
+
|
|
1599
|
+
### 3.3 — Use Case
|
|
1600
|
+
|
|
1601
|
+
File: `PerScreenOutputPaths[screenName].useCase`
|
|
1602
|
+
|
|
1603
|
+
- Class with `constructor(private readonly repository: <repoInterfaceName>)`
|
|
1604
|
+
- `execute(params)` method return type matches the repository interface return type
|
|
1605
|
+
- Each `businessLogic` rule from spec → one guard clause before delegation
|
|
1606
|
+
- Zero framework imports (no React, Redux, Axios, or any detected API lib)
|
|
1607
|
+
- **If `fingerprint.returnTypeStyle = "direct-promise"`**: let errors propagate naturally
|
|
1608
|
+
(no `try/catch` or `{ ok }` wrapping in the use case)
|
|
1609
|
+
- **If `fingerprint.returnTypeStyle = "result-wrapper"`**: catch and return
|
|
1610
|
+
`{ ok: false, error: message }`
|
|
1611
|
+
|
|
1612
|
+
---
|
|
1613
|
+
|
|
1614
|
+
## PHASE 4 — Generate Data Layer
|
|
1615
|
+
|
|
1616
|
+
### 4.1 — DTO
|
|
1617
|
+
|
|
1618
|
+
File: `PerScreenOutputPaths[screenName].dto`
|
|
1619
|
+
|
|
1620
|
+
- Mirror raw API response shape exactly, preserving original casing from spec
|
|
1621
|
+
- If spec shows PascalCase keys, generate BOTH PascalCase and camelCase variants as optional fields
|
|
1622
|
+
- Mapper will use `??` chaining across both variants
|
|
1623
|
+
|
|
1624
|
+
### 4.2 — Mapper
|
|
1625
|
+
|
|
1626
|
+
File: `PerScreenOutputPaths[screenName].mapper`
|
|
1627
|
+
|
|
1628
|
+
Rules (apply regardless of project):
|
|
1629
|
+
- Always generate `map<Entity>FromDto` AND `map<Entity>ListFromDto`
|
|
1630
|
+
- Every field: `(dto.PascalCaseKey ?? dto.camelCaseKey) ?? <default>`
|
|
1631
|
+
- Defaults: `''` for string, `0` for number, `false` for boolean, `[]` for array, `null` for object
|
|
1632
|
+
- One mapper file per entity — never merge unrelated entities into one mapper
|
|
1633
|
+
|
|
1634
|
+
### 4.3 — Mock JSON
|
|
1635
|
+
|
|
1636
|
+
File: `PerScreenOutputPaths[screenName].mockJson`
|
|
1637
|
+
|
|
1638
|
+
- Exact JSON matching the `responseShape` from spec
|
|
1639
|
+
- Populated with realistic placeholder values
|
|
1640
|
+
- Valid JSON (parseable by `JSON.parse`)
|
|
1641
|
+
|
|
1642
|
+
### 4.4 — Endpoint Constants
|
|
1643
|
+
|
|
1644
|
+
File: `PerScreenOutputPaths[screenName].endpoints`
|
|
1645
|
+
|
|
1646
|
+
```typescript
|
|
1647
|
+
export const <endpointConstantName>_ENDPOINTS = {
|
|
1648
|
+
FETCH: '<spec.apiUrl>',
|
|
1649
|
+
} as const;
|
|
1650
|
+
```
|
|
1651
|
+
|
|
1652
|
+
### 4.5 — Mock Config
|
|
1653
|
+
|
|
1654
|
+
File: `ModuleOutputPaths.mockConfig` — one file per module (shared by all screens)
|
|
1655
|
+
|
|
1656
|
+
In module mode: generate a single mockConfig that includes one entry per screen's `operationId`.
|
|
1657
|
+
Do NOT generate separate mockConfig files per screen.
|
|
1658
|
+
|
|
1659
|
+
Generate using the **exact shape** detected in `fingerprint.mockSystem`. Copy
|
|
1660
|
+
field names from the live mockConfig file detected in Phase 0.E. Do not invent
|
|
1661
|
+
field names.
|
|
1662
|
+
|
|
1663
|
+
If `fingerprint.mockSystem === "none"` → skip this file, add a TODO comment in
|
|
1664
|
+
the datasource.
|
|
1665
|
+
|
|
1666
|
+
Rules (apply regardless of shape):
|
|
1667
|
+
- `fingerprint.mockGlobalFlag` field always `false`
|
|
1668
|
+
- Per-API flag for `operationId` always `false`
|
|
1669
|
+
- `fingerprint.mockDelayField` value: copy from existing config or default to 800
|
|
1670
|
+
|
|
1671
|
+
### 4.6 — API DataSource
|
|
1672
|
+
|
|
1673
|
+
File: `PerScreenOutputPaths[screenName].dataSource`
|
|
1674
|
+
|
|
1675
|
+
> See: `references/api-envelope-patterns.md` — full templates for all `apiLib` + `envelopeStyle` combinations.
|
|
1676
|
+
> Read the section matching the detected values before generating.
|
|
1677
|
+
|
|
1678
|
+
Generate using the pattern matching `fingerprint.apiLib` + `fingerprint.envelopeStyle`.
|
|
1679
|
+
|
|
1680
|
+
**If apiLib = axios AND envelopeStyle = mb-operationId:**
|
|
1681
|
+
|
|
1682
|
+
```typescript
|
|
1683
|
+
import { apiClient } from '<fingerprint.apiClientImport>';
|
|
1684
|
+
import { <endpointConstantName>_ENDPOINTS } from './<screenName>Endpoints'; // file name uses screenName, not featureFolderName
|
|
1685
|
+
import { mockConfig } from './mockConfig';
|
|
1686
|
+
import type { Fetch<ScreenName>Params } from '<relative-path-to-domainRepo>';
|
|
1687
|
+
import mockData from '<relative-path-to-mockJson>';
|
|
1688
|
+
|
|
1689
|
+
function delay(ms: number): Promise<void> {
|
|
1690
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
// Replicate envelope builder exactly as detected — do not rename fields
|
|
1694
|
+
function buildEnvelope(operationId: string, reqdata: Record<string, unknown>): object {
|
|
1695
|
+
return { mb: { operationId, rq: { reqdata } } };
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
export class <dataSourceClassName> {
|
|
1699
|
+
async fetch<ScreenName>(params: Fetch<ScreenName>Params): Promise<Record<string, unknown>> {
|
|
1700
|
+
const { <operationId> } = mockConfig.apis;
|
|
1701
|
+
// fingerprint.mockGlobalFlag checked FIRST — always
|
|
1702
|
+
if (mockConfig.<fingerprint.mockGlobalFlag> || <operationId>.mockEnabled) {
|
|
1703
|
+
await delay(mockConfig.<fingerprint.mockDelayField>);
|
|
1704
|
+
return mockData as Record<string, unknown>;
|
|
1705
|
+
}
|
|
1706
|
+
const body = buildEnvelope('<operationId>', {
|
|
1707
|
+
// Map camelCase params → original API casing from spec requestBody
|
|
1708
|
+
});
|
|
1709
|
+
const response = await apiClient.<httpMethod.toLowerCase()><Record<string, unknown>>(
|
|
1710
|
+
<endpointConstantName>_ENDPOINTS.FETCH,
|
|
1711
|
+
body,
|
|
1712
|
+
);
|
|
1713
|
+
return response.data;
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
```
|
|
1717
|
+
|
|
1718
|
+
**If apiLib = fetch AND envelopeStyle = none:**
|
|
1719
|
+
|
|
1720
|
+
```typescript
|
|
1721
|
+
export class <dataSourceClassName> {
|
|
1722
|
+
async fetch<ScreenName>(params: Fetch<ScreenName>Params): Promise<Record<string, unknown>> {
|
|
1723
|
+
const response = await fetch('<spec.apiUrl>', {
|
|
1724
|
+
method: '<spec.httpMethod>',
|
|
1725
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1726
|
+
body: JSON.stringify({ /* mapped params */ }),
|
|
1727
|
+
});
|
|
1728
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
1729
|
+
return response.json();
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
```
|
|
1733
|
+
|
|
1734
|
+
**If apiLib = rtk-query or react-query:** Generate the appropriate endpoint/hook
|
|
1735
|
+
definition instead of a class. Adapt to the detected pattern.
|
|
1736
|
+
|
|
1737
|
+
**Request body field mapping:**
|
|
1738
|
+
- `requestBody` from spec shows API-expected field names and casing
|
|
1739
|
+
- `Params` interface uses camelCase
|
|
1740
|
+
- Datasource maps camelCase params → spec field names inside the request body
|
|
1741
|
+
|
|
1742
|
+
### 4.7 — Repository Implementation
|
|
1743
|
+
|
|
1744
|
+
File: `PerScreenOutputPaths[screenName].repoImpl`
|
|
1745
|
+
|
|
1746
|
+
> DTO type name rule: The DTO interface inside `<screenName>Dto.ts` is named `<ScreenName>Dto`.
|
|
1747
|
+
> The domain entity is `<EntityName>` (= `<ScreenName>Item` when the response is an array).
|
|
1748
|
+
> They are NOT the same name. Always cast raw items as `<ScreenName>Dto`, not `<EntityName>Dto`.
|
|
1749
|
+
|
|
1750
|
+
The return type of the implementation method is driven by `fingerprint.returnTypeStyle`.
|
|
1751
|
+
|
|
1752
|
+
**result-wrapper** (when `fingerprint.returnTypeStyle = "result-wrapper"`):
|
|
1753
|
+
|
|
1754
|
+
```typescript
|
|
1755
|
+
import type { Result } from '<fingerprint.resultTypeImport>';
|
|
1756
|
+
import type { <repoInterfaceName>, Fetch<ScreenName>Params } from '<relative-path-to-domainRepo>';
|
|
1757
|
+
import type { <EntityName> } from '<relative-path-to-domainModel>';
|
|
1758
|
+
import type { <dataSourceClassName> } from '<relative-path-to-dataSource>';
|
|
1759
|
+
import { map<Entity>ListFromDto } from '<relative-path-to-mapper>';
|
|
1760
|
+
|
|
1761
|
+
export class <repoImplClassName> implements <repoInterfaceName> {
|
|
1762
|
+
constructor(private readonly dataSource: <dataSourceClassName>) {}
|
|
1763
|
+
|
|
1764
|
+
async fetch<ScreenName>(params: Fetch<ScreenName>Params): Promise<Result<<EntityName>[]>> {
|
|
1765
|
+
try {
|
|
1766
|
+
const raw = await this.dataSource.fetch<ScreenName>(params);
|
|
1767
|
+
const items = (raw.Data ?? raw.data ?? []) as <ScreenName>Dto[]; // cast to DTO, not entity
|
|
1768
|
+
return { ok: true, data: map<Entity>ListFromDto(items) };
|
|
1769
|
+
} catch (err: unknown) {
|
|
1770
|
+
const apiErr = err as { statusCode?: number; message?: string };
|
|
1771
|
+
return { ok: false, error: apiErr.message ?? 'Failed to fetch <featureFolderName>' };
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
```
|
|
1776
|
+
|
|
1777
|
+
**custom-type** (when `fingerprint.returnTypeStyle = "custom-type"`):
|
|
1778
|
+
|
|
1779
|
+
```typescript
|
|
1780
|
+
import type { <repoInterfaceName>, Fetch<ScreenName>Params, <ScreenName>Result } from '<relative-path-to-domainRepo>';
|
|
1781
|
+
import type { <dataSourceClassName> } from '<relative-path-to-dataSource>';
|
|
1782
|
+
import { map<Entity>ListFromDto } from '<relative-path-to-mapper>';
|
|
1783
|
+
|
|
1784
|
+
export class <repoImplClassName> implements <repoInterfaceName> {
|
|
1785
|
+
constructor(private readonly dataSource: <dataSourceClassName>) {}
|
|
1786
|
+
|
|
1787
|
+
async fetch<ScreenName>(params: Fetch<ScreenName>Params): Promise<<ScreenName>Result> {
|
|
1788
|
+
const raw = await this.dataSource.fetch<ScreenName>(params);
|
|
1789
|
+
const items = (raw.Data ?? raw.data ?? []) as <ScreenName>Dto[]; // cast to DTO, not entity
|
|
1790
|
+
return { <primaryField>: map<Entity>ListFromDto(items) };
|
|
1791
|
+
// Shape must match the custom-type interface defined in the domain repo file
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
```
|
|
1795
|
+
|
|
1796
|
+
**direct-promise** (when `fingerprint.returnTypeStyle = "direct-promise"`):
|
|
1797
|
+
|
|
1798
|
+
```typescript
|
|
1799
|
+
import type { <repoInterfaceName>, Fetch<ScreenName>Params } from '<relative-path-to-domainRepo>';
|
|
1800
|
+
import type { <EntityName> } from '<relative-path-to-domainModel>';
|
|
1801
|
+
import type { <dataSourceClassName> } from '<relative-path-to-dataSource>';
|
|
1802
|
+
import { map<Entity>ListFromDto } from '<relative-path-to-mapper>';
|
|
1803
|
+
|
|
1804
|
+
export class <repoImplClassName> implements <repoInterfaceName> {
|
|
1805
|
+
constructor(private readonly dataSource: <dataSourceClassName>) {}
|
|
1806
|
+
|
|
1807
|
+
async fetch<ScreenName>(params: Fetch<ScreenName>Params): Promise<<EntityName>[]> {
|
|
1808
|
+
const raw = await this.dataSource.fetch<ScreenName>(params);
|
|
1809
|
+
const items = (raw.Data ?? raw.data ?? []) as <ScreenName>Dto[]; // cast to DTO, not entity
|
|
1810
|
+
return map<Entity>ListFromDto(items);
|
|
1811
|
+
// Errors propagate as thrown exceptions — caller handles them
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
```
|
|
1815
|
+
|
|
1816
|
+
---
|
|
1817
|
+
|
|
1818
|
+
## PHASE 5 — Generate State Layer
|
|
1819
|
+
|
|
1820
|
+
Generate state files only for the detected `fingerprint.stateLib`.
|
|
1821
|
+
|
|
1822
|
+
### 5.1 — Redux Toolkit (stateLib = redux-toolkit)
|
|
1823
|
+
|
|
1824
|
+
File: `PerScreenOutputPaths[screenName].slice`
|
|
1825
|
+
|
|
1826
|
+
Use `fingerprint.thunkPattern` to determine injection style.
|
|
1827
|
+
|
|
1828
|
+
**ThunkExtra pattern** (when `fingerprint.thunkPattern = "thunk-extra"`):
|
|
1829
|
+
|
|
1830
|
+
The thunk body structure depends on `fingerprint.returnTypeStyle`.
|
|
1831
|
+
|
|
1832
|
+
**result-wrapper** (use case returns `{ ok, data, error }`):
|
|
1833
|
+
|
|
1834
|
+
```typescript
|
|
1835
|
+
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
|
|
1836
|
+
import type { PayloadAction } from '@reduxjs/toolkit'; // needed: used in extraReducers fulfilled case
|
|
1837
|
+
// also add if any slice reducer receives a payload
|
|
1838
|
+
import type { <EntityName> } from '<relative-path-to-domainModel>';
|
|
1839
|
+
|
|
1840
|
+
export interface <ScreenName>State {
|
|
1841
|
+
items: <EntityName>[];
|
|
1842
|
+
isLoading: boolean;
|
|
1843
|
+
isRefreshing: boolean;
|
|
1844
|
+
error: string | null;
|
|
1845
|
+
isAuthError: boolean;
|
|
1846
|
+
// Additional state fields derived from spec.screenLogic
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
const initialState: <ScreenName>State = {
|
|
1850
|
+
items: [],
|
|
1851
|
+
isLoading: false,
|
|
1852
|
+
isRefreshing: false,
|
|
1853
|
+
error: null,
|
|
1854
|
+
isAuthError: false,
|
|
1855
|
+
// Add any extra fields from spec.screenLogic with appropriate zero-value defaults
|
|
1856
|
+
};
|
|
1857
|
+
|
|
1858
|
+
export const fetch<ScreenName>Thunk = createAsyncThunk(
|
|
1859
|
+
'<sliceName>/fetch', // e.g. 'transactionHistory/fetch'
|
|
1860
|
+
async (params: { /* from Fetch<ScreenName>Params */ }, { extra, rejectWithValue }) => {
|
|
1861
|
+
try {
|
|
1862
|
+
const { fetch<ScreenName>UseCase } = extra as {
|
|
1863
|
+
fetch<ScreenName>UseCase: {
|
|
1864
|
+
execute: (p: typeof params) => Promise<{ ok: boolean; data?: <EntityName>[]; error?: string }>;
|
|
1865
|
+
};
|
|
1866
|
+
};
|
|
1867
|
+
const result = await fetch<ScreenName>UseCase.execute(params);
|
|
1868
|
+
if (!result.ok) return rejectWithValue({ message: result.error ?? 'Unknown error', isAuthError: false });
|
|
1869
|
+
return result.data;
|
|
1870
|
+
} catch (err: unknown) {
|
|
1871
|
+
const apiErr = err as { statusCode?: number; message?: string };
|
|
1872
|
+
return rejectWithValue({ message: apiErr.message ?? 'Unexpected error', isAuthError: apiErr.statusCode === 401 });
|
|
1873
|
+
}
|
|
1874
|
+
},
|
|
1875
|
+
);
|
|
1876
|
+
```
|
|
1877
|
+
|
|
1878
|
+
**custom-type** (use case returns a named type — no `.ok` field):
|
|
1879
|
+
|
|
1880
|
+
```typescript
|
|
1881
|
+
export const fetch<ScreenName>Thunk = createAsyncThunk(
|
|
1882
|
+
'<sliceName>/fetch', // e.g. 'transactionHistory/fetch'
|
|
1883
|
+
async (params: { /* from Fetch<ScreenName>Params */ }, { extra, rejectWithValue }) => {
|
|
1884
|
+
try {
|
|
1885
|
+
const { fetch<ScreenName>UseCase } = extra as {
|
|
1886
|
+
fetch<ScreenName>UseCase: {
|
|
1887
|
+
execute: (p: typeof params) => Promise<<ScreenName>Result>;
|
|
1888
|
+
};
|
|
1889
|
+
};
|
|
1890
|
+
return await fetch<ScreenName>UseCase.execute(params);
|
|
1891
|
+
} catch (err: unknown) {
|
|
1892
|
+
const apiErr = err as { statusCode?: number; message?: string };
|
|
1893
|
+
return rejectWithValue({ message: apiErr.message ?? 'Unexpected error', isAuthError: apiErr.statusCode === 401 });
|
|
1894
|
+
}
|
|
1895
|
+
},
|
|
1896
|
+
);
|
|
1897
|
+
```
|
|
1898
|
+
|
|
1899
|
+
**direct-promise** (use case returns a plain type and throws on error):
|
|
1900
|
+
|
|
1901
|
+
```typescript
|
|
1902
|
+
export const fetch<ScreenName>Thunk = createAsyncThunk(
|
|
1903
|
+
'<sliceName>/fetch', // e.g. 'transactionHistory/fetch'
|
|
1904
|
+
async (params: { /* from Fetch<ScreenName>Params */ }, { extra, rejectWithValue }) => {
|
|
1905
|
+
try {
|
|
1906
|
+
const { fetch<ScreenName>UseCase } = extra as {
|
|
1907
|
+
fetch<ScreenName>UseCase: {
|
|
1908
|
+
execute: (p: typeof params) => Promise<<EntityName>[]>;
|
|
1909
|
+
};
|
|
1910
|
+
};
|
|
1911
|
+
return await fetch<ScreenName>UseCase.execute(params);
|
|
1912
|
+
} catch (err: unknown) {
|
|
1913
|
+
const apiErr = err as { statusCode?: number; message?: string };
|
|
1914
|
+
return rejectWithValue({ message: apiErr.message ?? 'Unexpected error', isAuthError: apiErr.statusCode === 401 });
|
|
1915
|
+
}
|
|
1916
|
+
},
|
|
1917
|
+
);
|
|
1918
|
+
```
|
|
1919
|
+
|
|
1920
|
+
// NOTE: If spec.screenLogic contains "pull to refresh" or "refresh", generate
|
|
1921
|
+
// refresh<ScreenName>Thunk as described in the refreshThunk generation section below.
|
|
1922
|
+
|
|
1923
|
+
const <sliceName>Slice = createSlice({
|
|
1924
|
+
name: '<sliceName>',
|
|
1925
|
+
initialState,
|
|
1926
|
+
reducers: {
|
|
1927
|
+
reset<ScreenName>: () => initialState,
|
|
1928
|
+
// Reducers derived from spec.screenLogic (filters, sorts, selectedItem, tabs)
|
|
1929
|
+
},
|
|
1930
|
+
extraReducers: builder => {
|
|
1931
|
+
// fetch thunk
|
|
1932
|
+
builder
|
|
1933
|
+
.addCase(fetch<ScreenName>Thunk.pending, (state) => {
|
|
1934
|
+
state.isLoading = true;
|
|
1935
|
+
state.error = null;
|
|
1936
|
+
state.isAuthError = false;
|
|
1937
|
+
})
|
|
1938
|
+
.addCase(fetch<ScreenName>Thunk.fulfilled, (state, action: PayloadAction<<EntityName>[]>) => {
|
|
1939
|
+
state.isLoading = false;
|
|
1940
|
+
state.items = action.payload;
|
|
1941
|
+
})
|
|
1942
|
+
.addCase(fetch<ScreenName>Thunk.rejected, (state, action) => {
|
|
1943
|
+
state.isLoading = false;
|
|
1944
|
+
state.error = action.payload as string ?? 'Failed to load';
|
|
1945
|
+
state.isAuthError = action.payload === 'AUTH_ERROR';
|
|
1946
|
+
});
|
|
1947
|
+
// refresh thunk (if generated)
|
|
1948
|
+
// builder
|
|
1949
|
+
// .addCase(refresh<ScreenName>Thunk.pending, (state) => { state.isRefreshing = true; state.error = null; })
|
|
1950
|
+
// .addCase(refresh<ScreenName>Thunk.fulfilled, (state, action) => { state.isRefreshing = false; state.items = action.payload; })
|
|
1951
|
+
// .addCase(refresh<ScreenName>Thunk.rejected, (state, action) => { state.isRefreshing = false; state.error = action.payload as string ?? 'Failed to refresh'; });
|
|
1952
|
+
},
|
|
1953
|
+
});
|
|
1954
|
+
```
|
|
1955
|
+
|
|
1956
|
+
**Direct import pattern** (when `fingerprint.thunkPattern = "direct-import"`):
|
|
1957
|
+
|
|
1958
|
+
Import the use case at the top of the slice file and call it directly without
|
|
1959
|
+
`extra`.
|
|
1960
|
+
|
|
1961
|
+
**refreshThunk generation (add when spec.screenLogic contains "pull to refresh" or "refresh"):**
|
|
1962
|
+
|
|
1963
|
+
Generate a separate `refresh<ScreenName>Thunk` alongside the fetch thunk. It has the
|
|
1964
|
+
exact same body as `fetch<ScreenName>Thunk` for the applicable `returnTypeStyle`. The
|
|
1965
|
+
only difference is the action type string and which state fields it sets:
|
|
1966
|
+
|
|
1967
|
+
```typescript
|
|
1968
|
+
export const refresh<ScreenName>Thunk = createAsyncThunk(
|
|
1969
|
+
'<sliceName>/refresh', // e.g. 'transactionHistory/refresh'
|
|
1970
|
+
async (params: { /* same as fetch params */ }, { extra, rejectWithValue }) => {
|
|
1971
|
+
// identical body to fetch<ScreenName>Thunk
|
|
1972
|
+
},
|
|
1973
|
+
);
|
|
1974
|
+
|
|
1975
|
+
// In extraReducers:
|
|
1976
|
+
.addCase(refresh<ScreenName>Thunk.pending, (state) => {
|
|
1977
|
+
state.isRefreshing = true; // NOT isLoading — this is the critical difference
|
|
1978
|
+
state.error = null;
|
|
1979
|
+
})
|
|
1980
|
+
.addCase(refresh<ScreenName>Thunk.fulfilled, (state, action) => {
|
|
1981
|
+
state.isRefreshing = false;
|
|
1982
|
+
state.items = action.payload ?? [];
|
|
1983
|
+
})
|
|
1984
|
+
.addCase(refresh<ScreenName>Thunk.rejected, (state, action) => {
|
|
1985
|
+
state.isRefreshing = false;
|
|
1986
|
+
const payload = action.payload as { message: string; isAuthError: boolean };
|
|
1987
|
+
state.error = payload?.message ?? 'Failed to refresh';
|
|
1988
|
+
});
|
|
1989
|
+
```
|
|
1990
|
+
|
|
1991
|
+
**State fields from screenLogic:**
|
|
1992
|
+
|
|
1993
|
+
| screenLogic phrase | State field | Reducer |
|
|
1994
|
+
|---|---|---|
|
|
1995
|
+
| "filter by category" | `filter: string` | `setFilter` |
|
|
1996
|
+
| "sortable by X" | `sortOrder: 'asc' \| 'desc'` | `setSortOrder` |
|
|
1997
|
+
| "selected item detail" | `selectedItemId: string \| null` | `setSelectedItem` |
|
|
1998
|
+
| "tab switcher" | `activeTab: string` | `setActiveTab` |
|
|
1999
|
+
| "pagination" | `page: number`, `hasMore: boolean` | `setPage`, `setHasMore` |
|
|
2000
|
+
|
|
2001
|
+
### 5.2 — Selectors
|
|
2002
|
+
|
|
2003
|
+
File: `PerScreenOutputPaths[screenName].selectors`
|
|
2004
|
+
|
|
2005
|
+
```typescript
|
|
2006
|
+
import type { RootState } from '<fingerprint.rootStateImport>';
|
|
2007
|
+
|
|
2008
|
+
export const select<ScreenName>Items = (state: RootState) =>
|
|
2009
|
+
state.<reducerKey>.items; // reducerKey derived in Phase 1.4 for this specific screen
|
|
2010
|
+
|
|
2011
|
+
// One selector per state field in the slice
|
|
2012
|
+
```
|
|
2013
|
+
|
|
2014
|
+
`reducerKey` is the per-screen derived value from Phase 1.4 — NOT the single `fingerprint.reducerKey`.
|
|
2015
|
+
In module mode each screen has its own `reducerKey` and they MUST all be distinct.
|
|
2016
|
+
|
|
2017
|
+
### 5.3 — Zustand (stateLib = zustand)
|
|
2018
|
+
|
|
2019
|
+
> See: `references/state-library-patterns.md` — Zustand section.
|
|
2020
|
+
> Detect zustand version and pattern (single store vs slice) before generating.
|
|
2021
|
+
|
|
2022
|
+
Generate a store file using the detected zustand pattern (slice-based or single
|
|
2023
|
+
store). Do not use Redux patterns.
|
|
2024
|
+
|
|
2025
|
+
### 5.4 — Context API (stateLib = context-api)
|
|
2026
|
+
|
|
2027
|
+
> See: `references/state-library-patterns.md` — Context API section.
|
|
2028
|
+
|
|
2029
|
+
Generate context + reducer + provider. Do not use Redux patterns.
|
|
2030
|
+
|
|
2031
|
+
### 5.5 — MobX (stateLib = mobx)
|
|
2032
|
+
|
|
2033
|
+
> See: `references/state-library-patterns.md` — MobX section.
|
|
2034
|
+
> Detect MobX version (6+ uses `makeAutoObservable`; 4/5 uses `@observable` decorators).
|
|
2035
|
+
|
|
2036
|
+
Generate a MobX store class. Do not generate a Redux slice, selectors file, or thunk.
|
|
2037
|
+
The screen component must be wrapped in `observer()` from `mobx-react-lite`.
|
|
2038
|
+
Computed values are class getters, not a separate selectors file.
|
|
2039
|
+
|
|
2040
|
+
---
|
|
2041
|
+
|
|
2042
|
+
## PHASE 6 — Generate DI Wiring
|
|
2043
|
+
|
|
2044
|
+
Use `fingerprint.diStyle` to choose the generation path.
|
|
2045
|
+
|
|
2046
|
+
**Module mode**: Phase 6 runs ONCE after the per-screen loop (Phases 3–5 and 7).
|
|
2047
|
+
One DI file is generated for the ENTIRE module. It imports and instantiates every
|
|
2048
|
+
datasource, repository, and use case across ALL screens — not one file per screen.
|
|
2049
|
+
|
|
2050
|
+
---
|
|
2051
|
+
|
|
2052
|
+
### 6.A — per-feature-bootstrap (diStyle = "per-feature-bootstrap")
|
|
2053
|
+
|
|
2054
|
+
File: `ModuleOutputPaths.diBootstrap` — one file for the whole module
|
|
2055
|
+
|
|
2056
|
+
**Module mode template** (expand the pattern for each screen in `SCREENS[]`):
|
|
2057
|
+
```typescript
|
|
2058
|
+
// ─── <ModuleName> DI Bootstrap ───────────────────────────────────────────────
|
|
2059
|
+
// Auto-generated. Do not edit manually.
|
|
2060
|
+
|
|
2061
|
+
// Screen 1: <ScreenName1>
|
|
2062
|
+
import { <DataSourceClassName1> } from '<relative-path>';
|
|
2063
|
+
import { <RepoImplClassName1> } from '<relative-path>';
|
|
2064
|
+
import { Fetch<ScreenName1>UseCase } from '<relative-path>';
|
|
2065
|
+
|
|
2066
|
+
// Screen 2: <ScreenName2>
|
|
2067
|
+
import { <DataSourceClassName2> } from '<relative-path>';
|
|
2068
|
+
import { <RepoImplClassName2> } from '<relative-path>';
|
|
2069
|
+
import { Fetch<ScreenName2>UseCase } from '<relative-path>';
|
|
2070
|
+
|
|
2071
|
+
// ... (repeat for each screen)
|
|
2072
|
+
|
|
2073
|
+
const <screenName1>DataSource = new <DataSourceClassName1>();
|
|
2074
|
+
const <screenName1>Repository = new <RepoImplClassName1>(<screenName1>DataSource);
|
|
2075
|
+
export const fetch<ScreenName1>UseCase = new Fetch<ScreenName1>UseCase(<screenName1>Repository);
|
|
2076
|
+
|
|
2077
|
+
const <screenName2>DataSource = new <DataSourceClassName2>();
|
|
2078
|
+
const <screenName2>Repository = new <RepoImplClassName2>(<screenName2>DataSource);
|
|
2079
|
+
export const fetch<ScreenName2>UseCase = new Fetch<ScreenName2>UseCase(<screenName2>Repository);
|
|
2080
|
+
|
|
2081
|
+
// ... (repeat for each screen)
|
|
2082
|
+
```
|
|
2083
|
+
|
|
2084
|
+
**Single mode template** (unchanged behaviour — one screen):
|
|
2085
|
+
```typescript
|
|
2086
|
+
import { <dataSourceClassName> } from '<relative-path>';
|
|
2087
|
+
import { <repoImplClassName> } from '<relative-path>';
|
|
2088
|
+
import { Fetch<ScreenName>UseCase } from '<relative-path>';
|
|
2089
|
+
|
|
2090
|
+
// <screenNameCamelCase> = camelCase of screenName, e.g. TransactionHistory → transactionHistory
|
|
2091
|
+
const <screenNameCamelCase>DataSource = new <dataSourceClassName>();
|
|
2092
|
+
const <screenNameCamelCase>Repository = new <repoImplClassName>(<screenNameCamelCase>DataSource);
|
|
2093
|
+
export const fetch<ScreenName>UseCase = new Fetch<ScreenName>UseCase(<screenNameCamelCase>Repository);
|
|
2094
|
+
```
|
|
2095
|
+
|
|
2096
|
+
**If `fingerprint.thunkPattern = "thunk-extra"`**, append a comment:
|
|
2097
|
+
```typescript
|
|
2098
|
+
// Wire into store extra argument in <fingerprint.storeConfigFile>:
|
|
2099
|
+
// { ...existingExtra, fetch<ScreenName>UseCase }
|
|
2100
|
+
```
|
|
2101
|
+
|
|
2102
|
+
**Store wiring instruction (append to diBootstrap.ts or DI container file):**
|
|
2103
|
+
|
|
2104
|
+
After the DI wiring, always add the following comments describing the exact manual steps
|
|
2105
|
+
the developer must perform.
|
|
2106
|
+
|
|
2107
|
+
*In module mode, list ALL screens in the STORE REDUCER and THUNK EXTRA blocks, and list
|
|
2108
|
+
ALL screen registrations in the NAVIGATOR block:*
|
|
2109
|
+
|
|
2110
|
+
```typescript
|
|
2111
|
+
// ─── MANUAL WIRING REQUIRED ───────────────────────────────────────────────
|
|
2112
|
+
//
|
|
2113
|
+
// 1. STORE REDUCER — open <fingerprint.storeConfigFile> and add one import + key per screen:
|
|
2114
|
+
//
|
|
2115
|
+
// import <sliceName1>Reducer from '<relative-path-to-slice1>';
|
|
2116
|
+
// import <sliceName2>Reducer from '<relative-path-to-slice2>';
|
|
2117
|
+
//
|
|
2118
|
+
// reducer: {
|
|
2119
|
+
// ...existingReducers,
|
|
2120
|
+
// <reducerKey1>: <sliceName1>Reducer,
|
|
2121
|
+
// <reducerKey2>: <sliceName2>Reducer,
|
|
2122
|
+
// }
|
|
2123
|
+
//
|
|
2124
|
+
// 2. THUNK EXTRA (thunk-extra only) — in the same file, add to middleware:
|
|
2125
|
+
//
|
|
2126
|
+
// getDefaultMiddleware({ thunk: { extraArgument: {
|
|
2127
|
+
// ...existingExtra,
|
|
2128
|
+
// fetch<ScreenName1>UseCase,
|
|
2129
|
+
// fetch<ScreenName2>UseCase,
|
|
2130
|
+
// }}})
|
|
2131
|
+
//
|
|
2132
|
+
// 3. NAVIGATOR — open <fingerprint.navScreenFile> and add one import + Stack.Screen per screen:
|
|
2133
|
+
//
|
|
2134
|
+
// import <ScreenName1>Screen from '<relative-path-to-screen1>';
|
|
2135
|
+
// import <ScreenName2>Screen from '<relative-path-to-screen2>';
|
|
2136
|
+
//
|
|
2137
|
+
// // Inside the navigator stack:
|
|
2138
|
+
// <Stack.Screen name="<ScreenName1>" component={<ScreenName1>Screen} />
|
|
2139
|
+
// <Stack.Screen name="<ScreenName2>" component={<ScreenName2>Screen} />
|
|
2140
|
+
//
|
|
2141
|
+
// // Add to <fingerprint.navParamsPattern> type:
|
|
2142
|
+
// <ScreenName1>: { /* params if any */ } | undefined;
|
|
2143
|
+
// <ScreenName2>: { /* params if any */ } | undefined;
|
|
2144
|
+
//
|
|
2145
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
2146
|
+
```
|
|
2147
|
+
|
|
2148
|
+
---
|
|
2149
|
+
|
|
2150
|
+
### 6.B — per-feature-container (diStyle = "per-feature-container")
|
|
2151
|
+
|
|
2152
|
+
File: `FEATURE_DIR + "/" + fingerprint.layerMap.di + "/" + moduleName + "Container.ts"`
|
|
2153
|
+
|
|
2154
|
+
In **module mode**: generate ONE container class for the entire module. It holds one datasource,
|
|
2155
|
+
repository, and use-case getter per screen — all wired inside the single class.
|
|
2156
|
+
|
|
2157
|
+
In **single mode**: generate one container for the single screen (same as before).
|
|
2158
|
+
|
|
2159
|
+
Use the class and method names matching those in `fingerprint.diContainerClass`.
|
|
2160
|
+
|
|
2161
|
+
```typescript
|
|
2162
|
+
import { <dataSourceClassName> } from '<relative-path>';
|
|
2163
|
+
import { <repoImplClassName> } from '<relative-path>';
|
|
2164
|
+
import { Fetch<ScreenName>UseCase } from '<relative-path>';
|
|
2165
|
+
import type { I<ScreenName>Repository } from '<relative-path>';
|
|
2166
|
+
|
|
2167
|
+
class <ScreenName>Container {
|
|
2168
|
+
private static _instance: <ScreenName>Container | null = null;
|
|
2169
|
+
|
|
2170
|
+
private readonly _dataSource: <dataSourceClassName>;
|
|
2171
|
+
private readonly _repository: I<ScreenName>Repository;
|
|
2172
|
+
|
|
2173
|
+
private constructor() {
|
|
2174
|
+
this._dataSource = new <dataSourceClassName>();
|
|
2175
|
+
this._repository = new <repoImplClassName>(this._dataSource);
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
static getInstance(): <ScreenName>Container {
|
|
2179
|
+
if (!<ScreenName>Container._instance) {
|
|
2180
|
+
<ScreenName>Container._instance = new <ScreenName>Container();
|
|
2181
|
+
}
|
|
2182
|
+
return <ScreenName>Container._instance;
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2185
|
+
get fetch<ScreenName>UseCase(): Fetch<ScreenName>UseCase {
|
|
2186
|
+
return new Fetch<ScreenName>UseCase(this._repository);
|
|
2187
|
+
}
|
|
2188
|
+
|
|
2189
|
+
// For testing: reset singleton between test suites
|
|
2190
|
+
static resetInstance(): void {
|
|
2191
|
+
<ScreenName>Container._instance = null;
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
|
|
2195
|
+
export const <featureFolderName>Container = <ScreenName>Container.getInstance();
|
|
2196
|
+
```
|
|
2197
|
+
|
|
2198
|
+
Consumers call `<featureFolderName>Container.fetch<ScreenName>UseCase.execute(params)`.
|
|
2199
|
+
|
|
2200
|
+
---
|
|
2201
|
+
|
|
2202
|
+
### 6.C — global (diStyle = "global")
|
|
2203
|
+
|
|
2204
|
+
Append the new feature's wiring to the existing global DI file. Never overwrite
|
|
2205
|
+
existing entries.
|
|
2206
|
+
|
|
2207
|
+
---
|
|
2208
|
+
|
|
2209
|
+
### 6.D — none (diStyle = "none")
|
|
2210
|
+
|
|
2211
|
+
Skip DI file generation. Components instantiate dependencies inline (rare — log a WARN).
|
|
2212
|
+
|
|
2213
|
+
---
|
|
2214
|
+
|
|
2215
|
+
## PHASE 7 — Generate Screen Component
|
|
2216
|
+
|
|
2217
|
+
File: `PerScreenOutputPaths[screenName].screen`
|
|
2218
|
+
|
|
2219
|
+
### 7.1 — Imports
|
|
2220
|
+
|
|
2221
|
+
Resolve all imports using the fingerprint:
|
|
2222
|
+
- `useDispatch`, `useSelector` from `react-redux` (if redux)
|
|
2223
|
+
- `AppDispatch` from fingerprint-resolved store path
|
|
2224
|
+
- Thunks from `PerScreenOutputPaths[screenName].slice` (relative import)
|
|
2225
|
+
- Selectors from `PerScreenOutputPaths[screenName].selectors` (relative import)
|
|
2226
|
+
- Entity type from `PerScreenOutputPaths[screenName].domainModel` (relative import)
|
|
2227
|
+
|
|
2228
|
+
For navigation (if spec.screenLogic mentions navigation):
|
|
2229
|
+
- Import navigator hook matching `fingerprint.navLib`:
|
|
2230
|
+
- `useNavigation` from `@react-navigation/native`
|
|
2231
|
+
- `useRouter` from `expo-router`
|
|
2232
|
+
- `Navigation` from `react-native-navigation`
|
|
2233
|
+
|
|
2234
|
+
### 7.2 — Response Mapping Implementation
|
|
2235
|
+
|
|
2236
|
+
> See: `assets/transform-rules.json` — full keyword-to-TypeScript-expression map.
|
|
2237
|
+
> Match each transform phrase in the spec against entries in that file (case-insensitive substring match).
|
|
2238
|
+
> If no entry matches, render as raw string and log a WARN.
|
|
2239
|
+
|
|
2240
|
+
For every `responseMappings` entry in the spec:
|
|
2241
|
+
|
|
2242
|
+
1. Trace the dot-notation path through the entity interface using optional chaining
|
|
2243
|
+
2. Match the transform keyword against `assets/transform-rules.json`. Use `nullSafe` variant when the field is optional.
|
|
2244
|
+
Quick reference (non-exhaustive — always read the JSON file):
|
|
2245
|
+
- `"formatted as indian currency"` → `toLocaleString('en-IN', { style: 'currency', currency: 'INR' })`
|
|
2246
|
+
- `"formatted as percentage"` → `` `${(value * 100).toFixed(2)}%` ``
|
|
2247
|
+
- `"formatted as date"` → `new Date(value).toLocaleDateString('en-IN', { day: '2-digit', month: 'short', year: 'numeric' })`
|
|
2248
|
+
- `"formatted as boolean badge"` → conditional `<Text style>` with color
|
|
2249
|
+
- `"formatted as compact number"` → Cr/L abbreviation for Indian units
|
|
2250
|
+
- `"unique key"` / `"hidden"` → used as keyExtractor only, not rendered
|
|
2251
|
+
3. Add a comment above each JSX element citing the spec mapping instruction
|
|
2252
|
+
4. If mapping says "hide when null/empty" → conditional render `{value ? <Text>...</Text> : null}`
|
|
2253
|
+
|
|
2254
|
+
### 7.3 — Screen Logic Implementation
|
|
2255
|
+
|
|
2256
|
+
> See: `references/screen-logic-patterns.md` — complete JSX implementations for every pattern below.
|
|
2257
|
+
> Read the matching section(s) and compose them into the screen component.
|
|
2258
|
+
|
|
2259
|
+
| screenLogic type | Implementation |
|
|
2260
|
+
|---|---|
|
|
2261
|
+
| Navigate to detail on tap | `onPress` → `navigation.navigate(...)` using fingerprint nav style |
|
|
2262
|
+
| Filter list | Local or Redux filter state, filtered `FlatList data` |
|
|
2263
|
+
| Summary card at top | `ListHeaderComponent` on FlatList |
|
|
2264
|
+
| Tab switcher | Tab bar component, state for active tab |
|
|
2265
|
+
| Pull to refresh | `RefreshControl` with refresh thunk dispatch |
|
|
2266
|
+
| Empty state | `ListEmptyComponent` |
|
|
2267
|
+
| Auth error + logout | Check `isAuthError`, render logout UI |
|
|
2268
|
+
| Pagination / load more | `onEndReached` with page state |
|
|
2269
|
+
| Search / query filter | `TextInput` with `useMemo` filtered array |
|
|
2270
|
+
|
|
2271
|
+
---
|
|
2272
|
+
|
|
2273
|
+
## PHASE 8 — Validation
|
|
2274
|
+
|
|
2275
|
+
> **Module mode**: run Phase 8 in a loop over every screen in `SCREENS[]`.
|
|
2276
|
+
> Check `ModuleOutputPaths` (mockConfig, diBootstrap) once after the per-screen loop.
|
|
2277
|
+
|
|
2278
|
+
### 8.1 — Mapping Coverage
|
|
2279
|
+
|
|
2280
|
+
For every `responseMappings` entry of the current screen: verify it appears in the screen component.
|
|
2281
|
+
Missing mapping → FATAL. Fix before output.
|
|
2282
|
+
|
|
2283
|
+
### 8.2 — File Checklist
|
|
2284
|
+
|
|
2285
|
+
Report a table of all expected files vs. actually generated files. Any layer
|
|
2286
|
+
that was `null` in the fingerprint is listed as `SKIPPED (not in project)` —
|
|
2287
|
+
not as missing.
|
|
2288
|
+
|
|
2289
|
+
### 8.3 — Pattern Consistency
|
|
2290
|
+
|
|
2291
|
+
| Check | Rule |
|
|
2292
|
+
|---|---|
|
|
2293
|
+
| API client import matches fingerprint | No feature-local axios.create |
|
|
2294
|
+
| mockGlobalFlag checked before per-API flag | In every datasource method |
|
|
2295
|
+
| All mapper fields use `??` chaining | No bare `dto.field` |
|
|
2296
|
+
| Thunk uses `rejectWithValue` | Never throws from thunk |
|
|
2297
|
+
| Use case has zero framework imports | No React, Redux, or API lib |
|
|
2298
|
+
| No `any` (if fingerprint.noImplicitAny) | Use `unknown` |
|
|
2299
|
+
| Readonly entity fields (if fingerprint.tsStrict) | Applied |
|
|
2300
|
+
| Import aliases match fingerprint.aliasMap | Consistent with project |
|
|
2301
|
+
| Quote/semicolon style matches fingerprint | Consistent with prettier config |
|
|
2302
|
+
|
|
2303
|
+
### 8.4 — Import Depth Verification
|
|
2304
|
+
|
|
2305
|
+
For each generated file: count actual directory depth from `appRoot`. Verify
|
|
2306
|
+
that relative imports compute to valid paths. Log any mismatch as FATAL.
|
|
2307
|
+
|
|
2308
|
+
---
|
|
2309
|
+
|
|
2310
|
+
## PHASE 9 — Output Summary
|
|
2311
|
+
|
|
2312
|
+
**Single mode summary:**
|
|
2313
|
+
```
|
|
2314
|
+
=== feature-generator OUTPUT SUMMARY ===
|
|
2315
|
+
Mode: single
|
|
2316
|
+
Architecture Fingerprint: [attached above]
|
|
2317
|
+
Screen: <ScreenName>
|
|
2318
|
+
Files generated: <N>
|
|
2319
|
+
Files skipped: <N> (layers not present in project)
|
|
2320
|
+
Mappings covered: <N>/<N> ✅
|
|
2321
|
+
|
|
2322
|
+
Generated:
|
|
2323
|
+
<list of PerScreenOutputPaths[ScreenName]>
|
|
2324
|
+
<ModuleOutputPaths.mockConfig>
|
|
2325
|
+
<ModuleOutputPaths.diBootstrap>
|
|
2326
|
+
|
|
2327
|
+
Skipped (layer not in project):
|
|
2328
|
+
<list of null layers>
|
|
2329
|
+
|
|
2330
|
+
Required manual steps:
|
|
2331
|
+
1. Add fetch<ScreenName>UseCase to <fingerprint.storeConfigFile> extra argument
|
|
2332
|
+
2. Register <ScreenName>Screen in <fingerprint.navScreenFile>
|
|
2333
|
+
3. Set <operationId>.mockEnabled = true in mockConfig for local testing
|
|
2334
|
+
==================================================
|
|
2335
|
+
```
|
|
2336
|
+
|
|
2337
|
+
**Module mode summary:**
|
|
2338
|
+
```
|
|
2339
|
+
=== feature-generator MODULE SUMMARY ===
|
|
2340
|
+
Mode: module
|
|
2341
|
+
Module: <ModuleName>
|
|
2342
|
+
Screens: <N>
|
|
2343
|
+
Architecture Fingerprint: [attached above]
|
|
2344
|
+
|
|
2345
|
+
Module folder: <FEATURE_DIR>/
|
|
2346
|
+
diBootstrap.ts [MODULE-LEVEL]
|
|
2347
|
+
domain/
|
|
2348
|
+
models/
|
|
2349
|
+
shared/
|
|
2350
|
+
<SharedEntityName>.ts [SHARED — used by: ScreenA, ScreenB]
|
|
2351
|
+
<ScreenName1>Item.ts
|
|
2352
|
+
<ScreenName2>Item.ts
|
|
2353
|
+
repositories/
|
|
2354
|
+
I<ScreenName1>Repository.ts
|
|
2355
|
+
I<ScreenName2>Repository.ts
|
|
2356
|
+
useCases/
|
|
2357
|
+
Fetch<ScreenName1>UseCase.ts
|
|
2358
|
+
Fetch<ScreenName2>UseCase.ts
|
|
2359
|
+
data/
|
|
2360
|
+
datasources/
|
|
2361
|
+
mockConfig.ts [MODULE-LEVEL]
|
|
2362
|
+
<ScreenName1>Endpoints.ts
|
|
2363
|
+
<ScreenName2>Endpoints.ts
|
|
2364
|
+
<ScreenName1>ApiDataSource.ts
|
|
2365
|
+
<ScreenName2>ApiDataSource.ts
|
|
2366
|
+
dtos/
|
|
2367
|
+
<ScreenName1>Dto.ts
|
|
2368
|
+
<ScreenName2>Dto.ts
|
|
2369
|
+
mappers/
|
|
2370
|
+
<ScreenName1>Mapper.ts
|
|
2371
|
+
<ScreenName2>Mapper.ts
|
|
2372
|
+
repositories/
|
|
2373
|
+
<ScreenName1>RepositoryImpl.ts
|
|
2374
|
+
<ScreenName2>RepositoryImpl.ts
|
|
2375
|
+
state/
|
|
2376
|
+
store/
|
|
2377
|
+
<screenName1>Slice.ts
|
|
2378
|
+
<screenName2>Slice.ts
|
|
2379
|
+
selectors/
|
|
2380
|
+
<ScreenName1>Selectors.ts
|
|
2381
|
+
<ScreenName2>Selectors.ts
|
|
2382
|
+
presentation/
|
|
2383
|
+
screens/
|
|
2384
|
+
<ScreenName1>Screen/
|
|
2385
|
+
<ScreenName1>Screen.tsx
|
|
2386
|
+
<ScreenName2>Screen/
|
|
2387
|
+
<ScreenName2>Screen.tsx
|
|
2388
|
+
|
|
2389
|
+
Files generated: <N total across all screens>
|
|
2390
|
+
Files skipped: <N> (layers not present in project)
|
|
2391
|
+
Shared entities: <N>
|
|
2392
|
+
|
|
2393
|
+
Per-screen mapping coverage:
|
|
2394
|
+
<ScreenName1>: <N>/<N> mappings ✅
|
|
2395
|
+
<ScreenName2>: <N>/<N> mappings ✅
|
|
2396
|
+
|
|
2397
|
+
Required manual steps:
|
|
2398
|
+
1. Add all slice reducers to <fingerprint.storeConfigFile>:
|
|
2399
|
+
<screenName1>: <sliceName1>Reducer
|
|
2400
|
+
<screenName2>: <sliceName2>Reducer
|
|
2401
|
+
2. Add all use cases to store extra argument:
|
|
2402
|
+
fetch<ScreenName1>UseCase, fetch<ScreenName2>UseCase
|
|
2403
|
+
3. Register all screens in <fingerprint.navScreenFile>:
|
|
2404
|
+
<ScreenName1>, <ScreenName2>
|
|
2405
|
+
4. Set mockEnabled = true per screen in mockConfig for local testing
|
|
2406
|
+
==================================================
|
|
2407
|
+
```
|
|
2408
|
+
|
|
2409
|
+
> **Do NOT report completion to the user after PHASE 9.**
|
|
2410
|
+
> Proceed immediately to PHASE 10 — Post-Completion Validation.
|
|
2411
|
+
|
|
2412
|
+
---
|
|
2413
|
+
|
|
2414
|
+
## PHASE 10 — Post-Completion Validation & Auto-Fix
|
|
2415
|
+
|
|
2416
|
+
> **Run this phase after all files in PHASE 9 have been generated.**
|
|
2417
|
+
> Do NOT report completion to the user until this phase produces a `READY` status.
|
|
2418
|
+
> For every failed check: fix the issue, then re-run that specific check before proceeding to the next.
|
|
2419
|
+
> Never skip a check. Never mark a check as passed without verifying it.
|
|
2420
|
+
>
|
|
2421
|
+
> **Module mode**: run checks 10.1–10.7 in a loop over every screen in `SCREENS[]`.
|
|
2422
|
+
> Also validate `ModuleOutputPaths` (diBootstrap, mockConfig) once after the per-screen loop.
|
|
2423
|
+
> The 10.8 final report aggregates results across all screens.
|
|
2424
|
+
|
|
2425
|
+
---
|
|
2426
|
+
|
|
2427
|
+
### 10.1 — File Existence Check
|
|
2428
|
+
|
|
2429
|
+
For every path in `PerScreenOutputPaths[currentScreen]` AND (after the loop) `ModuleOutputPaths`:
|
|
2430
|
+
- Read the file — if it does not exist or is empty, regenerate it using the phase that produces it
|
|
2431
|
+
- Log: `MISSING → regenerating <path>`
|
|
2432
|
+
|
|
2433
|
+
Pass condition: every non-null path entry exists and has content.
|
|
2434
|
+
|
|
2435
|
+
---
|
|
2436
|
+
|
|
2437
|
+
### 10.2 — Import Resolution Check
|
|
2438
|
+
|
|
2439
|
+
For each generated file:
|
|
2440
|
+
- Read every import statement
|
|
2441
|
+
- For relative imports: verify the target file exists at the computed path
|
|
2442
|
+
- For alias imports: verify the alias prefix exists in `fingerprint.aliasMap`
|
|
2443
|
+
- If a broken import is found: correct the path using the fingerprint, rewrite the file
|
|
2444
|
+
- Log: `BROKEN IMPORT in <file>: <bad-path> → fixed to <correct-path>`
|
|
2445
|
+
|
|
2446
|
+
Pass condition: zero broken imports across all generated files.
|
|
2447
|
+
|
|
2448
|
+
---
|
|
2449
|
+
|
|
2450
|
+
### 10.3 — Response Mapping Coverage Check
|
|
2451
|
+
|
|
2452
|
+
For every entry in `RESPONSE_MAPPINGS` collected in PHASE -1 Q5:
|
|
2453
|
+
- Search the generated screen component for the `responseField` value
|
|
2454
|
+
- If a field is present in mappings but absent from the screen: add it
|
|
2455
|
+
- If a `transform` was specified but not applied: apply it using the matching rule from `assets/transform-rules.json`
|
|
2456
|
+
- Log: `UNMAPPED FIELD <responseField> → added to screen with transform: <transform or none>`
|
|
2457
|
+
|
|
2458
|
+
Pass condition: every `RESPONSE_MAPPINGS` entry appears in the screen component.
|
|
2459
|
+
|
|
2460
|
+
---
|
|
2461
|
+
|
|
2462
|
+
### 10.4 — Business Logic Coverage Check
|
|
2463
|
+
|
|
2464
|
+
For every rule in `BUSINESS_LOGIC_RULES` collected in PHASE -1 Q4:
|
|
2465
|
+
- Read the use case `execute()` method
|
|
2466
|
+
- Verify a guard clause or conditional exists that directly implements the rule
|
|
2467
|
+
- If a rule has no corresponding guard: add it to the use case
|
|
2468
|
+
- Log: `MISSING RULE "<rule>" → added guard clause to <useCase path>`
|
|
2469
|
+
|
|
2470
|
+
Pass condition: every business logic rule has a traceable guard clause in the use case.
|
|
2471
|
+
|
|
2472
|
+
---
|
|
2473
|
+
|
|
2474
|
+
### 10.5 — Test Case Coverage Check
|
|
2475
|
+
|
|
2476
|
+
*Skip this check with `N/A` if no test cases were provided in PHASE -1 Q3.*
|
|
2477
|
+
|
|
2478
|
+
For each entry in `TEST_CASES`:
|
|
2479
|
+
- `type: happy` — verify the primary success path exists: data loads, renders in FlatList/View, no error state shown
|
|
2480
|
+
- `type: edge` — verify the edge condition is handled: empty state component, boundary guard in use case, null-safe access
|
|
2481
|
+
- `type: error` — verify the error state is rendered: error message in screen, `rejectWithValue` in thunk, error field in state
|
|
2482
|
+
- If a scenario is unhandled: add the required handling to the appropriate file (use case, slice, or screen)
|
|
2483
|
+
- Log: `UNHANDLED TC-<id> "<description>" → fixed in <file>`
|
|
2484
|
+
|
|
2485
|
+
Pass condition: all test case scenarios have implementation coverage.
|
|
2486
|
+
|
|
2487
|
+
---
|
|
2488
|
+
|
|
2489
|
+
### 10.6 — User Story Coverage Check
|
|
2490
|
+
|
|
2491
|
+
*Skip this check with `N/A` if no user stories were provided in PHASE -1 Q2.*
|
|
2492
|
+
|
|
2493
|
+
For each entry in `USER_STORIES`:
|
|
2494
|
+
- Map the `action` and `benefit` to a feature behaviour (navigation, data display, interaction, feedback)
|
|
2495
|
+
- Verify that behaviour is implemented across: screen logic, state slice, or navigation wiring
|
|
2496
|
+
- If a story is not covered: identify the phase responsible and regenerate only the affected file(s)
|
|
2497
|
+
- Log: `UNCOVERED US-<id> "<story>" → fixed in <file>`
|
|
2498
|
+
|
|
2499
|
+
Pass condition: every user story action has a traceable implementation.
|
|
2500
|
+
|
|
2501
|
+
---
|
|
2502
|
+
|
|
2503
|
+
### 10.7 — Code Quality Check
|
|
2504
|
+
|
|
2505
|
+
Read each generated file and verify the following rules. Fix any violation in-place:
|
|
2506
|
+
|
|
2507
|
+
| Rule | Check |
|
|
2508
|
+
|---|---|
|
|
2509
|
+
| R3 — `??` chaining | No bare `dto.field` — must be `(dto.FieldA ?? dto.fieldA) ?? default` |
|
|
2510
|
+
| R4 — mockGlobalFlag order | `fingerprint.mockGlobalFlag` checked BEFORE per-operationId `mockEnabled` |
|
|
2511
|
+
| R7 — Use case purity | Use case file has zero imports from react, redux, axios, or any detected API lib |
|
|
2512
|
+
| R5 — Readonly fields | All entity interface fields are `readonly` when `fingerprint.tsStrict === true` |
|
|
2513
|
+
| No `any` | No `any` type used when `fingerprint.noImplicitAny === true` — use `unknown` |
|
|
2514
|
+
| R12 — Style consistency | Quote style, semicolons, and tab width match `fingerprint.quoteStyle / semi / tabWidth` |
|
|
2515
|
+
|
|
2516
|
+
Log: `CODE QUALITY: <rule> violation in <file> at line <approx> → fixed`
|
|
2517
|
+
|
|
2518
|
+
Pass condition: zero unfixed violations.
|
|
2519
|
+
|
|
2520
|
+
---
|
|
2521
|
+
|
|
2522
|
+
### 10.8 — Final Status Report
|
|
2523
|
+
|
|
2524
|
+
After all checks pass (or have been fixed and re-verified):
|
|
2525
|
+
|
|
2526
|
+
**Single mode report:**
|
|
2527
|
+
```
|
|
2528
|
+
=== PHASE 10 VALIDATION REPORT ===
|
|
2529
|
+
Mode: single
|
|
2530
|
+
Screen: <ScreenName>
|
|
2531
|
+
Files verified: <N>/<N>
|
|
2532
|
+
Imports resolved: <N>/<N>
|
|
2533
|
+
Mappings covered: <N>/<N>
|
|
2534
|
+
Business rules covered: <N>/<N>
|
|
2535
|
+
Test cases handled: <N>/<N> (or N/A)
|
|
2536
|
+
User stories covered: <N>/<N> (or N/A)
|
|
2537
|
+
Code quality checks: <N>/<N>
|
|
2538
|
+
|
|
2539
|
+
Auto-fixed issues:
|
|
2540
|
+
<list each MISSING / BROKEN / UNMAPPED / UNHANDLED / CODE QUALITY item that was fixed>
|
|
2541
|
+
("None" if everything passed on first check)
|
|
2542
|
+
|
|
2543
|
+
Status: READY — all checks passed
|
|
2544
|
+
OR BLOCKED — <describe exactly what the user must provide to unblock>
|
|
2545
|
+
===================================
|
|
2546
|
+
```
|
|
2547
|
+
|
|
2548
|
+
**Module mode report:**
|
|
2549
|
+
```
|
|
2550
|
+
=== PHASE 10 MODULE VALIDATION REPORT ===
|
|
2551
|
+
Mode: module
|
|
2552
|
+
Module: <ModuleName>
|
|
2553
|
+
Screens validated: <N>
|
|
2554
|
+
|
|
2555
|
+
Per-screen results:
|
|
2556
|
+
<ScreenName1>:
|
|
2557
|
+
Files: <N>/<N> Imports: <N>/<N> Mappings: <N>/<N> Rules: <N>/<N>
|
|
2558
|
+
TC: <N>/<N> US: <N>/<N> Quality: <N>/<N>
|
|
2559
|
+
<ScreenName2>:
|
|
2560
|
+
Files: <N>/<N> Imports: <N>/<N> Mappings: <N>/<N> Rules: <N>/<N>
|
|
2561
|
+
TC: <N>/<N> US: <N>/<N> Quality: <N>/<N>
|
|
2562
|
+
|
|
2563
|
+
Module-level files:
|
|
2564
|
+
diBootstrap.ts: ✅ | ❌
|
|
2565
|
+
mockConfig.ts: ✅ | ❌
|
|
2566
|
+
|
|
2567
|
+
Totals:
|
|
2568
|
+
Files verified: <N>/<N>
|
|
2569
|
+
Imports resolved: <N>/<N>
|
|
2570
|
+
Mappings covered: <N>/<N>
|
|
2571
|
+
Business rules covered: <N>/<N>
|
|
2572
|
+
Test cases handled: <N>/<N> (or N/A)
|
|
2573
|
+
User stories covered: <N>/<N> (or N/A)
|
|
2574
|
+
Code quality checks: <N>/<N>
|
|
2575
|
+
|
|
2576
|
+
Auto-fixed issues:
|
|
2577
|
+
<list each MISSING / BROKEN / UNMAPPED / UNHANDLED / CODE QUALITY item with screen name prefix>
|
|
2578
|
+
("None" if everything passed on first check)
|
|
2579
|
+
|
|
2580
|
+
Status: READY — all screens and module-level files passed
|
|
2581
|
+
OR BLOCKED — <describe exactly what input or clarification is needed per screen>
|
|
2582
|
+
==========================================
|
|
2583
|
+
```
|
|
2584
|
+
|
|
2585
|
+
Only report completion to the user after this report shows `READY`.
|
|
2586
|
+
If `BLOCKED`, describe exactly what input or clarification is needed and wait for the user.
|
|
2587
|
+
|
|
2588
|
+
---
|
|
2589
|
+
|
|
2590
|
+
## Universal Rules
|
|
2591
|
+
|
|
2592
|
+
### R1 — Zero Hardcoded Paths
|
|
2593
|
+
No file, import path, folder name, or alias may be written without first
|
|
2594
|
+
deriving it from the Architecture Fingerprint. If the fingerprint cannot resolve
|
|
2595
|
+
it, log FATAL.
|
|
2596
|
+
|
|
2597
|
+
### R2 — Fingerprint Is the Only Source of Truth
|
|
2598
|
+
The spec tells you WHAT to build. The fingerprint tells you HOW (paths, imports,
|
|
2599
|
+
patterns, naming). Never override fingerprint values with guesses.
|
|
2600
|
+
|
|
2601
|
+
### R3 — Field Casing Always Uses `??` Chaining
|
|
2602
|
+
```typescript
|
|
2603
|
+
// ✅
|
|
2604
|
+
const name = (dto.UserName ?? dto.userName) ?? '';
|
|
2605
|
+
// ❌
|
|
2606
|
+
const name = dto.UserName;
|
|
2607
|
+
```
|
|
2608
|
+
|
|
2609
|
+
### R4 — mockGlobalFlag Before Per-API Flag
|
|
2610
|
+
```typescript
|
|
2611
|
+
// ✅
|
|
2612
|
+
if (mockConfig[fingerprint.mockGlobalFlag] || apiConfig.mockEnabled) { ... }
|
|
2613
|
+
// ❌
|
|
2614
|
+
if (apiConfig.mockEnabled || mockConfig[fingerprint.mockGlobalFlag]) { ... }
|
|
2615
|
+
```
|
|
2616
|
+
|
|
2617
|
+
### R5 — Entity Immutability
|
|
2618
|
+
`readonly` on all entity fields when `fingerprint.tsStrict === true`.
|
|
2619
|
+
|
|
2620
|
+
### R6 — Mapping Coverage Is Mandatory
|
|
2621
|
+
Every `responseMappings` entry in the spec must appear in the screen. Missing
|
|
2622
|
+
entries block output (FATAL).
|
|
2623
|
+
|
|
2624
|
+
### R7 — Business Logic Stays in Domain
|
|
2625
|
+
All spec `businessLogic` rules → use case `execute()` guard clauses only.
|
|
2626
|
+
Never in slice, datasource, or component.
|
|
2627
|
+
|
|
2628
|
+
### R8 — Screen Logic Stays in Screen
|
|
2629
|
+
All spec `screenLogic` behaviour → screen component only.
|
|
2630
|
+
Never in use case or slice.
|
|
2631
|
+
|
|
2632
|
+
### R9 — One Mapper Per Entity
|
|
2633
|
+
If the response produces two entity types → two mapper files.
|
|
2634
|
+
|
|
2635
|
+
### R10 — Append, Never Overwrite DI
|
|
2636
|
+
When adding to a global DI file, append new exports only.
|
|
2637
|
+
|
|
2638
|
+
### R11 — Skip, Don't Invent Missing Layers
|
|
2639
|
+
If a layer is `null` in the fingerprint, skip it with a WARN. Do not invent
|
|
2640
|
+
a folder that doesn't exist in the project.
|
|
2641
|
+
|
|
2642
|
+
### R12 — Quote/Semicolon/Indent Style Matches Project
|
|
2643
|
+
Apply `fingerprint.quoteStyle`, `fingerprint.semi`, `fingerprint.tabWidth` to
|
|
2644
|
+
every generated file. Consistency with the existing codebase is non-negotiable.
|
|
2645
|
+
|
|
2646
|
+
---
|
|
2647
|
+
|
|
2648
|
+
## Decision Points
|
|
2649
|
+
|
|
2650
|
+
| Situation | Action |
|
|
2651
|
+
|---|---|
|
|
2652
|
+
| App root cannot be located | FATAL — ask user to specify |
|
|
2653
|
+
| Feature organisation style is ambiguous | Generate to dominant style; WARN about secondary |
|
|
2654
|
+
| Detected state lib is not redux-toolkit | Adapt generation to detected lib; do not use RTK patterns |
|
|
2655
|
+
| No envelope pattern detected | Generate plain request body without wrapper |
|
|
2656
|
+
| No mock system detected | Skip mockConfig; add TODO comment in datasource |
|
|
2657
|
+
| Import aliases found but not used in existing features | Set preferAliases = false; use relative paths |
|
|
2658
|
+
| Response is single object (not array) | Generate `mapFromDto` only; skip list mapper |
|
|
2659
|
+
| Response has deeply nested data | Generate nested sub-entity interfaces and mappers |
|
|
2660
|
+
| Multiple spec files provided | In **module mode**: all spec files belong to one shared feature folder. Run Phases 3–5, 7 per screen then Phase 6 once. In **single mode** with multiple files: treat as multiple independent features and run the full pipeline separately for each. |
|
|
2661
|
+
| Global DI file exists | Append to it; never overwrite |
|
|
2662
|
+
| Screen file already exists | Ask user before overwriting; offer to merge mappings |
|
|
2663
|
+
| Navigation not detected | Generate screen component without nav imports; WARN |
|
|
2664
|
+
| RootState type not locatable | Generate selector with `any` as state type; mark as FIXME |
|
|
2665
|
+
| `dtos` layer is null | Inline DTO type inside mapper file |
|
|
2666
|
+
| `selectors` layer is null | Inline selectors at bottom of slice file |
|
|
2667
|
+
|
|
2668
|
+
---
|
|
2669
|
+
|
|
2670
|
+
## Integration with flutter-rn-orchestrator
|
|
2671
|
+
|
|
2672
|
+
When called via the orchestrator:
|
|
2673
|
+
- **Skip Phase 0** — use the GSD artifacts (`ARCHITECTURE.md`, `STRUCTURE.md`,
|
|
2674
|
+
`CONVENTIONS.md`, `INTEGRATIONS.md`, `STACK.md`) as the fingerprint source.
|
|
2675
|
+
Parse those files to build the fingerprint instead of crawling.
|
|
2676
|
+
- Spec file is derived from the orchestrator's layout and domain analysis artifacts.
|
|
2677
|
+
- Output file paths are written to the orchestrator's `artifacts/` inventory.
|
|
2678
|
+
|
|
2679
|
+
---
|
|
2680
|
+
|
|
2681
|
+
## Example Spec File
|
|
2682
|
+
|
|
2683
|
+
```markdown
|
|
2684
|
+
# Feature Spec: Transaction History
|
|
2685
|
+
|
|
2686
|
+
## Screen Name
|
|
2687
|
+
TransactionHistory
|
|
2688
|
+
|
|
2689
|
+
## API URL
|
|
2690
|
+
/api/v1/portfolio/transactions
|
|
2691
|
+
|
|
2692
|
+
## HTTP Method
|
|
2693
|
+
POST
|
|
2694
|
+
|
|
2695
|
+
## Request Body
|
|
2696
|
+
```json
|
|
2697
|
+
{
|
|
2698
|
+
"ClientCode": 12345,
|
|
2699
|
+
"SessionID": "abc-session-xyz",
|
|
2700
|
+
"FamilyFlag": 0
|
|
2701
|
+
}
|
|
2702
|
+
```
|
|
2703
|
+
|
|
2704
|
+
## Response Shape
|
|
2705
|
+
```json
|
|
2706
|
+
{
|
|
2707
|
+
"Status": "success",
|
|
2708
|
+
"Data": [
|
|
2709
|
+
{
|
|
2710
|
+
"TransactionID": "TXN001",
|
|
2711
|
+
"FundName": "SBI Bluechip Fund",
|
|
2712
|
+
"Amount": 5000.00,
|
|
2713
|
+
"TransactionDate": "2024-01-15",
|
|
2714
|
+
"TransactionType": "Purchase",
|
|
2715
|
+
"Units": 12.345,
|
|
2716
|
+
"NAV": 405.23
|
|
2717
|
+
}
|
|
2718
|
+
]
|
|
2719
|
+
}
|
|
2720
|
+
```
|
|
2721
|
+
|
|
2722
|
+
## Business Logic
|
|
2723
|
+
- Only show transactions where Amount > 0
|
|
2724
|
+
- Transactions older than 7 years should be marked as "Archived"
|
|
2725
|
+
- If Status is not "success", treat the response as an error
|
|
2726
|
+
|
|
2727
|
+
## Screen Logic
|
|
2728
|
+
- Fetch transaction history on screen mount
|
|
2729
|
+
- Support pull-to-refresh
|
|
2730
|
+
- Navigate to TransactionDetail screen when user taps a transaction row
|
|
2731
|
+
- Show empty state message "No transactions found" when list is empty
|
|
2732
|
+
|
|
2733
|
+
## Response Mapping
|
|
2734
|
+
- `Data[].TransactionID` → unique key for each list row (not visible to user)
|
|
2735
|
+
- `Data[].FundName` → primary title of each row
|
|
2736
|
+
- `Data[].Amount` → below FundName, formatted as Indian currency (₹)
|
|
2737
|
+
- `Data[].TransactionDate` → subtitle, formatted as "15 Jan 2024"
|
|
2738
|
+
- `Data[].TransactionType` → badge: green for Purchase, red for Redemption
|
|
2739
|
+
- `Data[].Units` → detail row: "Units: 12.345"
|
|
2740
|
+
- `Data[].NAV` → detail row: "NAV: ₹405.23"
|
|
2741
|
+
```
|