@mediacubeco/expo-template-fsd 2.3.1 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/git-commit.md +164 -0
- package/.claude/rules/architecture.md +38 -0
- package/.claude/rules/{prettier-eslint-typescript.mdc → prettier-eslint-typescript.md} +5 -10
- package/.claude/settings.json +2 -2
- package/.claude/skills/api/SKILL.md +362 -0
- package/.claude/skills/api/references/client/overview.md +106 -0
- package/.claude/skills/api/references/hooks/index.ts +3 -0
- package/.claude/skills/api/references/hooks/use-api-health.ts +25 -0
- package/.claude/skills/api/references/hooks/use-api-post.ts +46 -0
- package/.claude/skills/api/references/hooks/use-api-posts.ts +24 -0
- package/.claude/skills/api/references/mock/post.mock.ts +29 -0
- package/.claude/skills/{generate-api-model/references → api/references/model}/entities/common.ts +5 -2
- package/.claude/skills/api/references/model/resources/common.ts +9 -0
- package/.claude/skills/api/references/queries/health.ts +14 -0
- package/.claude/skills/api/references/queries/index.ts +2 -0
- package/.claude/skills/api/references/queries/post.ts +23 -0
- package/.claude/skills/api/references/repository/health/health.ts +11 -0
- package/.claude/skills/api/references/repository/health/health.types.ts +25 -0
- package/.claude/skills/api/references/repository/health/index.ts +2 -0
- package/.claude/skills/api/references/repository/post/index.ts +9 -0
- package/.claude/skills/api/references/repository/post/post.ts +34 -0
- package/.claude/skills/api/references/repository/post/post.types.ts +61 -0
- package/.claude/skills/code-style/SKILL.md +1025 -1
- package/.claude/skills/code-style/references/components/payout-speed-option/index.ts +2 -0
- package/.claude/skills/code-style/references/components/payout-speed-option/payout-speed-option.styles.ts +76 -0
- package/.claude/skills/code-style/references/components/payout-speed-option/payout-speed-option.tsx +58 -0
- package/.claude/skills/code-style/references/components/payout-speed-option/payout-speed-option.types.ts +22 -0
- package/.claude/skills/code-style/references/components/post/index.ts +2 -0
- package/.claude/skills/code-style/references/components/post/post.styles.ts +19 -0
- package/.claude/skills/code-style/references/components/post/post.tsx +21 -0
- package/.claude/skills/code-style/references/components/post/post.types.ts +9 -0
- package/.claude/skills/code-style/references/components/wrap/index.ts +2 -0
- package/.claude/skills/code-style/references/components/wrap/wrap.styles.ts +15 -0
- package/.claude/skills/code-style/references/components/wrap/wrap.tsx +31 -0
- package/.claude/skills/code-style/references/components/wrap/wrap.types.ts +10 -0
- package/.claude/skills/code-style/references/hooks/use-posts.ts +37 -0
- package/.claude/skills/code-style/references/pages/post/index.ts +4 -0
- package/.claude/skills/code-style/references/pages/post/post-details/index.ts +1 -0
- package/.claude/skills/code-style/references/pages/post/post-details/post-details.tsx +13 -0
- package/.claude/skills/code-style/references/pages/post/post-guide/index.ts +1 -0
- package/.claude/skills/code-style/references/pages/post/post-guide/post-guide.tsx +17 -0
- package/.claude/skills/code-style/references/pages/post/post-list/index.ts +1 -0
- package/.claude/skills/code-style/references/pages/post/post-list/post-list.styles.ts +17 -0
- package/.claude/skills/code-style/references/pages/post/post-list/post-list.tsx +25 -0
- package/.claude/skills/code-style/references/pages/post/post-share/index.ts +1 -0
- package/.claude/skills/code-style/references/pages/post/post-share/post-share.tsx +13 -0
- package/.claude/skills/code-style/references/routes/(stack)/_layout.tsx +18 -0
- package/.claude/skills/code-style/references/routes/(stack)/details.tsx +8 -0
- package/.claude/skills/code-style/references/routes/(stack)/guide.tsx +8 -0
- package/.claude/skills/code-style/references/routes/(stack)/index.tsx +8 -0
- package/.claude/skills/code-style/references/routes/(stack)/share.tsx +8 -0
- package/.claude/skills/code-style/references/routes/(tabs)/(post-details)/index.tsx +8 -0
- package/.claude/skills/code-style/references/routes/(tabs)/(post-list)/index.tsx +8 -0
- package/.claude/skills/code-style/references/routes/(tabs)/_layout.tsx +50 -0
- package/.claude/skills/code-style/references/routes/_app.tsx +20 -0
- package/.claude/skills/code-style/references/routes/guide.tsx +8 -0
- package/.claude/skills/code-style/references/routes/share.tsx +8 -0
- package/.claude/skills/code-style/references/store/use-post-draft-store.ts +62 -0
- package/.claude/skills/design-style/SKILL.md +320 -0
- package/.env +3 -1
- package/.env.example +3 -1
- package/CLAUDE.md +5 -11
- package/app.json +6 -3
- package/axios.d.ts +5 -0
- package/env.d.ts +3 -1
- package/package.json +33 -40
- package/patches/react-native-screens+4.25.2.patch +118 -0
- package/react-query.d.ts +13 -0
- package/src/app/(root)/(home)/_layout.tsx +2 -2
- package/src/app/(root)/(home)/index.tsx +2 -2
- package/src/app/(root)/(settings)/_layout.tsx +2 -2
- package/src/app/(root)/(settings)/index.tsx +2 -2
- package/src/app/(root)/_layout.tsx +35 -32
- package/src/pages/home/home-main.tsx +3 -7
- package/src/pages/settings/settings-main.tsx +3 -7
- package/src/shared/assets/fonts/index.ts +3 -3
- package/src/shared/assets/fonts/sf-pro-text-bold.ttf +0 -0
- package/src/shared/assets/fonts/sf-pro-text-regular.ttf +0 -0
- package/src/shared/assets/fonts/sf-pro-text-semibold.ttf +0 -0
- package/src/shared/assets/icons/back.tsx +21 -0
- package/src/shared/assets/icons/close.tsx +21 -0
- package/src/shared/assets/icons/common.ts +8 -0
- package/src/shared/assets/icons/default.tsx +44 -0
- package/src/shared/assets/icons/index.ts +13 -1
- package/src/shared/assets/icons/refresh.tsx +23 -0
- package/src/shared/assets/icons/share.tsx +38 -0
- package/src/shared/assets/icons/type.ts +7 -0
- package/src/shared/assets/translations/index.ts +4 -4
- package/src/shared/constants/config.ts +13 -0
- package/src/shared/constants/environment.ts +16 -4
- package/src/shared/constants/index.ts +1 -0
- package/src/shared/hooks/index.ts +1 -4
- package/src/shared/hooks/use-compose.ts +23 -0
- package/src/shared/lib/api/api.client.ts +157 -0
- package/src/shared/lib/api/api.provider.tsx +14 -36
- package/src/shared/lib/api/api.ts +1 -5
- package/src/shared/lib/api/api.types.ts +6 -3
- package/src/shared/lib/api/hooks/index.ts +1 -0
- package/src/shared/lib/api/index.ts +2 -3
- package/src/shared/lib/api/models/api-instance/api-instance.client.ts +50 -0
- package/src/shared/lib/api/models/api-instance/api-instance.ts +30 -82
- package/src/shared/lib/api/models/api-instance/api-instance.types.ts +16 -18
- package/src/shared/lib/api/models/api-instance/api-instance.utils.ts +13 -20
- package/src/shared/lib/api/models/api-instance/index.ts +3 -0
- package/src/shared/lib/api/queries/index.ts +1 -0
- package/src/shared/lib/api/repositories/index.ts +1 -1
- package/src/shared/lib/localization/constants/index.ts +16 -5
- package/src/shared/lib/localization/index.ts +5 -1
- package/src/shared/lib/navigation/hooks/index.ts +8 -3
- package/src/shared/{hooks/use-breakpoints.ts → lib/navigation/hooks/use-navigation-breakpoints.ts} +2 -5
- package/src/shared/lib/navigation/hooks/use-navigation-dimensions.ts +12 -0
- package/src/shared/lib/navigation/hooks/use-navigation-options.tsx +175 -0
- package/src/shared/lib/navigation/hooks/{use-navigation.ts → use-navigation-route.ts} +2 -2
- package/src/shared/lib/navigation/hooks/use-navigation-safe-area.ts +61 -0
- package/src/shared/lib/navigation/hooks/use-navigation-tabs-native.ts +10 -0
- package/src/shared/lib/navigation/hooks/use-navigation-tabs.tsx +10 -0
- package/src/shared/lib/navigation/hooks/use-navigation-transition.ts +27 -0
- package/src/shared/lib/navigation/index.ts +11 -4
- package/src/shared/lib/navigation/navigation-tabs-native.context.tsx +5 -0
- package/src/shared/lib/navigation/navigation-tabs-native.provider.tsx +17 -0
- package/src/shared/lib/navigation/navigation-tabs.context.tsx +10 -0
- package/src/shared/lib/navigation/navigation-tabs.provider.tsx +22 -0
- package/src/shared/lib/navigation/navigation.provider.tsx +6 -1
- package/src/shared/lib/storage/index.ts +1 -1
- package/src/shared/lib/storage/storage.provider.tsx +4 -0
- package/src/shared/lib/storage/storage.ts +29 -25
- package/src/shared/lib/theme/constants/animations.ts +26 -0
- package/src/shared/lib/theme/constants/colors.ts +336 -43
- package/src/shared/lib/theme/constants/delays.ts +1 -1
- package/src/shared/lib/theme/constants/durations.ts +1 -1
- package/src/shared/lib/theme/constants/fonts.ts +2 -1
- package/src/shared/lib/theme/constants/index.ts +42 -20
- package/src/shared/lib/theme/constants/shadows.ts +7 -25
- package/src/shared/lib/theme/constants/sizes.ts +11 -2
- package/src/shared/lib/theme/constants/transparencies.ts +1 -1
- package/src/shared/lib/theme/hooks/use-theme-setup.ts +3 -1
- package/src/shared/lib/theme/index.ts +10 -1
- package/src/shared/lib/theme/theme.provider.tsx +45 -19
- package/src/shared/lib/theme/theme.scope.tsx +43 -0
- package/src/shared/lib/theme/theme.types.ts +15 -3
- package/src/shared/lib/theme/utils/color.ts +81 -0
- package/src/shared/lib/theme/utils/index.ts +2 -0
- package/src/shared/lib/theme/utils/snapshot.ts +17 -0
- package/src/shared/lib/theme/utils/storage.ts +5 -2
- package/src/shared/services/browser/browser.ts +66 -0
- package/src/shared/services/browser/index.ts +1 -0
- package/src/shared/services/cookies/cookies.ts +73 -0
- package/src/shared/services/cookies/index.ts +1 -0
- package/src/shared/services/index.ts +5 -0
- package/src/shared/services/linking/index.ts +1 -0
- package/src/shared/services/linking/linking.ts +22 -0
- package/src/shared/services/share/index.ts +1 -0
- package/src/shared/services/share/share.ts +28 -0
- package/src/shared/services/universal-links/index.ts +104 -0
- package/src/shared/services/universal-links/universal-links.ts +25 -0
- package/src/shared/services/universal-links/universal-links.types.ts +5 -0
- package/src/shared/ui/atoms/blur/blur.tsx +9 -4
- package/src/shared/ui/atoms/blur/blur.types.ts +1 -1
- package/src/shared/ui/atoms/blur-progress/blur-progress.styles.ts +16 -0
- package/src/shared/ui/atoms/blur-progress/blur-progress.tsx +30 -0
- package/src/shared/ui/atoms/blur-progress/blur-progress.types.tsx +5 -0
- package/src/shared/ui/atoms/blur-progress/index.ts +1 -0
- package/src/shared/ui/atoms/glass/glass.tsx +13 -5
- package/src/shared/ui/atoms/glass/glass.types.ts +1 -1
- package/src/shared/ui/atoms/icon/icon.styles.ts +82 -0
- package/src/shared/ui/atoms/icon/icon.tsx +21 -0
- package/src/shared/ui/atoms/icon/icon.types.ts +23 -0
- package/src/shared/ui/atoms/icon/index.ts +2 -0
- package/src/shared/ui/atoms/index.ts +7 -0
- package/src/shared/ui/atoms/skeleton/index.ts +2 -0
- package/src/shared/ui/atoms/skeleton/skeleton.styles.ts +69 -0
- package/src/shared/ui/atoms/skeleton/skeleton.tsx +26 -0
- package/src/shared/ui/atoms/skeleton/skeleton.types.ts +9 -0
- package/src/shared/ui/atoms/spinner/index.ts +2 -0
- package/src/shared/ui/atoms/spinner/spinner.styles.ts +102 -0
- package/src/shared/ui/atoms/spinner/spinner.tsx +48 -0
- package/src/shared/ui/atoms/spinner/spinner.types.ts +25 -0
- package/src/shared/ui/atoms/text/index.ts +2 -0
- package/src/shared/ui/atoms/text/text.styles.ts +186 -0
- package/src/shared/ui/atoms/text/text.tsx +37 -0
- package/src/shared/ui/atoms/text/text.types.ts +35 -0
- package/src/shared/ui/atoms/touch/components/haptics/haptics.styles.ts +21 -0
- package/src/shared/ui/atoms/touch/components/haptics/haptics.tsx +28 -0
- package/src/shared/ui/atoms/touch/components/haptics/haptics.types.ts +17 -0
- package/src/shared/ui/atoms/touch/components/haptics/index.ts +2 -0
- package/src/shared/ui/atoms/touch/components/index.ts +3 -0
- package/src/shared/ui/atoms/touch/components/ripple/index.ts +2 -0
- package/src/shared/ui/atoms/touch/components/ripple/ripple.styles.ts +78 -0
- package/src/shared/ui/atoms/touch/components/ripple/ripple.tsx +29 -0
- package/src/shared/ui/atoms/touch/components/ripple/ripple.types.ts +17 -0
- package/src/shared/ui/atoms/touch/components/spring/index.ts +2 -0
- package/src/shared/ui/atoms/touch/components/spring/spring.styles.ts +31 -0
- package/src/shared/ui/atoms/touch/components/spring/spring.tsx +28 -0
- package/src/shared/ui/atoms/touch/components/spring/spring.types.ts +17 -0
- package/src/shared/ui/atoms/touch/index.ts +2 -0
- package/src/shared/ui/atoms/touch/touch.styles.ts +14 -0
- package/src/shared/ui/atoms/touch/touch.tsx +137 -0
- package/src/shared/ui/atoms/touch/touch.types.ts +27 -0
- package/src/shared/ui/atoms/visibility/index.ts +2 -0
- package/src/shared/ui/atoms/visibility/visibility.styles.ts +85 -0
- package/src/shared/ui/atoms/visibility/visibility.tsx +61 -0
- package/src/shared/ui/atoms/visibility/visibility.types.ts +34 -0
- package/src/shared/ui/molecules/keyboard-area/keyboard-area.styles.ts +11 -9
- package/src/shared/ui/molecules/keyboard-area/keyboard-area.tsx +3 -6
- package/src/shared/ui/molecules/refresh-control/refresh-control.tsx +3 -5
- package/src/shared/ui/molecules/refresh-control/refresh-control.types.ts +1 -1
- package/src/shared/ui/molecules/slider/components/indicator/components/dot/dot.styles.ts +41 -0
- package/src/shared/ui/molecules/slider/components/indicator/components/dot/dot.tsx +12 -0
- package/src/shared/ui/molecules/slider/components/indicator/components/dot/dot.types.ts +13 -0
- package/src/shared/ui/molecules/slider/components/indicator/components/dot/index.ts +2 -0
- package/src/shared/ui/molecules/slider/components/indicator/index.ts +2 -0
- package/src/shared/ui/molecules/slider/components/indicator/indicator.styles.ts +20 -0
- package/src/shared/ui/molecules/slider/components/indicator/indicator.tsx +22 -0
- package/src/shared/ui/molecules/slider/components/indicator/indicator.types.ts +10 -0
- package/src/shared/ui/molecules/slider/index.ts +3 -0
- package/src/shared/ui/molecules/slider/slider.styles.ts +68 -0
- package/src/shared/ui/molecules/slider/slider.tsx +64 -0
- package/src/shared/ui/molecules/slider/slider.types.ts +28 -0
- package/src/shared/ui/organisms/bar/bar.styles.ts +100 -0
- package/src/shared/ui/organisms/bar/bar.tsx +89 -0
- package/src/shared/ui/organisms/bar/bar.types.ts +49 -0
- package/src/shared/ui/organisms/bar/index.ts +2 -0
- package/src/shared/ui/organisms/bar-tab/bar-tab.tsx +9 -0
- package/src/shared/ui/organisms/bar-tab/bar-tab.types.ts +5 -0
- package/src/shared/ui/organisms/bar-tab/components/index.ts +1 -0
- package/src/shared/ui/organisms/bar-tab/components/tab/index.ts +2 -0
- package/src/shared/ui/organisms/bar-tab/components/tab/tab.styles.ts +21 -0
- package/src/shared/ui/organisms/bar-tab/components/tab/tab.tsx +18 -0
- package/src/shared/ui/organisms/bar-tab/components/tab/tab.types.ts +5 -0
- package/src/shared/ui/organisms/bar-tab/index.ts +2 -0
- package/src/shared/ui/organisms/bar-tab-navigation/bar-tab-navigation.styles.ts +22 -0
- package/src/shared/ui/organisms/bar-tab-navigation/bar-tab-navigation.tsx +66 -0
- package/src/shared/ui/organisms/bar-tab-navigation/bar-tab-navigation.types.ts +5 -0
- package/src/shared/ui/organisms/bar-tab-navigation/components/index.ts +1 -0
- package/src/shared/ui/organisms/bar-tab-navigation/components/tab-navigation/index.ts +2 -0
- package/src/shared/ui/organisms/bar-tab-navigation/components/tab-navigation/tab-navigation.styles.ts +56 -0
- package/src/shared/ui/organisms/bar-tab-navigation/components/tab-navigation/tab-navigation.tsx +30 -0
- package/src/shared/ui/organisms/bar-tab-navigation/components/tab-navigation/tab-navigation.types.ts +7 -0
- package/src/shared/ui/organisms/bar-tab-navigation/index.ts +2 -0
- package/src/shared/ui/organisms/index.ts +4 -1
- package/src/shared/ui/organisms/slider/components/indicator/components/dot/dot.styles.ts +41 -0
- package/src/shared/ui/organisms/slider/components/indicator/components/dot/dot.tsx +12 -0
- package/src/shared/ui/organisms/slider/components/indicator/components/dot/dot.types.ts +13 -0
- package/src/shared/ui/organisms/slider/components/indicator/components/dot/index.ts +2 -0
- package/src/shared/ui/organisms/slider/components/indicator/index.ts +2 -0
- package/src/shared/ui/organisms/slider/components/indicator/indicator.styles.ts +20 -0
- package/src/shared/ui/organisms/slider/components/indicator/indicator.tsx +22 -0
- package/src/shared/ui/organisms/slider/components/indicator/indicator.types.ts +10 -0
- package/src/shared/ui/organisms/slider/index.ts +3 -0
- package/src/shared/ui/organisms/slider/slider.styles.ts +68 -0
- package/src/shared/ui/organisms/slider/slider.tsx +64 -0
- package/src/shared/ui/organisms/slider/slider.types.ts +28 -0
- package/src/shared/ui/templates/index.ts +3 -0
- package/src/shared/ui/templates/modal-container/index.ts +13 -0
- package/src/shared/ui/templates/modal-container/modal-container-list.tsx +45 -0
- package/src/shared/ui/templates/modal-container/modal-container-scroll.tsx +66 -0
- package/src/shared/ui/templates/modal-container/modal-container-section-list.tsx +46 -0
- package/src/shared/ui/templates/modal-container/modal-container.hooks.tsx +172 -0
- package/src/shared/ui/templates/modal-container/modal-container.styles.ts +121 -0
- package/src/shared/ui/templates/modal-container/modal-container.types.ts +69 -0
- package/src/shared/ui/templates/screen-container/index.ts +1 -1
- package/src/shared/ui/templates/screen-container/screen-container-list.tsx +28 -10
- package/src/shared/ui/templates/screen-container/screen-container-scroll.tsx +28 -10
- package/src/shared/ui/templates/screen-container/screen-container-section-list.tsx +29 -11
- package/src/shared/ui/templates/screen-container/screen-container.hooks.tsx +79 -8
- package/src/shared/ui/templates/screen-container/screen-container.styles.ts +105 -15
- package/src/shared/ui/templates/screen-container/screen-container.types.tsx +34 -7
- package/src/shared/ui/templates/screen-header/components/index.ts +6 -0
- package/src/shared/ui/templates/screen-header/components/screen-header-description/index.ts +2 -0
- package/src/shared/ui/templates/screen-header/components/screen-header-description/screen-header-description.tsx +15 -0
- package/src/shared/ui/templates/screen-header/components/screen-header-description/screen-header-description.types.ts +11 -0
- package/src/shared/ui/templates/screen-header/components/screen-header-icon/index.ts +2 -0
- package/src/shared/ui/templates/screen-header/components/screen-header-icon/screen-header-icon.styles.ts +44 -0
- package/src/shared/ui/templates/screen-header/components/screen-header-icon/screen-header-icon.tsx +65 -0
- package/src/shared/ui/templates/screen-header/components/screen-header-icon/screen-header-icon.types.ts +14 -0
- package/src/shared/ui/templates/screen-header/components/screen-header-icon-back/index.ts +2 -0
- package/src/shared/ui/templates/screen-header/components/screen-header-icon-back/screen-header-icon-back.tsx +17 -0
- package/src/shared/ui/templates/screen-header/components/screen-header-icon-back/screen-header-icon-back.types.ts +6 -0
- package/src/shared/ui/templates/screen-header/components/screen-header-icon-close/index.ts +2 -0
- package/src/shared/ui/templates/screen-header/components/screen-header-icon-close/screen-header-icon-close.tsx +27 -0
- package/src/shared/ui/templates/screen-header/components/screen-header-icon-close/screen-header-icon-close.types.ts +10 -0
- package/src/shared/ui/templates/screen-header/components/screen-header-info/index.ts +2 -0
- package/src/shared/ui/templates/screen-header/components/screen-header-info/screen-header-info.tsx +15 -0
- package/src/shared/ui/templates/screen-header/components/screen-header-info/screen-header-info.types.ts +10 -0
- package/src/shared/ui/templates/screen-header/components/screen-header-title/index.ts +2 -0
- package/src/shared/ui/templates/screen-header/components/screen-header-title/screen-header-title.tsx +19 -0
- package/src/shared/ui/templates/screen-header/components/screen-header-title/screen-header-title.types.ts +13 -0
- package/src/shared/ui/templates/screen-header/index.ts +3 -0
- package/src/shared/ui/templates/screen-header/screen-header.styles.ts +49 -0
- package/src/shared/ui/templates/screen-header/screen-header.tsx +26 -0
- package/src/shared/ui/templates/screen-header/screen-header.types.ts +15 -0
- package/src/shared/ui/templates/web-container/index.ts +2 -0
- package/src/shared/ui/templates/web-container/web-container.styles.ts +34 -0
- package/src/shared/ui/templates/web-container/web-container.tsx +172 -0
- package/src/shared/ui/templates/web-container/web-container.types.ts +21 -0
- package/.claude/rules/app-routing.mdc +0 -47
- package/.claude/rules/exports-at-end.mdc +0 -55
- package/.claude/rules/kebab-case-naming.mdc +0 -29
- package/.claude/rules/mediacubeco-packages.mdc +0 -56
- package/.claude/skills/generate-api-model/SKILL.md +0 -230
- package/.claude/skills/generate-api-model/references/resources/common.ts +0 -8
- package/.claude/skills/generate-api-model-mock/SKILL.md +0 -56
- package/.claude/skills/git-commit/SKILL.md +0 -139
- package/.gitignore +0 -34
- package/src/shared/assets/icons/like.tsx +0 -17
- package/src/shared/hooks/use-animated.ts +0 -200
- package/src/shared/hooks/use-mixins.ts +0 -36
- package/src/shared/hooks/use-safe-area.ts +0 -75
- package/src/shared/lib/api/repositories/auth/auth.ts +0 -23
- package/src/shared/lib/api/repositories/auth/auth.types.ts +0 -22
- package/src/shared/lib/api/repositories/auth/index.ts +0 -1
- package/src/shared/lib/navigation/hooks/use-navigation-native-tabs.ts +0 -10
- package/src/shared/lib/navigation/hooks/use-navigation-options.ts +0 -123
- package/src/shared/lib/navigation/navigation-native-tabs.context.tsx +0 -5
- package/src/shared/lib/navigation/navigation-native-tabs.provider.tsx +0 -12
- package/src/shared/lib/theme/constants/borders.ts +0 -8
- package/src/shared/lib/theme/constants/weights.ts +0 -13
- /package/.claude/skills/{generate-api-model/references → api/references/model}/entities/index.ts +0 -0
- /package/.claude/skills/{generate-api-model/references → api/references/model}/entities/post/index.ts +0 -0
- /package/.claude/skills/{generate-api-model/references → api/references/model}/entities/post/post-comment.ts +0 -0
- /package/.claude/skills/{generate-api-model/references → api/references/model}/entities/post/post.ts +0 -0
- /package/.claude/skills/{generate-api-model/references → api/references/model}/entities/user/index.ts +0 -0
- /package/.claude/skills/{generate-api-model/references → api/references/model}/entities/user/user-settings.ts +0 -0
- /package/.claude/skills/{generate-api-model/references → api/references/model}/entities/user/user-verification.ts +0 -0
- /package/.claude/skills/{generate-api-model/references → api/references/model}/entities/user/user.ts +0 -0
- /package/.claude/skills/{generate-api-model/references → api/references/model}/resources/index.ts +0 -0
- /package/.claude/skills/{generate-api-model/references → api/references/model}/resources/post/index.ts +0 -0
- /package/.claude/skills/{generate-api-model/references → api/references/model}/resources/post/post-comment.ts +0 -0
- /package/.claude/skills/{generate-api-model/references → api/references/model}/resources/post/post.ts +0 -0
- /package/.claude/skills/{generate-api-model/references → api/references/model}/resources/user/index.ts +0 -0
- /package/.claude/skills/{generate-api-model/references → api/references/model}/resources/user/user-settings.ts +0 -0
- /package/.claude/skills/{generate-api-model/references → api/references/model}/resources/user/user-verification.ts +0 -0
- /package/.claude/skills/{generate-api-model/references → api/references/model}/resources/user/user.ts +0 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { type IterableOf, type NotNullable } from '@mediacubeco/base/types'
|
|
2
|
+
import { runCallback } from '@mediacubeco/base/utils'
|
|
3
|
+
import { useCallback, useState } from 'react'
|
|
4
|
+
import { View } from 'react-native'
|
|
5
|
+
import { WebView, type WebViewNavigation, type WebViewProps } from 'react-native-webview'
|
|
6
|
+
import { useComposeRef } from '../../../hooks'
|
|
7
|
+
import { Share } from '../../../services'
|
|
8
|
+
import { Spinner } from '../../atoms'
|
|
9
|
+
import { ScreenContainerScroll } from '../screen-container'
|
|
10
|
+
import {
|
|
11
|
+
ScreenHeader,
|
|
12
|
+
ScreenHeaderIcon,
|
|
13
|
+
ScreenHeaderIconClose,
|
|
14
|
+
ScreenHeaderInfo,
|
|
15
|
+
} from '../screen-header'
|
|
16
|
+
import { useWebContainerStyles } from './web-container.styles'
|
|
17
|
+
import { type WebContainerProps } from './web-container.types'
|
|
18
|
+
|
|
19
|
+
const iconList = ['share' as const, 'refresh' as const]
|
|
20
|
+
const originWhitelist = ['*']
|
|
21
|
+
const injectedJavaScript =
|
|
22
|
+
'window.ReactNativeWebView.postMessage(JSON.stringify(window.location.hostname))'
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Корневой контейнер WebView для модального окна (formSheet на всю высоту). Рисует шапку
|
|
26
|
+
* с заголовком текущей страницы и кнопкой закрытия, следит за жизненным циклом загрузки:
|
|
27
|
+
* пока страница грузится — оверлей со спиннером, при провале навигации — оверлей ошибки.
|
|
28
|
+
* Все нативные пропсы `WebView` пробрасываются насквозь; свои колбэки жизненного цикла
|
|
29
|
+
* контейнер оборачивает и вызывает пользовательские после своих.
|
|
30
|
+
*/
|
|
31
|
+
const WebContainer = ({
|
|
32
|
+
ref: initRef,
|
|
33
|
+
uri,
|
|
34
|
+
method,
|
|
35
|
+
headers,
|
|
36
|
+
body,
|
|
37
|
+
title: initTitle = '',
|
|
38
|
+
onChange,
|
|
39
|
+
}: WebContainerProps) => {
|
|
40
|
+
const hasURI = !!uri
|
|
41
|
+
|
|
42
|
+
const [title, setTitle] = useState(initTitle)
|
|
43
|
+
const [description, setDescription] = useState('')
|
|
44
|
+
const [currentUrl, setCurrentUrl] = useState(uri ?? '')
|
|
45
|
+
const [_errorCode, setErrorCode] = useState('')
|
|
46
|
+
const [_errorDescription, setErrorDescription] = useState('')
|
|
47
|
+
const [didFail, setDidFail] = useState(false)
|
|
48
|
+
const [isPending, setIsPending] = useState(true)
|
|
49
|
+
const ref = useComposeRef<WebView>(initRef)
|
|
50
|
+
const { styles } = useWebContainerStyles()
|
|
51
|
+
|
|
52
|
+
const shouldPlaceholder = !hasURI || didFail
|
|
53
|
+
const shouldSpinner = hasURI && isPending
|
|
54
|
+
|
|
55
|
+
const handleReload = useCallback(() => {
|
|
56
|
+
runCallback(ref.current?.reload)
|
|
57
|
+
}, [ref])
|
|
58
|
+
|
|
59
|
+
const handleHeaderIconPress = useCallback(
|
|
60
|
+
async (value: IterableOf<typeof iconList>) => {
|
|
61
|
+
if (value === 'refresh') {
|
|
62
|
+
handleReload()
|
|
63
|
+
} else if (value === 'share' && currentUrl) {
|
|
64
|
+
await Share.openUrl(currentUrl)
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
[currentUrl, handleReload]
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
const handleLoadStart: NotNullable<WebViewProps['onLoadStart']> = useCallback(() => {
|
|
71
|
+
setDidFail(false)
|
|
72
|
+
}, [])
|
|
73
|
+
|
|
74
|
+
const handleLoadEnd: NotNullable<WebViewProps['onLoadEnd']> = useCallback(() => {
|
|
75
|
+
setIsPending(false)
|
|
76
|
+
}, [])
|
|
77
|
+
|
|
78
|
+
const handleError: NotNullable<WebViewProps['onError']> = useCallback((event) => {
|
|
79
|
+
const { code, description } = event.nativeEvent
|
|
80
|
+
|
|
81
|
+
setErrorCode(String(code))
|
|
82
|
+
setErrorDescription(description)
|
|
83
|
+
|
|
84
|
+
setDidFail(true)
|
|
85
|
+
setIsPending(false)
|
|
86
|
+
}, [])
|
|
87
|
+
|
|
88
|
+
const handleHttpError: NotNullable<WebViewProps['onHttpError']> = useCallback((event) => {
|
|
89
|
+
const { statusCode, description } = event.nativeEvent
|
|
90
|
+
|
|
91
|
+
setErrorCode(String(statusCode))
|
|
92
|
+
setErrorDescription(description)
|
|
93
|
+
|
|
94
|
+
setDidFail(true)
|
|
95
|
+
setIsPending(false)
|
|
96
|
+
}, [])
|
|
97
|
+
|
|
98
|
+
const handleNavigationStateChange = useCallback(
|
|
99
|
+
(event: WebViewNavigation) => {
|
|
100
|
+
const info = event.title.split('|')
|
|
101
|
+
|
|
102
|
+
const title = info[0]?.trim()
|
|
103
|
+
const description = info[1]?.trim()
|
|
104
|
+
|
|
105
|
+
if (title) {
|
|
106
|
+
setTitle(title)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (description) {
|
|
110
|
+
setDescription(description)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (event.url) {
|
|
114
|
+
setCurrentUrl(event.url)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
runCallback(onChange, event)
|
|
118
|
+
},
|
|
119
|
+
[onChange]
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
<ScreenContainerScroll
|
|
124
|
+
variant={'secondary'}
|
|
125
|
+
wrapperStyle={styles.wrapper}
|
|
126
|
+
outerStyle={styles.outer}
|
|
127
|
+
innerStyle={styles.inner}
|
|
128
|
+
withScroll={isPending || didFail}
|
|
129
|
+
withAreaTop={false}
|
|
130
|
+
withAreaBottom={false}
|
|
131
|
+
withIndentBottom={false}
|
|
132
|
+
screenHeader={
|
|
133
|
+
<ScreenHeader
|
|
134
|
+
left={<ScreenHeaderIconClose />}
|
|
135
|
+
center={<ScreenHeaderInfo title={title} description={description} />}
|
|
136
|
+
right={<ScreenHeaderIcon value={iconList} onPress={handleHeaderIconPress} />}
|
|
137
|
+
withArea={false}
|
|
138
|
+
/>
|
|
139
|
+
}
|
|
140
|
+
>
|
|
141
|
+
{hasURI && (
|
|
142
|
+
<WebView
|
|
143
|
+
ref={ref}
|
|
144
|
+
style={styles.web}
|
|
145
|
+
source={{ uri, method, headers, body }}
|
|
146
|
+
androidLayerType={'hardware'}
|
|
147
|
+
originWhitelist={originWhitelist}
|
|
148
|
+
injectedJavaScript={injectedJavaScript}
|
|
149
|
+
pullToRefreshEnabled={!isPending}
|
|
150
|
+
scalesPageToFit={false}
|
|
151
|
+
sharedCookiesEnabled={true}
|
|
152
|
+
allowsInlineMediaPlayback={true}
|
|
153
|
+
showsVerticalScrollIndicator={false}
|
|
154
|
+
mediaPlaybackRequiresUserAction={false}
|
|
155
|
+
onLoadStart={handleLoadStart}
|
|
156
|
+
onLoadEnd={handleLoadEnd}
|
|
157
|
+
onError={handleError}
|
|
158
|
+
onHttpError={handleHttpError}
|
|
159
|
+
onNavigationStateChange={handleNavigationStateChange}
|
|
160
|
+
/>
|
|
161
|
+
)}
|
|
162
|
+
{shouldPlaceholder && <></>}
|
|
163
|
+
{shouldSpinner && (
|
|
164
|
+
<View style={styles.spinner}>
|
|
165
|
+
<Spinner />
|
|
166
|
+
</View>
|
|
167
|
+
)}
|
|
168
|
+
</ScreenContainerScroll>
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export default WebContainer
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type RefObject } from 'react'
|
|
2
|
+
import { type WebView, type WebViewProps } from 'react-native-webview'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Пропсы корневого контейнера WebView: обёртка над нативными пропсами `WebView`
|
|
6
|
+
* с шапкой (заголовок текущей страницы и закрытие), индикатором загрузки и
|
|
7
|
+
* состоянием ошибки.
|
|
8
|
+
*/
|
|
9
|
+
type WebContainerProps = {
|
|
10
|
+
ref?: RefObject<WebView>
|
|
11
|
+
uri?: string
|
|
12
|
+
method?: string
|
|
13
|
+
headers?: object
|
|
14
|
+
body?: string
|
|
15
|
+
/** Заголовок шапки, пока страница не сообщила собственный title. */
|
|
16
|
+
title?: string
|
|
17
|
+
/** Закрытие модального окна кнопкой в шапке. */
|
|
18
|
+
onChange?: WebViewProps['onNavigationStateChange']
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type { WebContainerProps }
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Thin Expo Router layer in src/app — layout/screen and delegation to ~pages
|
|
3
|
-
globs: src/app/**/*.tsx
|
|
4
|
-
alwaysApply: false
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# `src/app` layer (Expo Router)
|
|
8
|
-
|
|
9
|
-
Files under `src/app` only wire routes to implementation. Screen UI and business logic live in `~pages/*` (or other features), not in `app`.
|
|
10
|
-
|
|
11
|
-
## `_layout.tsx`
|
|
12
|
-
|
|
13
|
-
- The route’s main component is a function named **`layout`** (`Stack` / `Tabs` / `NativeTabs` config, `Stack.Screen`, options).
|
|
14
|
-
- **Default**-export that function (Expo Router requirement). Follow the “exports at end” policy in `.cursor/rules/exports-at-end.mdc`: declare `function layout()` first, then `export default layout` at the bottom of the file.
|
|
15
|
-
- Do not turn `_layout` into a “screen”: no content markup, only navigation and options.
|
|
16
|
-
|
|
17
|
-
## Route screen files (`index.tsx`, `[param].tsx`, named routes, etc.)
|
|
18
|
-
|
|
19
|
-
- The main component is a function named **`screen`**; it only renders the root screen from `~pages/...` (import a single root screen component).
|
|
20
|
-
- **Default**-export it (`export default screen` at the end of the file, same export rules).
|
|
21
|
-
- Do not duplicate screen markup in the route file — only a thin wrapper around the `~pages` import.
|
|
22
|
-
|
|
23
|
-
## Exceptions
|
|
24
|
-
|
|
25
|
-
- Special Expo Router files (`+not-found.tsx`, `+html.tsx`, etc.), if you add them, may follow a different contract — use Expo’s docs for that file.
|
|
26
|
-
|
|
27
|
-
## Templates
|
|
28
|
-
|
|
29
|
-
```tsx
|
|
30
|
-
// _layout.tsx — navigation only
|
|
31
|
-
function Layout() {
|
|
32
|
-
return <Stack>{/* Stack.Screen … */}</Stack>
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export default Layout
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
```tsx
|
|
39
|
-
// route screen
|
|
40
|
-
import { SomeFeatureScreen } from '~pages/some-feature'
|
|
41
|
-
|
|
42
|
-
function Screen() {
|
|
43
|
-
return <SomeFeatureScreen />
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export default Screen
|
|
47
|
-
```
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Place all export statements at the end of TS/TSX modules
|
|
3
|
-
globs: **/*.{ts,tsx}
|
|
4
|
-
alwaysApply: false
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Exports at end of file
|
|
8
|
-
|
|
9
|
-
All public exports from a module must appear **at the end of the file**, after imports, types, and implementation.
|
|
10
|
-
|
|
11
|
-
## Rules
|
|
12
|
-
|
|
13
|
-
- Do not scatter `export` on declarations through the file (e.g. `export const`, `export function`, `export class`, `export interface`). Declare first, then export in one place at the bottom.
|
|
14
|
-
- Use explicit export statements: `export { … }`, `export type { … }`, and `export default …`.
|
|
15
|
-
- Barrel files (`export … from '…'`) should also keep exports in the final section of the file when combined with other code (prefer dedicated barrel files with only re-exports).
|
|
16
|
-
|
|
17
|
-
## Suggested order (bottom of file)
|
|
18
|
-
|
|
19
|
-
1. `export { … }` — runtime named exports
|
|
20
|
-
2. `export type { … }` — type-only exports
|
|
21
|
-
3. `export default …` — default export last
|
|
22
|
-
|
|
23
|
-
Adjust if the team prefers default first; stay consistent within the repo.
|
|
24
|
-
|
|
25
|
-
## Examples
|
|
26
|
-
|
|
27
|
-
```typescript
|
|
28
|
-
// ❌ BAD — exports mixed with implementation
|
|
29
|
-
export const someVar = 1
|
|
30
|
-
export type { SomeType }
|
|
31
|
-
export function someFunction() {}
|
|
32
|
-
|
|
33
|
-
// ✅ GOOD — definitions first, exports last
|
|
34
|
-
const someVar = 1
|
|
35
|
-
type SomeType = { x: number }
|
|
36
|
-
function someFunction() {}
|
|
37
|
-
|
|
38
|
-
export { someVar, someFunction }
|
|
39
|
-
export type { SomeType }
|
|
40
|
-
export default someFunction
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
```typescript
|
|
44
|
-
// ❌ BAD
|
|
45
|
-
export default function SomeComponent() {
|
|
46
|
-
return null
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// ✅ GOOD
|
|
50
|
-
function SomeComponent() {
|
|
51
|
-
return null
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export default SomeComponent
|
|
55
|
-
```
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Enforce kebab-case naming for all files and folders
|
|
3
|
-
alwaysApply: true
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Kebab-case naming convention
|
|
7
|
-
|
|
8
|
-
All files and folders must use kebab-case naming.
|
|
9
|
-
|
|
10
|
-
## Rules
|
|
11
|
-
|
|
12
|
-
- Use only lowercase letters, digits, and hyphens: `some-name.ts`
|
|
13
|
-
- No camelCase, PascalCase, or snake_case in file/folder names
|
|
14
|
-
- Dots are allowed only for file extensions and config modifiers: `eslint.config.js`, `app.json`
|
|
15
|
-
|
|
16
|
-
## Examples
|
|
17
|
-
|
|
18
|
-
```
|
|
19
|
-
# ✅ GOOD
|
|
20
|
-
src/shared/ui/
|
|
21
|
-
some-entity
|
|
22
|
-
some-feature
|
|
23
|
-
some-widget
|
|
24
|
-
|
|
25
|
-
# ❌ BAD
|
|
26
|
-
SomeEntity
|
|
27
|
-
some_feature
|
|
28
|
-
someWidget
|
|
29
|
-
```
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Enforce use of @mediacubeco/* corporate packages over custom re-implementations
|
|
3
|
-
alwaysApply: true
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Use @mediacubeco/\* corporate packages
|
|
7
|
-
|
|
8
|
-
All utilities, helpers, hooks, and configs that exist in `@mediacubeco/*` packages **must** be imported from there. Do not duplicate or re-implement what is already provided.
|
|
9
|
-
|
|
10
|
-
## Available packages
|
|
11
|
-
|
|
12
|
-
To discover what each package exports, read its DTS index:
|
|
13
|
-
|
|
14
|
-
- `@mediacubeco/base` — `dist/index.d.ts`
|
|
15
|
-
- subpaths: `/helpers`, `/services`, `/types`, `/utils`
|
|
16
|
-
- `@mediacubeco/base-react-native` — `dist/index.d.ts`
|
|
17
|
-
- subpaths: `/helpers`, `/hooks`, `/utils`
|
|
18
|
-
|
|
19
|
-
## Rules
|
|
20
|
-
|
|
21
|
-
- Before writing any utility, helper, hook, or type — read the relevant package's DTS index and check if it already exists.
|
|
22
|
-
- Always import via subpath, never from the package root:
|
|
23
|
-
- `@mediacubeco/base/utils` ✅ — `@mediacubeco/base` ❌
|
|
24
|
-
- `@mediacubeco/base-react-native/hooks` ✅ — `@mediacubeco/base-react-native` ❌
|
|
25
|
-
- Never copy source from a `@mediacubeco/*` package into the app.
|
|
26
|
-
- ESLint, Prettier, and TypeScript configs must extend the corresponding `@mediacubeco/*` config package — do not inline equivalent rules.
|
|
27
|
-
- If something is missing from a `@mediacubeco/*` package, add it there first, then import it in the app.
|
|
28
|
-
|
|
29
|
-
## Examples
|
|
30
|
-
|
|
31
|
-
```typescript
|
|
32
|
-
// ✅ GOOD — subpath import
|
|
33
|
-
import { isNull } from '@mediacubeco/base/utils'
|
|
34
|
-
|
|
35
|
-
// ❌ BAD — inline
|
|
36
|
-
value === null
|
|
37
|
-
|
|
38
|
-
// ❌ BAD — root import
|
|
39
|
-
import { isNull } from '@mediacubeco/base'
|
|
40
|
-
|
|
41
|
-
// ❌ BAD — reimplementing what already exists
|
|
42
|
-
function isNull(value: unknown): value is null {
|
|
43
|
-
return value === null
|
|
44
|
-
}
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
```typescript
|
|
48
|
-
// ✅ GOOD — subpath import
|
|
49
|
-
import { useKeyboard } from '@mediacubeco/base-react-native/hooks'
|
|
50
|
-
|
|
51
|
-
// ❌ BAD — root import
|
|
52
|
-
import { useKeyboard } from '@mediacubeco/base-react-native'
|
|
53
|
-
|
|
54
|
-
// ❌ BAD — custom re-implementation
|
|
55
|
-
function useKeyboard() { /* ... */ }
|
|
56
|
-
```
|
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: generate-api-model
|
|
3
|
-
description: >-
|
|
4
|
-
Generates API Resource types and Entity classes from a backend JSON response.
|
|
5
|
-
Use this skill whenever the user provides a backend JSON object or API schema
|
|
6
|
-
and wants TypeScript types generated from it, asks to "add a new API model",
|
|
7
|
-
"create an entity for...", "generate resource and entity", or wants to model
|
|
8
|
-
an API response. Also use when adding new domain types to the shared API
|
|
9
|
-
library (resources/ + entities/ folders).
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
# Generate API Model
|
|
13
|
-
|
|
14
|
-
## What this skill does
|
|
15
|
-
|
|
16
|
-
Take a **backend JSON response** (or API contract) and produce two TypeScript artifacts per domain object:
|
|
17
|
-
|
|
18
|
-
1. **Resource** (`*Resource`) — a `type` that mirrors the **exact wire format** of the backend: field names are copied verbatim, including any snake_case, camelCase, kebab-case, UPPER_SNAKE_CASE, or PascalCase the backend uses.
|
|
19
|
-
2. **Entity** (`*Entity`) — a `class` that maps every field to strict **camelCase** and applies project naming conventions for dates, arrays, and booleans.
|
|
20
|
-
|
|
21
|
-
## Filesystem layout
|
|
22
|
-
|
|
23
|
-
```
|
|
24
|
-
<api-lib>/
|
|
25
|
-
├── resources/
|
|
26
|
-
│ ├── common.ts ← BaseResource shared type (see references/)
|
|
27
|
-
│ ├── index.ts ← barrel re-exporting all domains
|
|
28
|
-
│ └── <domain>/
|
|
29
|
-
│ ├── index.ts ← barrel for this domain
|
|
30
|
-
│ └── <domain>.ts ← Resource type(s) for this domain
|
|
31
|
-
└── entities/
|
|
32
|
-
├── common.ts ← BaseEntity<T> base class (see references/)
|
|
33
|
-
├── index.ts ← barrel re-exporting all domains
|
|
34
|
-
└── <domain>/
|
|
35
|
-
├── index.ts ← barrel for this domain
|
|
36
|
-
└── <domain>.ts ← Entity class for this domain
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
Domain folder names are **kebab-case**. See `references/` for complete working examples.
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
## Step 1 — Identify domains
|
|
44
|
-
|
|
45
|
-
From the backend JSON, identify independent domain objects. Exclude generic HTTP envelopes (pagination wrappers, status/error shapes) unless the user explicitly requests them.
|
|
46
|
-
|
|
47
|
-
**If the user did not specify a domain name for the root object — ask before generating anything.** Never invent a domain name on your own. The root domain name determines all type names, filenames, and folder names, so it must be confirmed by the user.
|
|
48
|
-
|
|
49
|
-
Every distinct nested object that carries its own data becomes its own `*Resource` + `*Entity` pair.
|
|
50
|
-
|
|
51
|
-
**Where to place nested domain files:** if a nested object only appears inside one parent domain, put its files **in the parent's folder** (e.g., `brand/brand-translation.ts`, `post/post-comment.ts`, `user/user-settings.ts`). Create a separate top-level folder only when the object appears across multiple domains or is clearly a standalone concept (e.g., `stored-file` that could be attached to many entities).
|
|
52
|
-
|
|
53
|
-
---
|
|
54
|
-
|
|
55
|
-
## Step 2 — Resource types (wire format)
|
|
56
|
-
|
|
57
|
-
**Rule: Resource field names must exactly match the backend.** Copy them verbatim — do not normalize, rename, or change casing.
|
|
58
|
-
|
|
59
|
-
```typescript
|
|
60
|
-
// Backend sends: { "first_name": "John", "UserAge": 30, "is-active": true }
|
|
61
|
-
type UserResource = BaseResource & {
|
|
62
|
-
'first_name'?: string // snake_case preserved as-is
|
|
63
|
-
'UserAge'?: number // PascalCase preserved as-is
|
|
64
|
-
'is-active'?: boolean // hyphenated keys must be quoted
|
|
65
|
-
}
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
Additional rules:
|
|
69
|
-
|
|
70
|
-
- All fields are **optional** (`?`) by default, unless the contract guarantees the field is always present.
|
|
71
|
-
- **Do not add `id`, `createdAt` / `updatedAt`, or `isPending`** to domain Resource types — these are already declared in `BaseResource` and inherited automatically.
|
|
72
|
-
- Nested objects → their own `*Resource` type in a sibling file, referenced here.
|
|
73
|
-
- Arrays of objects → `ChildResource[]`.
|
|
74
|
-
- String enums → `export enum` in the same file as the resource that owns the field.
|
|
75
|
-
- Import `BaseResource` from `../common` (or the resources barrel).
|
|
76
|
-
|
|
77
|
-
---
|
|
78
|
-
|
|
79
|
-
## Step 3 — Entity classes (always camelCase)
|
|
80
|
-
|
|
81
|
-
Every property in the Entity must be **camelCase**, regardless of the wire-format key.
|
|
82
|
-
|
|
83
|
-
### Case conversion table
|
|
84
|
-
|
|
85
|
-
| Wire format | Wire key example | Entity field |
|
|
86
|
-
| ---------------- | ----------------- | --------------- |
|
|
87
|
-
| snake_case | `created_at` | `createdAt` |
|
|
88
|
-
| snake_case | `first_name` | `firstName` |
|
|
89
|
-
| snake_case | `number_of_likes` | `numberOfLikes` |
|
|
90
|
-
| kebab-case | `user-name` | `userName` |
|
|
91
|
-
| kebab-case | `post-id` | `postId` |
|
|
92
|
-
| UPPER_SNAKE_CASE | `USER_NAME` | `userName` |
|
|
93
|
-
| UPPER_SNAKE_CASE | `POST_ID` | `postId` |
|
|
94
|
-
| camelCase | `firstName` | `firstName` |
|
|
95
|
-
| PascalCase | `FirstName` | `firstName` |
|
|
96
|
-
|
|
97
|
-
**Algorithm:** split the wire key on `_`, `-`, spaces, or case-boundary transitions (uppercase after lowercase) → lowercase the first segment, capitalize the first letter of every subsequent segment → join.
|
|
98
|
-
|
|
99
|
-
### Entity naming conventions
|
|
100
|
-
|
|
101
|
-
After converting to camelCase, apply these semantic rules:
|
|
102
|
-
|
|
103
|
-
#### Dates → must end in `at`
|
|
104
|
-
|
|
105
|
-
Any field that represents a point in time must end with `at`.
|
|
106
|
-
|
|
107
|
-
```
|
|
108
|
-
created_at → createdAt ✓
|
|
109
|
-
updated_at → updatedAt ✓
|
|
110
|
-
publish_date → publishedAt ✓ (rename: "date" → "at")
|
|
111
|
-
expiry → expiredAt ✓ (rename: add "at" suffix)
|
|
112
|
-
birth_date → birthAt ✓
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
#### Arrays → must end in `list`
|
|
116
|
-
|
|
117
|
-
All array properties in Entity end in `list`. Strip a trailing plural `s` (when it makes the base form a clean word) before appending `List`.
|
|
118
|
-
|
|
119
|
-
Resource keeps the backend wire name; Entity renames to `*List`.
|
|
120
|
-
|
|
121
|
-
```
|
|
122
|
-
// Resource field (wire name) Entity field (camelCase + list)
|
|
123
|
-
comments: PostCommentResource[] → commentList: PostCommentEntity[]
|
|
124
|
-
tags: string[] → tagList: string[]
|
|
125
|
-
user_posts: PostResource[] → userPostList: PostEntity[]
|
|
126
|
-
items: ItemResource[] → itemList: ItemEntity[]
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
If the backend already uses a `list`-style name (e.g., `comment_list`), the Entity field is simply the camelCase conversion: `commentList`.
|
|
130
|
-
|
|
131
|
-
#### Booleans → should start with `is`, `has`, `did`, `can`, `should`, `was`, `will`
|
|
132
|
-
|
|
133
|
-
```
|
|
134
|
-
is_email_verified → isEmailVerified ✓
|
|
135
|
-
verified → isVerified ✓ (add prefix when the boolean nature is clear)
|
|
136
|
-
email_confirmed → didConfirmEmail ✓
|
|
137
|
-
can_edit → canEdit ✓
|
|
138
|
-
has_access → hasAccess ✓
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### Entity constructor pattern
|
|
142
|
-
|
|
143
|
-
**Do not re-declare or set `id`, `createdAt`, `updatedAt` in domain entities** — `BaseEntity` already handles them via `super(resource, context)`. Only add fields that are specific to this domain.
|
|
144
|
-
|
|
145
|
-
See `references/entities/common.ts` for the full `BaseEntity<T>` implementation and `references/entities/post/post.ts` for a complete domain example (with nested entity, array `→ List`, and cross-domain reference).
|
|
146
|
-
|
|
147
|
-
Default values by type:
|
|
148
|
-
| Type | Default |
|
|
149
|
-
|------------------|-----------------------|
|
|
150
|
-
| `string` | `''` |
|
|
151
|
-
| `number` | `0` |
|
|
152
|
-
| `boolean` | `false` |
|
|
153
|
-
| `Date` | `new Date()` |
|
|
154
|
-
| nested Entity | `new ChildEntity(resource?.field, this)` |
|
|
155
|
-
| array | `[]` |
|
|
156
|
-
|
|
157
|
-
---
|
|
158
|
-
|
|
159
|
-
## Step 4 — Barrel index files
|
|
160
|
-
|
|
161
|
-
Each `<domain>/` folder must have an `index.ts` re-exporting its modules. After adding a new domain, update the parent barrels too.
|
|
162
|
-
|
|
163
|
-
See `references/resources/post/index.ts`, `references/entities/post/index.ts`, and `references/resources/index.ts` for examples.
|
|
164
|
-
|
|
165
|
-
---
|
|
166
|
-
|
|
167
|
-
## Import conventions
|
|
168
|
-
|
|
169
|
-
```typescript
|
|
170
|
-
// Inside a Resource file:
|
|
171
|
-
import { BaseResource } from '../common'
|
|
172
|
-
import { ChildResource } from './child-domain'
|
|
173
|
-
|
|
174
|
-
// Inside an Entity file:
|
|
175
|
-
import { BaseResource, DomainResource } from '../../resources'
|
|
176
|
-
import { BaseEntity } from '../common'
|
|
177
|
-
import { ChildEntity } from '../child-domain'
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
Dependency direction is **entities → resources only**. Resources never import from entities.
|
|
181
|
-
|
|
182
|
-
---
|
|
183
|
-
|
|
184
|
-
## Export style
|
|
185
|
-
|
|
186
|
-
```typescript
|
|
187
|
-
// Resource files:
|
|
188
|
-
export type { DomainResource }
|
|
189
|
-
export { DomainStatusEnum } // enums use a regular (value) export
|
|
190
|
-
|
|
191
|
-
// Entity files:
|
|
192
|
-
export { DomainEntity }
|
|
193
|
-
|
|
194
|
-
// Index files:
|
|
195
|
-
export * from './domain'
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
---
|
|
199
|
-
|
|
200
|
-
## Checklist before finishing
|
|
201
|
-
|
|
202
|
-
- [ ] Every Resource field name matches the backend wire format exactly (no renaming)
|
|
203
|
-
- [ ] Every Entity property is camelCase
|
|
204
|
-
- [ ] All date fields in Entity end in `at`
|
|
205
|
-
- [ ] All array fields in Entity end in `list`
|
|
206
|
-
- [ ] All boolean fields in Entity start with `is` / `has` / `did` / `can` / `should` / `was` / `will`
|
|
207
|
-
- [ ] Nested objects have their own `*Resource` + `*Entity` files
|
|
208
|
-
- [ ] Each domain folder has an `index.ts` barrel
|
|
209
|
-
- [ ] Parent `resources/index.ts` and `entities/index.ts` updated
|
|
210
|
-
|
|
211
|
-
---
|
|
212
|
-
|
|
213
|
-
## Reference examples
|
|
214
|
-
|
|
215
|
-
Read these files to understand the complete, working implementation:
|
|
216
|
-
|
|
217
|
-
| File | What it shows |
|
|
218
|
-
| -------------------------------------------- | ----------------------------------------------------------------------------- |
|
|
219
|
-
| `references/resources/common.ts` | `BaseResource` type — `id`, `created_at`, `updated_at`, `is_pending` |
|
|
220
|
-
| `references/entities/common.ts` | `BaseEntity<T>` — `generateIdFactory`, `isPending` getter, `super()` contract |
|
|
221
|
-
| `references/resources/post/post.ts` | Resource with snake_case fields, nested types, cross-domain ref |
|
|
222
|
-
| `references/entities/post/post.ts` | Entity with camelCase, `commentList`, `numberOfLikes`, nested `UserEntity` |
|
|
223
|
-
| `references/resources/post/post-comment.ts` | Minimal nested Resource inside a parent domain folder |
|
|
224
|
-
| `references/entities/post/post-comment.ts` | Minimal nested Entity inside a parent domain folder |
|
|
225
|
-
| `references/resources/user/user.ts` | Multi-file domain Resource with two nested sub-resources |
|
|
226
|
-
| `references/entities/user/user.ts` | Multi-file domain Entity with two nested sub-entities |
|
|
227
|
-
| `references/resources/user/user-settings.ts` | Leaf Resource (no further nesting) |
|
|
228
|
-
| `references/entities/user/user-settings.ts` | Leaf Entity (no further nesting) |
|
|
229
|
-
| `references/resources/post/index.ts` | Domain barrel — re-exports all files in the folder |
|
|
230
|
-
| `references/resources/index.ts` | Root barrel — re-exports all domains + common |
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: generate-api-model-mock
|
|
3
|
-
description: >-
|
|
4
|
-
Generate mock data (JSON fixtures and optional helpers) aligned with API
|
|
5
|
-
resource and entity types. Use when the user asks for mocks, mock data, test
|
|
6
|
-
fixtures, or fake API responses.
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# Generate API model mocks
|
|
10
|
-
|
|
11
|
-
## Purpose
|
|
12
|
-
|
|
13
|
-
Produce static data that **conforms** to the project’s `*Resource` types and, when useful, round-trips through `*Entity` classes the same way production code would.
|
|
14
|
-
|
|
15
|
-
## Before generating
|
|
16
|
-
|
|
17
|
-
If the user has not specified **where** fixtures live or **which response shape** (single object vs array vs envelope), ask once for:
|
|
18
|
-
|
|
19
|
-
- Target folder (or confirm the repo’s usual fixtures root), and
|
|
20
|
-
- Whether the mock is raw JSON, a TS module that exports objects, or both.
|
|
21
|
-
|
|
22
|
-
Do not invent a new root if the project already has a convention—search for existing `mock`, `fixtures`, or `__fixtures__` directories first.
|
|
23
|
-
|
|
24
|
-
## Alignment with the API layer
|
|
25
|
-
|
|
26
|
-
Follow the same **resources / entities** split as in `generate-api-model`:
|
|
27
|
-
|
|
28
|
-
1. Read the **`{Domain}Resource`** type from `<api-lib>/resources/<domain>/<domain>.ts` (and `resources/common.ts` if the DTO extends a shared fragment).
|
|
29
|
-
2. Read the **`{Domain}Entity`** class from `<api-lib>/entities/<domain>/<domain>.ts` and `entities/common.ts` if you need defaults or nesting behavior.
|
|
30
|
-
3. Field names in JSON must match the **resource** type (wire names), not entity getter names—unless the user explicitly wants a camelCase transport sample that still type-checks via mapping.
|
|
31
|
-
|
|
32
|
-
Generate **2–3** sample items unless the user asks otherwise. Values should be realistic and respect optional vs required fields implied by the types.
|
|
33
|
-
|
|
34
|
-
## Typical artifacts
|
|
35
|
-
|
|
36
|
-
### Fixture file (e.g. `resource.json` or `*.mock.json`)
|
|
37
|
-
|
|
38
|
-
Valid JSON array or object matching the agreed response shape; property names match `*Resource`.
|
|
39
|
-
|
|
40
|
-
### Optional helper module (e.g. `entity.ts` or `*.ts`)
|
|
41
|
-
|
|
42
|
-
- Import the resource type from the API barrel or the domain module.
|
|
43
|
-
- Import the fixture (JSON module or inline object) according to project bundler/tsconfig rules.
|
|
44
|
-
- Export functions such as `get{Domain}MockResource()` that return a **deep clone** (e.g. `JSON.parse(JSON.stringify(data))`) when callers must not mutate shared state.
|
|
45
|
-
- If tests should use entities, export helpers that return `new {Domain}Entity(resource)` from the same payload.
|
|
46
|
-
|
|
47
|
-
Annotate return types with the resource type or a dedicated mock alias (e.g. `{Domain}MockResource`) if the response is a subset or wrapper around the flat resource.
|
|
48
|
-
|
|
49
|
-
### Barrel
|
|
50
|
-
|
|
51
|
-
If the folder has `index.ts`, re-export the helper module. Update the nearest parent barrel only if that matches existing project practice.
|
|
52
|
-
|
|
53
|
-
## Constraints
|
|
54
|
-
|
|
55
|
-
- Do not drift from the **current** `*Resource` definition; if the contract changed, update the types first (see `generate-api-model`), then regenerate mocks.
|
|
56
|
-
- Keep skills **abstract**: no hard-coded app routes, copy-pasted production payloads, or long excerpts from `src/` in this file—describe steps and placeholders, not a snapshot of one endpoint.
|