@qlover/create-app 1.0.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (556) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/index.cjs +9 -9
  3. package/dist/index.js +9 -9
  4. package/package.json +4 -3
  5. package/dist/configs/_common/.editorconfig +0 -23
  6. package/dist/configs/_common/.env.template +0 -13
  7. package/dist/configs/_common/.gitattributes +0 -2
  8. package/dist/configs/_common/.github/workflows/general-check.yml +0 -41
  9. package/dist/configs/_common/.github/workflows/release.yml +0 -81
  10. package/dist/configs/_common/.gitignore.template +0 -64
  11. package/dist/configs/_common/.husky/commit-msg +0 -3
  12. package/dist/configs/_common/.husky/pre-commit +0 -3
  13. package/dist/configs/_common/.prettierignore +0 -17
  14. package/dist/configs/_common/.prettierrc.js +0 -7
  15. package/dist/configs/_common/.vscode/extensions.json +0 -9
  16. package/dist/configs/_common/.vscode/react.code-snippets +0 -19
  17. package/dist/configs/_common/.vscode/settings.json +0 -16
  18. package/dist/configs/_common/commitlint.config.js +0 -10
  19. package/dist/configs/_common/package.json.template +0 -75
  20. package/dist/configs/node-lib/eslint.config.js +0 -50
  21. package/dist/templates/next-app/.env.template +0 -25
  22. package/dist/templates/next-app/.prettierignore +0 -58
  23. package/dist/templates/next-app/README.en.md +0 -130
  24. package/dist/templates/next-app/README.md +0 -130
  25. package/dist/templates/next-app/config/IOCIdentifier.ts +0 -74
  26. package/dist/templates/next-app/config/Identifier/api.ts +0 -41
  27. package/dist/templates/next-app/config/Identifier/common/admint.table.ts +0 -69
  28. package/dist/templates/next-app/config/Identifier/common/common.ts +0 -90
  29. package/dist/templates/next-app/config/Identifier/common/index.ts +0 -3
  30. package/dist/templates/next-app/config/Identifier/common/validators.ts +0 -34
  31. package/dist/templates/next-app/config/Identifier/index.ts +0 -3
  32. package/dist/templates/next-app/config/Identifier/pages/index.ts +0 -7
  33. package/dist/templates/next-app/config/Identifier/pages/page.about.ts +0 -20
  34. package/dist/templates/next-app/config/Identifier/pages/page.admin.home.ts +0 -27
  35. package/dist/templates/next-app/config/Identifier/pages/page.admin.locales.ts +0 -266
  36. package/dist/templates/next-app/config/Identifier/pages/page.admin.user.ts +0 -293
  37. package/dist/templates/next-app/config/Identifier/pages/page.home.ts +0 -56
  38. package/dist/templates/next-app/config/Identifier/pages/page.login.ts +0 -159
  39. package/dist/templates/next-app/config/Identifier/pages/page.register.ts +0 -177
  40. package/dist/templates/next-app/config/adminNavs.ts +0 -19
  41. package/dist/templates/next-app/config/common.ts +0 -43
  42. package/dist/templates/next-app/config/cookies.ts +0 -23
  43. package/dist/templates/next-app/config/i18n/AboutI18n.ts +0 -14
  44. package/dist/templates/next-app/config/i18n/HomeI18n.ts +0 -24
  45. package/dist/templates/next-app/config/i18n/PageI18nInterface.ts +0 -51
  46. package/dist/templates/next-app/config/i18n/admin18n.ts +0 -75
  47. package/dist/templates/next-app/config/i18n/i18nConfig.ts +0 -16
  48. package/dist/templates/next-app/config/i18n/i18nKeyScheam.ts +0 -36
  49. package/dist/templates/next-app/config/i18n/index.ts +0 -7
  50. package/dist/templates/next-app/config/i18n/loginI18n.ts +0 -50
  51. package/dist/templates/next-app/config/i18n/register18n.ts +0 -44
  52. package/dist/templates/next-app/config/route.ts +0 -9
  53. package/dist/templates/next-app/config/theme.ts +0 -28
  54. package/dist/templates/next-app/docs/en/api.md +0 -387
  55. package/dist/templates/next-app/docs/en/component.md +0 -544
  56. package/dist/templates/next-app/docs/en/database.md +0 -496
  57. package/dist/templates/next-app/docs/en/development-guide.md +0 -727
  58. package/dist/templates/next-app/docs/en/env.md +0 -563
  59. package/dist/templates/next-app/docs/en/i18n.md +0 -287
  60. package/dist/templates/next-app/docs/en/index.md +0 -165
  61. package/dist/templates/next-app/docs/en/page.md +0 -457
  62. package/dist/templates/next-app/docs/en/project-structure.md +0 -176
  63. package/dist/templates/next-app/docs/en/router.md +0 -427
  64. package/dist/templates/next-app/docs/en/theme.md +0 -532
  65. package/dist/templates/next-app/docs/en/validator.md +0 -478
  66. package/dist/templates/next-app/docs/zh/api.md +0 -387
  67. package/dist/templates/next-app/docs/zh/component.md +0 -544
  68. package/dist/templates/next-app/docs/zh/database.md +0 -496
  69. package/dist/templates/next-app/docs/zh/development-guide.md +0 -727
  70. package/dist/templates/next-app/docs/zh/env.md +0 -563
  71. package/dist/templates/next-app/docs/zh/i18n.md +0 -287
  72. package/dist/templates/next-app/docs/zh/index.md +0 -165
  73. package/dist/templates/next-app/docs/zh/page.md +0 -457
  74. package/dist/templates/next-app/docs/zh/project-structure.md +0 -176
  75. package/dist/templates/next-app/docs/zh/router.md +0 -427
  76. package/dist/templates/next-app/docs/zh/theme.md +0 -532
  77. package/dist/templates/next-app/docs/zh/validator.md +0 -476
  78. package/dist/templates/next-app/eslint.config.mjs +0 -285
  79. package/dist/templates/next-app/make/generateLocales.ts +0 -32
  80. package/dist/templates/next-app/migrations/schema/LocalesSchema.ts +0 -15
  81. package/dist/templates/next-app/migrations/schema/UserSchema.ts +0 -38
  82. package/dist/templates/next-app/migrations/sql/1694244000000.sql +0 -21
  83. package/dist/templates/next-app/next.config.ts +0 -25
  84. package/dist/templates/next-app/package.json +0 -87
  85. package/dist/templates/next-app/postcss.config.mjs +0 -5
  86. package/dist/templates/next-app/public/favicon.ico +0 -0
  87. package/dist/templates/next-app/public/file.svg +0 -1
  88. package/dist/templates/next-app/public/globe.svg +0 -1
  89. package/dist/templates/next-app/public/locales/en.json +0 -182
  90. package/dist/templates/next-app/public/locales/zh.json +0 -182
  91. package/dist/templates/next-app/public/next.svg +0 -1
  92. package/dist/templates/next-app/public/vercel.svg +0 -1
  93. package/dist/templates/next-app/public/window.svg +0 -1
  94. package/dist/templates/next-app/src/app/[locale]/admin/AdminI18nProvider.tsx +0 -37
  95. package/dist/templates/next-app/src/app/[locale]/admin/layout.tsx +0 -42
  96. package/dist/templates/next-app/src/app/[locale]/admin/locales/page.tsx +0 -153
  97. package/dist/templates/next-app/src/app/[locale]/admin/page.tsx +0 -20
  98. package/dist/templates/next-app/src/app/[locale]/admin/users/page.tsx +0 -67
  99. package/dist/templates/next-app/src/app/[locale]/auth/layout.tsx +0 -18
  100. package/dist/templates/next-app/src/app/[locale]/auth/login/LoginForm.tsx +0 -126
  101. package/dist/templates/next-app/src/app/[locale]/auth/login/page.tsx +0 -90
  102. package/dist/templates/next-app/src/app/[locale]/auth/page.tsx +0 -8
  103. package/dist/templates/next-app/src/app/[locale]/auth/register/RegisterForm.tsx +0 -197
  104. package/dist/templates/next-app/src/app/[locale]/auth/register/page.tsx +0 -90
  105. package/dist/templates/next-app/src/app/[locale]/layout.tsx +0 -63
  106. package/dist/templates/next-app/src/app/[locale]/not-found.tsx +0 -24
  107. package/dist/templates/next-app/src/app/[locale]/page.tsx +0 -98
  108. package/dist/templates/next-app/src/app/api/admin/locales/create/route.ts +0 -13
  109. package/dist/templates/next-app/src/app/api/admin/locales/import/route.ts +0 -13
  110. package/dist/templates/next-app/src/app/api/admin/locales/route.ts +0 -20
  111. package/dist/templates/next-app/src/app/api/admin/locales/update/route.ts +0 -13
  112. package/dist/templates/next-app/src/app/api/admin/users/route.ts +0 -20
  113. package/dist/templates/next-app/src/app/api/ai/completions/route.ts +0 -32
  114. package/dist/templates/next-app/src/app/api/auth/callback/route.ts +0 -11
  115. package/dist/templates/next-app/src/app/api/callback/route.ts +0 -49
  116. package/dist/templates/next-app/src/app/api/locales/json/route.ts +0 -33
  117. package/dist/templates/next-app/src/app/api/user/login/route.ts +0 -10
  118. package/dist/templates/next-app/src/app/api/user/logout/route.ts +0 -8
  119. package/dist/templates/next-app/src/app/api/user/register/route.ts +0 -11
  120. package/dist/templates/next-app/src/app/manifest.ts +0 -16
  121. package/dist/templates/next-app/src/app/robots.txt +0 -2
  122. package/dist/templates/next-app/src/base/cases/AdminPageManager.ts +0 -28
  123. package/dist/templates/next-app/src/base/cases/AppConfig.ts +0 -40
  124. package/dist/templates/next-app/src/base/cases/Datetime.ts +0 -18
  125. package/dist/templates/next-app/src/base/cases/DialogErrorPlugin.ts +0 -57
  126. package/dist/templates/next-app/src/base/cases/DialogHandler.ts +0 -116
  127. package/dist/templates/next-app/src/base/cases/InversifyContainer.ts +0 -45
  128. package/dist/templates/next-app/src/base/cases/NavigateBridge.ts +0 -32
  129. package/dist/templates/next-app/src/base/cases/RequestEncryptPlugin.ts +0 -77
  130. package/dist/templates/next-app/src/base/cases/ResourceState.ts +0 -17
  131. package/dist/templates/next-app/src/base/cases/RouterService.ts +0 -52
  132. package/dist/templates/next-app/src/base/cases/StringEncryptor.ts +0 -73
  133. package/dist/templates/next-app/src/base/cases/TranslateI18nUtil.ts +0 -53
  134. package/dist/templates/next-app/src/base/cases/ZodColumnBuilder.ts +0 -212
  135. package/dist/templates/next-app/src/base/port/AdminLayoutInterface.ts +0 -26
  136. package/dist/templates/next-app/src/base/port/AppApiInterface.ts +0 -36
  137. package/dist/templates/next-app/src/base/port/AppUserApiInterface.ts +0 -27
  138. package/dist/templates/next-app/src/base/port/I18nServiceInterface.ts +0 -25
  139. package/dist/templates/next-app/src/base/port/IOCInterface.ts +0 -33
  140. package/dist/templates/next-app/src/base/port/RouterInterface.ts +0 -11
  141. package/dist/templates/next-app/src/base/port/UserServiceInterface.ts +0 -25
  142. package/dist/templates/next-app/src/base/port/ZodBuilderInterface.ts +0 -8
  143. package/dist/templates/next-app/src/base/services/AdminPageEvent.ts +0 -26
  144. package/dist/templates/next-app/src/base/services/AdminPageScheduler.ts +0 -42
  145. package/dist/templates/next-app/src/base/services/AppApiRequester.ts +0 -67
  146. package/dist/templates/next-app/src/base/services/AppUserGateway.ts +0 -110
  147. package/dist/templates/next-app/src/base/services/I18nService.ts +0 -87
  148. package/dist/templates/next-app/src/base/services/ResourceService.ts +0 -139
  149. package/dist/templates/next-app/src/base/services/UserService.ts +0 -68
  150. package/dist/templates/next-app/src/base/services/adminApi/AdminLocalesApi.ts +0 -106
  151. package/dist/templates/next-app/src/base/services/adminApi/AdminUserApi.ts +0 -87
  152. package/dist/templates/next-app/src/base/services/appApi/AppApiPlugin.ts +0 -110
  153. package/dist/templates/next-app/src/base/services/appApi/AppUserApiBootstrap.ts +0 -52
  154. package/dist/templates/next-app/src/base/types/AppPageRouter.ts +0 -12
  155. package/dist/templates/next-app/src/base/types/PagesRouter.ts +0 -9
  156. package/dist/templates/next-app/src/core/bootstraps/BootstrapClient.ts +0 -76
  157. package/dist/templates/next-app/src/core/bootstraps/BootstrapServer.ts +0 -125
  158. package/dist/templates/next-app/src/core/bootstraps/BootstrapsRegistry.ts +0 -50
  159. package/dist/templates/next-app/src/core/bootstraps/IocIdentifierTest.ts +0 -26
  160. package/dist/templates/next-app/src/core/bootstraps/PrintBootstrap.ts +0 -18
  161. package/dist/templates/next-app/src/core/clientIoc/ClientIOC.ts +0 -68
  162. package/dist/templates/next-app/src/core/clientIoc/ClientIOCRegister.ts +0 -100
  163. package/dist/templates/next-app/src/core/globals.ts +0 -28
  164. package/dist/templates/next-app/src/core/serverIoc/ServerIOC.ts +0 -80
  165. package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +0 -66
  166. package/dist/templates/next-app/src/i18n/loadMessages.ts +0 -103
  167. package/dist/templates/next-app/src/i18n/request.ts +0 -31
  168. package/dist/templates/next-app/src/i18n/routing.ts +0 -35
  169. package/dist/templates/next-app/src/lib/supabase/client.ts +0 -8
  170. package/dist/templates/next-app/src/lib/supabase/conts.ts +0 -2
  171. package/dist/templates/next-app/src/lib/supabase/proxy.ts +0 -84
  172. package/dist/templates/next-app/src/lib/supabase/server.ts +0 -38
  173. package/dist/templates/next-app/src/pages/[locale]/about.tsx +0 -61
  174. package/dist/templates/next-app/src/pages/_app.tsx +0 -50
  175. package/dist/templates/next-app/src/pages/_document.tsx +0 -13
  176. package/dist/templates/next-app/src/proxy.ts +0 -33
  177. package/dist/templates/next-app/src/server/AppErrorApi.ts +0 -10
  178. package/dist/templates/next-app/src/server/AppPageRouteParams.ts +0 -110
  179. package/dist/templates/next-app/src/server/AppSuccessApi.ts +0 -7
  180. package/dist/templates/next-app/src/server/NextApiServer.ts +0 -61
  181. package/dist/templates/next-app/src/server/PagesRouteParams.ts +0 -145
  182. package/dist/templates/next-app/src/server/PasswordEncrypt.ts +0 -18
  183. package/dist/templates/next-app/src/server/ServerAuth.ts +0 -81
  184. package/dist/templates/next-app/src/server/SupabaseBridge.ts +0 -262
  185. package/dist/templates/next-app/src/server/UserCredentialToken.ts +0 -53
  186. package/dist/templates/next-app/src/server/controllers/AdminLocalesController.ts +0 -86
  187. package/dist/templates/next-app/src/server/controllers/AdminUserController.ts +0 -42
  188. package/dist/templates/next-app/src/server/controllers/LocalesController.ts +0 -36
  189. package/dist/templates/next-app/src/server/controllers/UserController.ts +0 -91
  190. package/dist/templates/next-app/src/server/port/AIControllerInterface.ts +0 -8
  191. package/dist/templates/next-app/src/server/port/AdminLocalesControllerInterface.ts +0 -21
  192. package/dist/templates/next-app/src/server/port/AdminUserControllerInterface.ts +0 -11
  193. package/dist/templates/next-app/src/server/port/CrentialTokenInterface.ts +0 -5
  194. package/dist/templates/next-app/src/server/port/DBBridgeInterface.ts +0 -36
  195. package/dist/templates/next-app/src/server/port/DBTableInterface.ts +0 -12
  196. package/dist/templates/next-app/src/server/port/LocalesControllerInterface.ts +0 -10
  197. package/dist/templates/next-app/src/server/port/LocalesRepositoryInterface.ts +0 -43
  198. package/dist/templates/next-app/src/server/port/PaginationInterface.ts +0 -6
  199. package/dist/templates/next-app/src/server/port/RouteParamsnHandlerInterface.ts +0 -18
  200. package/dist/templates/next-app/src/server/port/ServerAuthInterface.ts +0 -15
  201. package/dist/templates/next-app/src/server/port/ServerInterface.ts +0 -23
  202. package/dist/templates/next-app/src/server/port/UserRepositoryInterface.ts +0 -15
  203. package/dist/templates/next-app/src/server/port/UserServiceInterface.ts +0 -14
  204. package/dist/templates/next-app/src/server/port/ValidatorInterface.ts +0 -23
  205. package/dist/templates/next-app/src/server/repositorys/LocalesRepository.ts +0 -216
  206. package/dist/templates/next-app/src/server/repositorys/UserRepository.ts +0 -102
  207. package/dist/templates/next-app/src/server/services/AIService.ts +0 -45
  208. package/dist/templates/next-app/src/server/services/AdminAuthPlugin.ts +0 -21
  209. package/dist/templates/next-app/src/server/services/AdminLocalesService.ts +0 -20
  210. package/dist/templates/next-app/src/server/services/ApiLocaleService.ts +0 -137
  211. package/dist/templates/next-app/src/server/services/ApiUserService.ts +0 -29
  212. package/dist/templates/next-app/src/server/services/UserService.ts +0 -134
  213. package/dist/templates/next-app/src/server/validators/ExtendedExecutorError.ts +0 -6
  214. package/dist/templates/next-app/src/server/validators/LocalesValidator.ts +0 -145
  215. package/dist/templates/next-app/src/server/validators/LoginValidator.ts +0 -82
  216. package/dist/templates/next-app/src/server/validators/PaginationValidator.ts +0 -70
  217. package/dist/templates/next-app/src/server/validators/SignupVerifyValidator.ts +0 -68
  218. package/dist/templates/next-app/src/styles/css/antd-themes/_common/_default.css +0 -280
  219. package/dist/templates/next-app/src/styles/css/antd-themes/_common/dark.css +0 -233
  220. package/dist/templates/next-app/src/styles/css/antd-themes/_common/index.css +0 -3
  221. package/dist/templates/next-app/src/styles/css/antd-themes/_common/pink.css +0 -246
  222. package/dist/templates/next-app/src/styles/css/antd-themes/index.css +0 -4
  223. package/dist/templates/next-app/src/styles/css/antd-themes/menu/_default.css +0 -108
  224. package/dist/templates/next-app/src/styles/css/antd-themes/menu/dark.css +0 -67
  225. package/dist/templates/next-app/src/styles/css/antd-themes/menu/index.css +0 -3
  226. package/dist/templates/next-app/src/styles/css/antd-themes/menu/pink.css +0 -67
  227. package/dist/templates/next-app/src/styles/css/antd-themes/no-context.css +0 -34
  228. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/_default.css +0 -34
  229. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/dark.css +0 -31
  230. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/index.css +0 -3
  231. package/dist/templates/next-app/src/styles/css/antd-themes/pagination/pink.css +0 -36
  232. package/dist/templates/next-app/src/styles/css/antd-themes/table/_default.css +0 -44
  233. package/dist/templates/next-app/src/styles/css/antd-themes/table/dark.css +0 -43
  234. package/dist/templates/next-app/src/styles/css/antd-themes/table/index.css +0 -3
  235. package/dist/templates/next-app/src/styles/css/antd-themes/table/pink.css +0 -43
  236. package/dist/templates/next-app/src/styles/css/index.css +0 -6
  237. package/dist/templates/next-app/src/styles/css/page.css +0 -20
  238. package/dist/templates/next-app/src/styles/css/scrollbar.css +0 -34
  239. package/dist/templates/next-app/src/styles/css/tailwind.css +0 -5
  240. package/dist/templates/next-app/src/styles/css/themes/_default.css +0 -30
  241. package/dist/templates/next-app/src/styles/css/themes/dark.css +0 -30
  242. package/dist/templates/next-app/src/styles/css/themes/index.css +0 -3
  243. package/dist/templates/next-app/src/styles/css/themes/pink.css +0 -30
  244. package/dist/templates/next-app/src/styles/css/zIndex.css +0 -9
  245. package/dist/templates/next-app/src/uikit/components/AdminLayout.tsx +0 -150
  246. package/dist/templates/next-app/src/uikit/components/BootstrapsProvider.tsx +0 -39
  247. package/dist/templates/next-app/src/uikit/components/ClientRootProvider.tsx +0 -64
  248. package/dist/templates/next-app/src/uikit/components/ClientSeo.tsx +0 -36
  249. package/dist/templates/next-app/src/uikit/components/ClinetRenderProvider.tsx +0 -42
  250. package/dist/templates/next-app/src/uikit/components/EditableCell.tsx +0 -118
  251. package/dist/templates/next-app/src/uikit/components/FeatureItem.tsx +0 -13
  252. package/dist/templates/next-app/src/uikit/components/IOCProvider.tsx +0 -34
  253. package/dist/templates/next-app/src/uikit/components/LocaleLink.tsx +0 -50
  254. package/dist/templates/next-app/src/uikit/components/With.tsx +0 -17
  255. package/dist/templates/next-app/src/uikit/components/localesImportButton/LocalesImportButton.tsx +0 -62
  256. package/dist/templates/next-app/src/uikit/components/localesImportButton/LocalesImportEvent.ts +0 -28
  257. package/dist/templates/next-app/src/uikit/components/localesImportButton/import.module.css +0 -6
  258. package/dist/templates/next-app/src/uikit/components-app/AdminButton.tsx +0 -29
  259. package/dist/templates/next-app/src/uikit/components-app/AppBridge.tsx +0 -17
  260. package/dist/templates/next-app/src/uikit/components-app/AppRoutePage.tsx +0 -105
  261. package/dist/templates/next-app/src/uikit/components-app/AuthButton.tsx +0 -20
  262. package/dist/templates/next-app/src/uikit/components-app/LanguageSwitcher.tsx +0 -75
  263. package/dist/templates/next-app/src/uikit/components-app/LogoutButton.tsx +0 -45
  264. package/dist/templates/next-app/src/uikit/components-app/ThemeSwitcher.tsx +0 -144
  265. package/dist/templates/next-app/src/uikit/components-pages/LanguageSwitcher.tsx +0 -98
  266. package/dist/templates/next-app/src/uikit/components-pages/PagesRoutePage.tsx +0 -93
  267. package/dist/templates/next-app/src/uikit/context/IOCContext.ts +0 -25
  268. package/dist/templates/next-app/src/uikit/hook/useI18nInterface.ts +0 -22
  269. package/dist/templates/next-app/src/uikit/hook/useIOC.ts +0 -37
  270. package/dist/templates/next-app/src/uikit/hook/useStrictEffect.ts +0 -32
  271. package/dist/templates/next-app/src/uikit/hook/useWarnTranslations.ts +0 -11
  272. package/dist/templates/next-app/src/uikit/utils/getHashParams.ts +0 -8
  273. package/dist/templates/next-app/src/uikit/utils/getHashVerifyEmailParams.ts +0 -42
  274. package/dist/templates/next-app/tailwind.config.ts +0 -8
  275. package/dist/templates/next-app/tsconfig.json +0 -39
  276. package/dist/templates/node-lib/__tests__/readJson.test.ts +0 -26
  277. package/dist/templates/node-lib/bin/test.js +0 -30
  278. package/dist/templates/node-lib/package.json +0 -66
  279. package/dist/templates/node-lib/rollup.config.js +0 -79
  280. package/dist/templates/node-lib/src/index.ts +0 -7
  281. package/dist/templates/node-lib/src/readJson.ts +0 -12
  282. package/dist/templates/node-lib/tsconfig.json +0 -23
  283. package/dist/templates/pack-app/README.md +0 -108
  284. package/dist/templates/pack-app/eslint.config.js +0 -97
  285. package/dist/templates/pack-app/fe-config.json +0 -35
  286. package/dist/templates/pack-app/package.json +0 -86
  287. package/dist/templates/pack-app/pnpm-workspace.yaml +0 -2
  288. package/dist/templates/pack-app/tsconfig.json +0 -9
  289. package/dist/templates/pack-app/tsconfig.test.json +0 -10
  290. package/dist/templates/pack-app/vite.config.ts +0 -14
  291. package/dist/templates/react-app/.env.template +0 -22
  292. package/dist/templates/react-app/.prettierignore +0 -17
  293. package/dist/templates/react-app/README.en.md +0 -274
  294. package/dist/templates/react-app/README.md +0 -273
  295. package/dist/templates/react-app/__tests__/__mocks__/BootstrapTest.ts +0 -16
  296. package/dist/templates/react-app/__tests__/__mocks__/MockAppConfig.ts +0 -48
  297. package/dist/templates/react-app/__tests__/__mocks__/MockDialogHandler.ts +0 -17
  298. package/dist/templates/react-app/__tests__/__mocks__/MockLogger.ts +0 -14
  299. package/dist/templates/react-app/__tests__/__mocks__/components/TestApp.tsx +0 -38
  300. package/dist/templates/react-app/__tests__/__mocks__/components/TestBootstrapsProvider.tsx +0 -53
  301. package/dist/templates/react-app/__tests__/__mocks__/components/TestRouter.tsx +0 -46
  302. package/dist/templates/react-app/__tests__/__mocks__/components/index.ts +0 -12
  303. package/dist/templates/react-app/__tests__/__mocks__/createMockGlobals.ts +0 -96
  304. package/dist/templates/react-app/__tests__/__mocks__/i18nextHttpBackend.ts +0 -110
  305. package/dist/templates/react-app/__tests__/__mocks__/testIOC/TestIOC.ts +0 -55
  306. package/dist/templates/react-app/__tests__/__mocks__/testIOC/TestIOCRegister.ts +0 -74
  307. package/dist/templates/react-app/__tests__/setup/index.ts +0 -1
  308. package/dist/templates/react-app/__tests__/setup/setupGlobal.ts +0 -64
  309. package/dist/templates/react-app/__tests__/src/App.structure.test.tsx +0 -115
  310. package/dist/templates/react-app/__tests__/src/base/cases/AppConfig.test.ts +0 -288
  311. package/dist/templates/react-app/__tests__/src/base/cases/DialogHandler.test.ts +0 -226
  312. package/dist/templates/react-app/__tests__/src/base/cases/I18nKeyErrorPlugin.test.ts +0 -178
  313. package/dist/templates/react-app/__tests__/src/base/cases/InversifyContainer.test.ts +0 -181
  314. package/dist/templates/react-app/__tests__/src/base/cases/PublicAssetsPath.test.ts +0 -61
  315. package/dist/templates/react-app/__tests__/src/base/cases/RequestLogger.test.ts +0 -177
  316. package/dist/templates/react-app/__tests__/src/base/cases/RequestStatusCatcher.test.ts +0 -191
  317. package/dist/templates/react-app/__tests__/src/base/cases/RouterLoader.test.ts +0 -245
  318. package/dist/templates/react-app/__tests__/src/base/services/I18nService.test.ts +0 -240
  319. package/dist/templates/react-app/__tests__/src/core/IOC.test.ts +0 -242
  320. package/dist/templates/react-app/__tests__/src/core/bootstraps/BootstrapClient.test.ts +0 -135
  321. package/dist/templates/react-app/__tests__/src/core/bootstraps/BootstrapsApp.test.ts +0 -74
  322. package/dist/templates/react-app/__tests__/src/main.test.tsx +0 -46
  323. package/dist/templates/react-app/__tests__/src/uikit/components/BaseHeader.test.tsx +0 -97
  324. package/dist/templates/react-app/__tests__/src/uikit/components/chatMessage/ChatRoot.test.tsx +0 -274
  325. package/dist/templates/react-app/config/IOCIdentifier.ts +0 -91
  326. package/dist/templates/react-app/config/Identifier/common/common.error.ts +0 -34
  327. package/dist/templates/react-app/config/Identifier/common/common.ts +0 -62
  328. package/dist/templates/react-app/config/Identifier/common/index.ts +0 -2
  329. package/dist/templates/react-app/config/Identifier/components/component.chatMessage.ts +0 -56
  330. package/dist/templates/react-app/config/Identifier/components/component.messageBaseList.ts +0 -103
  331. package/dist/templates/react-app/config/Identifier/index.ts +0 -2
  332. package/dist/templates/react-app/config/Identifier/pages/index.ts +0 -9
  333. package/dist/templates/react-app/config/Identifier/pages/page.about.ts +0 -189
  334. package/dist/templates/react-app/config/Identifier/pages/page.executor.ts +0 -275
  335. package/dist/templates/react-app/config/Identifier/pages/page.home.ts +0 -71
  336. package/dist/templates/react-app/config/Identifier/pages/page.identifiter.ts +0 -102
  337. package/dist/templates/react-app/config/Identifier/pages/page.jsonStorage.ts +0 -77
  338. package/dist/templates/react-app/config/Identifier/pages/page.login.ts +0 -162
  339. package/dist/templates/react-app/config/Identifier/pages/page.message.ts +0 -20
  340. package/dist/templates/react-app/config/Identifier/pages/page.register.ts +0 -159
  341. package/dist/templates/react-app/config/Identifier/pages/page.request.ts +0 -169
  342. package/dist/templates/react-app/config/app.router.ts +0 -338
  343. package/dist/templates/react-app/config/common.ts +0 -85
  344. package/dist/templates/react-app/config/feapi.mock.json +0 -34
  345. package/dist/templates/react-app/config/i18n/PageI18nInterface.ts +0 -51
  346. package/dist/templates/react-app/config/i18n/aboutI18n.ts +0 -42
  347. package/dist/templates/react-app/config/i18n/chatMessageI18n.ts +0 -17
  348. package/dist/templates/react-app/config/i18n/executorI18n.ts +0 -51
  349. package/dist/templates/react-app/config/i18n/homeI18n.ts +0 -24
  350. package/dist/templates/react-app/config/i18n/i18nConfig.ts +0 -30
  351. package/dist/templates/react-app/config/i18n/identifiter18n.ts +0 -30
  352. package/dist/templates/react-app/config/i18n/jsonStorage18n.ts +0 -27
  353. package/dist/templates/react-app/config/i18n/login18n.ts +0 -42
  354. package/dist/templates/react-app/config/i18n/messageBaseListI18n.ts +0 -22
  355. package/dist/templates/react-app/config/i18n/messageI18n.ts +0 -14
  356. package/dist/templates/react-app/config/i18n/notFoundI18n.ts +0 -34
  357. package/dist/templates/react-app/config/i18n/register18n.ts +0 -40
  358. package/dist/templates/react-app/config/i18n/request18n.ts +0 -41
  359. package/dist/templates/react-app/config/theme.ts +0 -21
  360. package/dist/templates/react-app/docs/en/bootstrap.md +0 -1891
  361. package/dist/templates/react-app/docs/en/components/chat-message-component.md +0 -320
  362. package/dist/templates/react-app/docs/en/components/chat-message-refactor.md +0 -283
  363. package/dist/templates/react-app/docs/en/components/message-base-list-component.md +0 -171
  364. package/dist/templates/react-app/docs/en/development-guide.md +0 -1199
  365. package/dist/templates/react-app/docs/en/env.md +0 -1336
  366. package/dist/templates/react-app/docs/en/global.md +0 -509
  367. package/dist/templates/react-app/docs/en/i18n.md +0 -979
  368. package/dist/templates/react-app/docs/en/index.md +0 -802
  369. package/dist/templates/react-app/docs/en/ioc.md +0 -1365
  370. package/dist/templates/react-app/docs/en/playwright/e2e-tests.md +0 -321
  371. package/dist/templates/react-app/docs/en/playwright/index.md +0 -19
  372. package/dist/templates/react-app/docs/en/playwright/installation-summary.md +0 -332
  373. package/dist/templates/react-app/docs/en/playwright/overview.md +0 -222
  374. package/dist/templates/react-app/docs/en/playwright/quickstart.md +0 -325
  375. package/dist/templates/react-app/docs/en/playwright/reorganization-notes.md +0 -340
  376. package/dist/templates/react-app/docs/en/playwright/setup-complete.md +0 -290
  377. package/dist/templates/react-app/docs/en/playwright/testing-guide.md +0 -565
  378. package/dist/templates/react-app/docs/en/request.md +0 -423
  379. package/dist/templates/react-app/docs/en/router.md +0 -404
  380. package/dist/templates/react-app/docs/en/store.md +0 -1331
  381. package/dist/templates/react-app/docs/en/test-guide.md +0 -976
  382. package/dist/templates/react-app/docs/en/theme.md +0 -424
  383. package/dist/templates/react-app/docs/en/typescript-guide.md +0 -473
  384. package/dist/templates/react-app/docs/en/why-no-globals.md +0 -797
  385. package/dist/templates/react-app/docs/zh/bootstrap.md +0 -1891
  386. package/dist/templates/react-app/docs/zh/components/chat-message-component.md +0 -320
  387. package/dist/templates/react-app/docs/zh/components/chat-message-refactor.md +0 -283
  388. package/dist/templates/react-app/docs/zh/components/message-base-list-component.md +0 -171
  389. package/dist/templates/react-app/docs/zh/development-guide.md +0 -1199
  390. package/dist/templates/react-app/docs/zh/env.md +0 -1336
  391. package/dist/templates/react-app/docs/zh/global.md +0 -511
  392. package/dist/templates/react-app/docs/zh/i18n.md +0 -979
  393. package/dist/templates/react-app/docs/zh/index.md +0 -786
  394. package/dist/templates/react-app/docs/zh/ioc.md +0 -1364
  395. package/dist/templates/react-app/docs/zh/playwright/e2e-tests.md +0 -321
  396. package/dist/templates/react-app/docs/zh/playwright/index.md +0 -19
  397. package/dist/templates/react-app/docs/zh/playwright/installation-summary.md +0 -332
  398. package/dist/templates/react-app/docs/zh/playwright/overview.md +0 -222
  399. package/dist/templates/react-app/docs/zh/playwright/quickstart.md +0 -325
  400. package/dist/templates/react-app/docs/zh/playwright/reorganization-notes.md +0 -340
  401. package/dist/templates/react-app/docs/zh/playwright/setup-complete.md +0 -290
  402. package/dist/templates/react-app/docs/zh/playwright/testing-guide.md +0 -565
  403. package/dist/templates/react-app/docs/zh/request.md +0 -427
  404. package/dist/templates/react-app/docs/zh/router.md +0 -408
  405. package/dist/templates/react-app/docs/zh/store.md +0 -1329
  406. package/dist/templates/react-app/docs/zh/test-guide.md +0 -976
  407. package/dist/templates/react-app/docs/zh/theme.md +0 -424
  408. package/dist/templates/react-app/docs/zh/typescript-guide.md +0 -473
  409. package/dist/templates/react-app/docs/zh/why-no-globals.md +0 -797
  410. package/dist/templates/react-app/e2e/App.spec.ts +0 -319
  411. package/dist/templates/react-app/e2e/fixtures/base.fixture.ts +0 -40
  412. package/dist/templates/react-app/e2e/main.spec.ts +0 -20
  413. package/dist/templates/react-app/e2e/utils/test-helpers.ts +0 -19
  414. package/dist/templates/react-app/eslint.config.mjs +0 -325
  415. package/dist/templates/react-app/index.html +0 -13
  416. package/dist/templates/react-app/makes/generateTs2LocalesOptions.ts +0 -26
  417. package/dist/templates/react-app/package.json +0 -125
  418. package/dist/templates/react-app/playwright.config.ts +0 -79
  419. package/dist/templates/react-app/postcss.config.js +0 -5
  420. package/dist/templates/react-app/public/locales/en/common.json +0 -235
  421. package/dist/templates/react-app/public/locales/zh/common.json +0 -235
  422. package/dist/templates/react-app/public/logo.svg +0 -1
  423. package/dist/templates/react-app/public/router-root/logo.svg +0 -1
  424. package/dist/templates/react-app/src/App.tsx +0 -35
  425. package/dist/templates/react-app/src/assets/react.svg +0 -1
  426. package/dist/templates/react-app/src/base/apis/AiApi.ts +0 -68
  427. package/dist/templates/react-app/src/base/apis/feApi/FeApi.ts +0 -29
  428. package/dist/templates/react-app/src/base/apis/feApi/FeApiAdapter.ts +0 -14
  429. package/dist/templates/react-app/src/base/apis/feApi/FeApiBootstarp.ts +0 -86
  430. package/dist/templates/react-app/src/base/apis/feApi/FeApiType.ts +0 -21
  431. package/dist/templates/react-app/src/base/apis/userApi/UserApi.ts +0 -142
  432. package/dist/templates/react-app/src/base/apis/userApi/UserApiAdapter.ts +0 -14
  433. package/dist/templates/react-app/src/base/apis/userApi/UserApiBootstarp.ts +0 -86
  434. package/dist/templates/react-app/src/base/apis/userApi/UserApiType.ts +0 -70
  435. package/dist/templates/react-app/src/base/cases/AppConfig.ts +0 -123
  436. package/dist/templates/react-app/src/base/cases/DialogHandler.ts +0 -115
  437. package/dist/templates/react-app/src/base/cases/I18nKeyErrorPlugin.ts +0 -64
  438. package/dist/templates/react-app/src/base/cases/InversifyContainer.ts +0 -45
  439. package/dist/templates/react-app/src/base/cases/PublicAssetsPath.ts +0 -23
  440. package/dist/templates/react-app/src/base/cases/RequestLanguages.ts +0 -55
  441. package/dist/templates/react-app/src/base/cases/RequestLogger.ts +0 -80
  442. package/dist/templates/react-app/src/base/cases/RequestStatusCatcher.ts +0 -40
  443. package/dist/templates/react-app/src/base/cases/ResourceState.ts +0 -23
  444. package/dist/templates/react-app/src/base/cases/RouterLoader.ts +0 -173
  445. package/dist/templates/react-app/src/base/cases/TranslateI18nInterface.ts +0 -26
  446. package/dist/templates/react-app/src/base/port/ExecutorPageBridgeInterface.ts +0 -23
  447. package/dist/templates/react-app/src/base/port/I18nServiceInterface.ts +0 -10
  448. package/dist/templates/react-app/src/base/port/IOCInterface.ts +0 -38
  449. package/dist/templates/react-app/src/base/port/JSONStoragePageBridgeInterface.ts +0 -21
  450. package/dist/templates/react-app/src/base/port/RequestPageBridgeInterface.ts +0 -23
  451. package/dist/templates/react-app/src/base/port/RequestStatusInterface.ts +0 -5
  452. package/dist/templates/react-app/src/base/port/RouteServiceInterface.ts +0 -29
  453. package/dist/templates/react-app/src/base/port/UserServiceInterface.ts +0 -20
  454. package/dist/templates/react-app/src/base/services/BaseLayoutService.ts +0 -61
  455. package/dist/templates/react-app/src/base/services/I18nService.ts +0 -146
  456. package/dist/templates/react-app/src/base/services/IdentifierService.ts +0 -162
  457. package/dist/templates/react-app/src/base/services/RouteService.ts +0 -115
  458. package/dist/templates/react-app/src/base/services/UserBootstrap.ts +0 -45
  459. package/dist/templates/react-app/src/base/services/UserService.ts +0 -88
  460. package/dist/templates/react-app/src/base/types/Page.ts +0 -47
  461. package/dist/templates/react-app/src/base/types/deprecated-antd.d.ts +0 -60
  462. package/dist/templates/react-app/src/base/types/global.d.ts +0 -8
  463. package/dist/templates/react-app/src/core/IOC.ts +0 -28
  464. package/dist/templates/react-app/src/core/bootstraps/BootstrapClient.ts +0 -108
  465. package/dist/templates/react-app/src/core/bootstraps/BootstrapsRegistry.ts +0 -54
  466. package/dist/templates/react-app/src/core/bootstraps/IocIdentifierTest.ts +0 -26
  467. package/dist/templates/react-app/src/core/bootstraps/PrintBootstrap.ts +0 -14
  468. package/dist/templates/react-app/src/core/bootstraps/SaveAppInfo.ts +0 -28
  469. package/dist/templates/react-app/src/core/clientIoc/ClientIOC.ts +0 -47
  470. package/dist/templates/react-app/src/core/clientIoc/ClientIOCRegister.ts +0 -142
  471. package/dist/templates/react-app/src/core/globals.ts +0 -47
  472. package/dist/templates/react-app/src/main.tsx +0 -19
  473. package/dist/templates/react-app/src/pages/404.tsx +0 -19
  474. package/dist/templates/react-app/src/pages/500.tsx +0 -18
  475. package/dist/templates/react-app/src/pages/NoRouteFound.tsx +0 -5
  476. package/dist/templates/react-app/src/pages/auth/Layout.tsx +0 -27
  477. package/dist/templates/react-app/src/pages/auth/LoginPage.tsx +0 -166
  478. package/dist/templates/react-app/src/pages/auth/RegisterPage.tsx +0 -226
  479. package/dist/templates/react-app/src/pages/base/AboutPage.tsx +0 -123
  480. package/dist/templates/react-app/src/pages/base/ExecutorPage.tsx +0 -467
  481. package/dist/templates/react-app/src/pages/base/HomePage.tsx +0 -81
  482. package/dist/templates/react-app/src/pages/base/IdentifierPage.tsx +0 -117
  483. package/dist/templates/react-app/src/pages/base/JSONStoragePage.tsx +0 -132
  484. package/dist/templates/react-app/src/pages/base/Layout.tsx +0 -20
  485. package/dist/templates/react-app/src/pages/base/MessagePage.tsx +0 -71
  486. package/dist/templates/react-app/src/pages/base/RedirectPathname.tsx +0 -18
  487. package/dist/templates/react-app/src/pages/base/RequestPage.tsx +0 -193
  488. package/dist/templates/react-app/src/styles/css/antd-themes/_common/_default.css +0 -280
  489. package/dist/templates/react-app/src/styles/css/antd-themes/_common/dark.css +0 -233
  490. package/dist/templates/react-app/src/styles/css/antd-themes/_common/index.css +0 -3
  491. package/dist/templates/react-app/src/styles/css/antd-themes/_common/pink.css +0 -246
  492. package/dist/templates/react-app/src/styles/css/antd-themes/index.css +0 -4
  493. package/dist/templates/react-app/src/styles/css/antd-themes/menu/_default.css +0 -108
  494. package/dist/templates/react-app/src/styles/css/antd-themes/menu/dark.css +0 -67
  495. package/dist/templates/react-app/src/styles/css/antd-themes/menu/index.css +0 -3
  496. package/dist/templates/react-app/src/styles/css/antd-themes/menu/pink.css +0 -67
  497. package/dist/templates/react-app/src/styles/css/antd-themes/no-context.css +0 -34
  498. package/dist/templates/react-app/src/styles/css/antd-themes/pagination/_default.css +0 -34
  499. package/dist/templates/react-app/src/styles/css/antd-themes/pagination/dark.css +0 -31
  500. package/dist/templates/react-app/src/styles/css/antd-themes/pagination/index.css +0 -3
  501. package/dist/templates/react-app/src/styles/css/antd-themes/pagination/pink.css +0 -36
  502. package/dist/templates/react-app/src/styles/css/antd-themes/table/_default.css +0 -44
  503. package/dist/templates/react-app/src/styles/css/antd-themes/table/dark.css +0 -43
  504. package/dist/templates/react-app/src/styles/css/antd-themes/table/index.css +0 -3
  505. package/dist/templates/react-app/src/styles/css/antd-themes/table/pink.css +0 -43
  506. package/dist/templates/react-app/src/styles/css/index.css +0 -6
  507. package/dist/templates/react-app/src/styles/css/page.css +0 -20
  508. package/dist/templates/react-app/src/styles/css/scrollbar.css +0 -34
  509. package/dist/templates/react-app/src/styles/css/tailwind.css +0 -5
  510. package/dist/templates/react-app/src/styles/css/themes/_default.css +0 -30
  511. package/dist/templates/react-app/src/styles/css/themes/dark.css +0 -30
  512. package/dist/templates/react-app/src/styles/css/themes/index.css +0 -3
  513. package/dist/templates/react-app/src/styles/css/themes/pink.css +0 -30
  514. package/dist/templates/react-app/src/styles/css/zIndex.css +0 -9
  515. package/dist/templates/react-app/src/uikit/bridges/ExecutorPageBridge.ts +0 -72
  516. package/dist/templates/react-app/src/uikit/bridges/JSONStoragePageBridge.ts +0 -41
  517. package/dist/templates/react-app/src/uikit/bridges/NavigateBridge.ts +0 -22
  518. package/dist/templates/react-app/src/uikit/bridges/RequestPageBridge.ts +0 -136
  519. package/dist/templates/react-app/src/uikit/components/AppRouterProvider.tsx +0 -35
  520. package/dist/templates/react-app/src/uikit/components/BaseHeader.tsx +0 -51
  521. package/dist/templates/react-app/src/uikit/components/BaseLayoutProvider.tsx +0 -44
  522. package/dist/templates/react-app/src/uikit/components/BaseRouteProvider.tsx +0 -21
  523. package/dist/templates/react-app/src/uikit/components/BaseRouteSeo.tsx +0 -18
  524. package/dist/templates/react-app/src/uikit/components/BootstrapsProvider.tsx +0 -11
  525. package/dist/templates/react-app/src/uikit/components/ClientSeo.tsx +0 -62
  526. package/dist/templates/react-app/src/uikit/components/ComboProvider.tsx +0 -38
  527. package/dist/templates/react-app/src/uikit/components/LanguageSwitcher.tsx +0 -78
  528. package/dist/templates/react-app/src/uikit/components/Loading.tsx +0 -49
  529. package/dist/templates/react-app/src/uikit/components/LocaleLink.tsx +0 -43
  530. package/dist/templates/react-app/src/uikit/components/LogoutButton.tsx +0 -58
  531. package/dist/templates/react-app/src/uikit/components/MessageBaseList.tsx +0 -258
  532. package/dist/templates/react-app/src/uikit/components/RouterRenderComponent.tsx +0 -19
  533. package/dist/templates/react-app/src/uikit/components/ThemeSwitcher.tsx +0 -137
  534. package/dist/templates/react-app/src/uikit/components/With.tsx +0 -17
  535. package/dist/templates/react-app/src/uikit/components/chatMessage/ChatMessageBridge.ts +0 -205
  536. package/dist/templates/react-app/src/uikit/components/chatMessage/ChatRoot.tsx +0 -21
  537. package/dist/templates/react-app/src/uikit/components/chatMessage/FocusBar.tsx +0 -108
  538. package/dist/templates/react-app/src/uikit/components/chatMessage/MessageApi.ts +0 -282
  539. package/dist/templates/react-app/src/uikit/components/chatMessage/MessageItem.tsx +0 -102
  540. package/dist/templates/react-app/src/uikit/components/chatMessage/MessagesList.tsx +0 -86
  541. package/dist/templates/react-app/src/uikit/contexts/BaseRouteContext.ts +0 -42
  542. package/dist/templates/react-app/src/uikit/contexts/IOCContext.ts +0 -13
  543. package/dist/templates/react-app/src/uikit/hooks/useAppTranslation.ts +0 -26
  544. package/dist/templates/react-app/src/uikit/hooks/useI18nInterface.ts +0 -25
  545. package/dist/templates/react-app/src/uikit/hooks/useIOC.ts +0 -35
  546. package/dist/templates/react-app/src/uikit/hooks/useNavigateBridge.ts +0 -21
  547. package/dist/templates/react-app/src/uikit/hooks/useRouterI18nGuard.ts +0 -25
  548. package/dist/templates/react-app/src/uikit/hooks/useStrictEffect.ts +0 -31
  549. package/dist/templates/react-app/src/vite-env.d.ts +0 -1
  550. package/dist/templates/react-app/tailwind.config.js +0 -4
  551. package/dist/templates/react-app/tsconfig.app.json +0 -36
  552. package/dist/templates/react-app/tsconfig.e2e.json +0 -24
  553. package/dist/templates/react-app/tsconfig.json +0 -22
  554. package/dist/templates/react-app/tsconfig.node.json +0 -27
  555. package/dist/templates/react-app/tsconfig.test.json +0 -18
  556. package/dist/templates/react-app/vite.config.ts +0 -144
@@ -1,1331 +0,0 @@
1
- # Store State Management
2
-
3
- ## 📋 Table of Contents
4
-
5
- - [Core Philosophy](#-core-philosophy) - Application layer notifies UI layer
6
- - [What is Store](#-what-is-store) - State container
7
- - [Why Need Store](#-why-need-store) - Solve communication problem
8
- - [Core Problem](#-core-problem) - How application layer notifies UI layer
9
- - [Implementation in the Project](#-implementation-in-the-project) - Practical guide
10
- - [How to Use](#-how-to-use) - Service + Store + useStore
11
- - [Testing](#-testing) - Independent testing and combination testing
12
- - [Best Practices](#-best-practices) - 7 core practices
13
- - [FAQ](#-faq) - Common questions
14
-
15
- ---
16
-
17
- ## 🎯 Core Philosophy
18
-
19
- > **🚨 Core Problem: How does the application layer (Service) notify the UI layer to update while maintaining separation?**
20
-
21
- > **⭐ Solution: Service contains Store, publishes state through `emit`, UI subscribes to state through `useStore`!**
22
-
23
- ### Core Concept
24
-
25
- ```
26
- ┌──────────────────────────────────────────────┐
27
- │ Problem: UI and logic are separated, │
28
- │ but how do they communicate? │
29
- │ │
30
- │ Service (Application Layer) │
31
- │ ├── Business logic │
32
- │ └── Data processing │
33
- │ ↓ How to notify? │
34
- │ Component (UI Layer) │
35
- │ └── UI rendering │
36
- │ │
37
- │ ❌ Problem: Service changed data, how does │
38
- │ UI know? │
39
- └──────────────────────────────────────────────┘
40
-
41
- ┌──────────────────────────────────────────────┐
42
- │ Solution: Store as bridge │
43
- │ │
44
- │ Service (Application Layer) │
45
- │ ├── Business logic │
46
- │ ├── Store (State container) │
47
- │ │ ├── state │
48
- │ │ └── emit() (Publish state) │
49
- │ │ │
50
- │ │ ↓ Publish-Subscribe pattern │
51
- │ │ │
52
- │ └── useStore (Subscribe) │
53
- │ ↓ │
54
- │ Component (UI Layer) │
55
- │ └── Auto-update UI │
56
- │ │
57
- │ ✅ Service publishes state via emit │
58
- │ ✅ UI subscribes to state via useStore │
59
- │ ✅ Maintain separation, decoupled │
60
- └──────────────────────────────────────────────┘
61
- ```
62
-
63
- ---
64
-
65
- ## 🗂️ What is Store
66
-
67
- Store is a **reactive state container** based on the **publish-subscribe pattern**.
68
-
69
- ### Simple Understanding
70
-
71
- ```
72
- Store = State + Publish-Subscribe
73
-
74
- Service owns Store
75
- Service publishes state through Store.emit()
76
- UI subscribes to state through useStore()
77
- ```
78
-
79
- ### Analogy
80
-
81
- ```
82
- Store is like a radio station:
83
-
84
- 📻 Station (Store)
85
- - Has program content (state)
86
- - Can broadcast programs (emit)
87
- - Listeners can tune in (subscribe)
88
-
89
- 🎤 Host (Service)
90
- - Creates program content (business logic)
91
- - Broadcasts via station (emit)
92
-
93
- 📱 Listener (UI Component)
94
- - Tunes into station (useStore)
95
- - Automatically reacts to new content (auto-update UI)
96
- ```
97
-
98
- ---
99
-
100
- ## 🤔 Why Need Store
101
-
102
- ### Core Problem: How to communicate after UI and logic separation?
103
-
104
- We've already separated UI and logic through IOC, but here's the problem:
105
-
106
- #### ❌ Problem Example: Without Store
107
-
108
- ```typescript
109
- // Service (Logic layer)
110
- @injectable()
111
- export class UserService {
112
- private user: UserInfo | null = null;
113
-
114
- async login(username: string, password: string) {
115
- const response = await this.api.login({ username, password });
116
- this.user = response.user; // ✅ Login successful, user updated
117
-
118
- // ❌ Problem: How does UI know user has been updated?
119
- // ❌ Service cannot notify UI
120
- }
121
- }
122
-
123
- // UI component
124
- function UserProfile() {
125
- const userService = useIOC('UserServiceInterface');
126
-
127
- // ❌ Problem: How to get userService.user?
128
- // ❌ How to trigger UI re-render when userService.user updates?
129
-
130
- return <div>{/* Cannot display user */}</div>;
131
- }
132
-
133
- // 😰😰😰 Problem Summary:
134
- // 1. UI cannot access Service's internal state
135
- // 2. UI doesn't know when Service state updates
136
- // 3. Need to manually call a method to get state? (breaks separation principle)
137
- // 4. Need to poll for state? (poor performance)
138
- ```
139
-
140
- #### ✅ Solution: Use Store
141
-
142
- ```typescript
143
- // Service (Logic layer)
144
- @injectable()
145
- export class UserService extends StoreInterface<UserState> {
146
- constructor() {
147
- super(() => ({
148
- user: null,
149
- loading: false
150
- }));
151
- }
152
-
153
- async login(username: string, password: string) {
154
- // Set loading state
155
- this.emit({ ...this.state, loading: true });
156
-
157
- const response = await this.api.login({ username, password });
158
-
159
- // ✅ Publish new state via emit, automatically notify all subscribers
160
- this.emit({
161
- user: response.user,
162
- loading: false
163
- });
164
- }
165
- }
166
-
167
- // UI component
168
- function UserProfile() {
169
- const userService = useIOC('UserServiceInterface');
170
-
171
- // ✅ Subscribe to state via useStore
172
- const { user, loading } = useStore(userService);
173
-
174
- // ✅ Component automatically re-renders when userService.emit()
175
-
176
- if (loading) return <div>Loading...</div>;
177
- return <div>{user?.name}</div>;
178
- }
179
-
180
- // ✅✅✅ Advantages Summary:
181
- // 1. UI can subscribe to Service state
182
- // 2. UI automatically updates when Service state updates
183
- // 3. Maintain separation (Service doesn't know which UIs are listening)
184
- // 4. High performance (only subscribed components update)
185
- ```
186
-
187
- ### Comparison Summary
188
-
189
- | Feature | Without Store | With Store |
190
- | ----------------------- | -------------------------------------- | ------------------------------ |
191
- | **State Access** | ❌ Cannot access internal state | ✅ Subscribe via useStore |
192
- | **Update Notification** | ❌ UI doesn't know about state changes | ✅ emit automatically notifies |
193
- | **UI Update** | ❌ Need manual trigger | ✅ Auto re-render |
194
- | **Decoupling** | ❌ Service needs to know UI | ✅ Completely decoupled |
195
- | **Performance** | ❌ Polling or global update | ✅ Precise subscriber updates |
196
- | **Testability** | ❌ Hard to test state changes | ✅ Easy to test state |
197
-
198
- ---
199
-
200
- ## ❓ Core Problem
201
-
202
- ### How does application layer notify UI layer while maintaining separation?
203
-
204
- #### Problem Breakdown
205
-
206
- 1. **Application layer (Service) has state** - like user info, loading state
207
- 2. **UI layer needs to display this state** - show username, show loading animation
208
- 3. **Application layer state changes** - after login success, user info updates
209
- 4. **UI layer needs to auto-update** - after user info changes, UI automatically shows new name
210
- 5. **Maintain separation** - Service shouldn't directly manipulate UI, UI shouldn't directly access Service internals
211
-
212
- #### Solution: Publish-Subscribe Pattern
213
-
214
- ```typescript
215
- // 1. Service defines state
216
- interface UserState {
217
- user: UserInfo | null;
218
- loading: boolean;
219
- }
220
-
221
- // 2. Service extends StoreInterface
222
- @injectable()
223
- export class UserService extends StoreInterface<UserState> {
224
- constructor() {
225
- super(() => ({
226
- user: null,
227
- loading: false
228
- }));
229
- }
230
-
231
- // 3. Service publishes state via emit
232
- async login(username: string, password: string) {
233
- this.emit({ ...this.state, loading: true }); // Publish: start loading
234
-
235
- const response = await this.api.login({ username, password });
236
-
237
- this.emit({
238
- user: response.user,
239
- loading: false
240
- }); // Publish: loading complete, user logged in
241
- }
242
-
243
- // 4. Service doesn't need to know who's listening
244
- // ✅ Completely decoupled
245
- }
246
-
247
- // 5. UI subscribes to state via useStore
248
- function LoginPage() {
249
- const userService = useIOC('UserServiceInterface');
250
- const { loading } = useStore(userService);
251
-
252
- const handleLogin = () => {
253
- userService.login('user', 'pass');
254
- };
255
-
256
- // 6. When Service emits new state, UI auto-updates
257
- return (
258
- <button onClick={handleLogin} disabled={loading}>
259
- {loading ? 'Logging in...' : 'Login'}
260
- </button>
261
- );
262
- }
263
- ```
264
-
265
- #### Workflow
266
-
267
- ```
268
- ┌─────────────────────────────────────────────┐
269
- │ Complete state update flow │
270
- │ │
271
- │ 1. User clicks button │
272
- │ ↓ │
273
- │ 2. UI calls Service method │
274
- │ userService.login() │
275
- │ ↓ │
276
- │ 3. Service executes business logic │
277
- │ - Call API │
278
- │ - Process data │
279
- │ ↓ │
280
- │ 4. Service publishes new state via emit │
281
- │ this.emit({ user: ..., loading: false })│
282
- │ ↓ │
283
- │ 5. Store notifies all subscribers │
284
- │ listeners.forEach(listener => ...) │
285
- │ ↓ │
286
- │ 6. useStore receives notification │
287
- │ Trigger component re-render │
288
- │ ↓ │
289
- │ 7. UI displays latest state │
290
- │ Show username / Hide loading animation │
291
- └─────────────────────────────────────────────┘
292
- ```
293
-
294
- ---
295
-
296
- ## 🛠️ Implementation in the Project
297
-
298
- ### 1. File Structure
299
-
300
- ```
301
- src/
302
- ├── base/
303
- │ ├── services/
304
- │ │ ├── UserService.ts # Service extends StoreInterface
305
- │ │ ├── RouteService.ts # Service extends StoreInterface
306
- │ │ └── I18nService.ts # Service extends StoreInterface
307
- │ └── port/
308
- │ └── UserServiceInterface.ts # Service interface
309
- └── uikit/
310
- └── hooks/
311
- └── useStore.ts (from @brain-toolkit/react-kit)
312
- ```
313
-
314
- ### 2. Store Base Class
315
-
316
- Store system is based on `SliceStore` from `@brain-toolkit/react-kit`:
317
-
318
- ```typescript
319
- // From @brain-toolkit/react-kit
320
- export class SliceStore<T> {
321
- protected state: T;
322
- private listeners = new Set<(state: T) => void>();
323
-
324
- constructor(stateFactory: () => T) {
325
- this.state = stateFactory();
326
- }
327
-
328
- // Publish state
329
- protected emit(newState: T) {
330
- this.state = newState;
331
- // Notify all subscribers
332
- this.listeners.forEach((listener) => listener(this.state));
333
- }
334
-
335
- // Subscribe to state
336
- subscribe(listener: (state: T) => void) {
337
- this.listeners.add(listener);
338
- // Return unsubscribe function
339
- return () => this.listeners.delete(listener);
340
- }
341
-
342
- // Get current state
343
- getState(): T {
344
- return this.state;
345
- }
346
- }
347
- ```
348
-
349
- ### 3. StoreInterface Base Class
350
-
351
- Project's Store base class, provides additional utility methods:
352
-
353
- ```typescript
354
- // From @qlover/corekit-bridge
355
- export abstract class StoreInterface<
356
- T extends StoreStateInterface
357
- > extends SliceStore<T> {
358
- constructor(protected stateFactory: () => T) {
359
- super(stateFactory);
360
- }
361
-
362
- // Reset state
363
- resetState(): void {
364
- this.emit(this.stateFactory());
365
- }
366
-
367
- // Clone state (for updates)
368
- cloneState(source?: Partial<T>): T {
369
- const cloned = clone(this.state);
370
- if (typeof cloned === 'object' && cloned !== null) {
371
- Object.assign(cloned, source);
372
- }
373
- return cloned;
374
- }
375
- }
376
- ```
377
-
378
- ### 4. State Interface
379
-
380
- ```typescript
381
- // All state must implement this interface
382
- export interface StoreStateInterface {
383
- // Can define common properties here
384
- // loading?: boolean;
385
- // error?: Error | null;
386
- }
387
- ```
388
-
389
- ---
390
-
391
- ## 📝 How to Use
392
-
393
- ### 1. Define State Interface
394
-
395
- ```typescript
396
- // src/base/services/UserService.ts
397
- export interface UserState extends StoreStateInterface {
398
- user: UserInfo | null;
399
- loading: boolean;
400
- error: Error | null;
401
- }
402
- ```
403
-
404
- ### 2. Service Extends StoreInterface
405
-
406
- ```typescript
407
- // src/base/services/UserService.ts
408
- import { StoreInterface } from '@qlover/corekit-bridge';
409
- import { injectable, inject } from 'inversify';
410
-
411
- @injectable()
412
- export class UserService extends StoreInterface<UserState> {
413
- constructor(
414
- @inject(UserApi) private api: UserApi,
415
- @inject(IOCIdentifier.AppConfig) private config: AppConfig
416
- ) {
417
- // Initialize state
418
- super(() => ({
419
- user: null,
420
- loading: false,
421
- error: null
422
- }));
423
- }
424
-
425
- // Business method: publish state via emit
426
- async login(username: string, password: string) {
427
- // 1. Start loading
428
- this.emit({
429
- ...this.state,
430
- loading: true,
431
- error: null
432
- });
433
-
434
- try {
435
- // 2. Call API
436
- const response = await this.api.login({ username, password });
437
-
438
- // 3. Success: publish new state
439
- this.emit({
440
- user: response.user,
441
- loading: false,
442
- error: null
443
- });
444
- } catch (error) {
445
- // 4. Failure: publish error state
446
- this.emit({
447
- ...this.state,
448
- loading: false,
449
- error: error as Error
450
- });
451
- }
452
- }
453
-
454
- async logout() {
455
- this.emit({
456
- user: null,
457
- loading: false,
458
- error: null
459
- });
460
- }
461
-
462
- // Use cloneState to simplify updates
463
- setUser(user: UserInfo) {
464
- this.emit(this.cloneState({ user }));
465
- }
466
- }
467
- ```
468
-
469
- ### 3. UI Subscribes to State
470
-
471
- ```typescript
472
- // src/pages/LoginPage.tsx
473
- import { useStore } from '@brain-toolkit/react-kit/hooks/useStore';
474
- import { useIOC } from '@/uikit/hooks/useIOC';
475
-
476
- function LoginPage() {
477
- const userService = useIOC('UserServiceInterface');
478
-
479
- // ✅ Method 1: Subscribe to complete state
480
- const { user, loading, error } = useStore(userService);
481
-
482
- const handleLogin = async () => {
483
- await userService.login('username', 'password');
484
- };
485
-
486
- if (loading) {
487
- return <div>Loading...</div>;
488
- }
489
-
490
- return (
491
- <div>
492
- {error && <div>Error: {error.message}</div>}
493
- <button onClick={handleLogin}>Login</button>
494
- </div>
495
- );
496
- }
497
- ```
498
-
499
- ### 4. Use Selectors (Performance Optimization)
500
-
501
- ```typescript
502
- // src/pages/UserProfile.tsx
503
- function UserProfile() {
504
- const userService = useIOC('UserServiceInterface');
505
-
506
- // ✅ Method 2: Only subscribe to needed state (better performance)
507
- const user = useStore(userService, (state) => state.user);
508
-
509
- // ✅ Only re-renders when user changes, loading changes won't trigger
510
-
511
- return <div>{user?.name}</div>;
512
- }
513
- ```
514
-
515
- ### 5. Define Selectors (Recommended)
516
-
517
- ```typescript
518
- // src/base/services/UserService.ts
519
- @injectable()
520
- export class UserService extends StoreInterface<UserState> {
521
- // ... other code
522
-
523
- // ✅ Define selectors
524
- selector = {
525
- user: (state: UserState) => state.user,
526
- loading: (state: UserState) => state.loading,
527
- error: (state: UserState) => state.error,
528
- isLoggedIn: (state: UserState) => state.user !== null
529
- };
530
- }
531
-
532
- // Usage
533
- function UserProfile() {
534
- const userService = useIOC('UserServiceInterface');
535
-
536
- // ✅ Use predefined selectors
537
- const user = useStore(userService, userService.selector.user);
538
- const isLoggedIn = useStore(userService, userService.selector.isLoggedIn);
539
-
540
- return <div>{isLoggedIn ? user?.name : 'Please login'}</div>;
541
- }
542
- ```
543
-
544
- ### 6. Real Project Examples
545
-
546
- #### Example 1: UserService
547
-
548
- ```typescript
549
- // src/base/services/UserService.ts
550
- @injectable()
551
- export class UserService extends UserAuthServiceInterface {
552
- constructor(
553
- @inject(UserApi) userApi: UserApi,
554
- @inject(IOCIdentifier.AppConfig) appConfig: AppConfig,
555
- @inject(IOCIdentifier.LocalStorageEncrypt) storage: Storage
556
- ) {
557
- super(userApi, {
558
- userStorage: {
559
- key: appConfig.userInfoStorageKey,
560
- storage: storage
561
- },
562
- credentialStorage: {
563
- key: appConfig.userTokenStorageKey,
564
- storage: storage
565
- }
566
- });
567
- }
568
-
569
- // ✅ UserService's base class contains store
570
- override get store(): UserAuthStore<UserApiState> {
571
- return super.store as UserAuthStore<UserApiState>;
572
- }
573
-
574
- override async logout(): Promise<void> {
575
- await super.logout();
576
- // ✅ store automatically notifies UI
577
- this.routerService.gotoLogin();
578
- }
579
- }
580
-
581
- // Usage
582
- function Layout() {
583
- const userService = useIOC(IOCIdentifier.UserServiceInterface);
584
-
585
- // ✅ Subscribe to userService.store
586
- useStore(userService.store);
587
-
588
- if (userService.isAuthenticated()) {
589
- return <Navigate to="/" replace />;
590
- }
591
-
592
- return <Outlet />;
593
- }
594
- ```
595
-
596
- #### Example 2: RouteService
597
-
598
- ```typescript
599
- // src/base/services/RouteService.ts
600
- export class RouteService extends StoreInterface<RouterServiceState> {
601
- constructor(
602
- protected uiBridge: UIBridgeInterface<NavigateFunction>,
603
- protected i18nService: I18nServiceInterface,
604
- protected options: RouterServiceOptions
605
- ) {
606
- super(
607
- () => new RouterServiceState(options.routes, !!options.hasLocalRoutes)
608
- );
609
- }
610
-
611
- // ✅ Publish route changes via emit
612
- override changeRoutes(routes: RouteConfigValue[]): void {
613
- this.emit(this.cloneState({ routes }));
614
- }
615
-
616
- override goto(path: string, options?: NavigateOptions): void {
617
- const composedPath = this.composePath(path);
618
- this.uiBridge.getUIBridge()(composedPath, options);
619
- }
620
- }
621
-
622
- // Usage
623
- function AppRouterProvider() {
624
- const routerService = useIOC(IOCIdentifier.RouteServiceInterface);
625
-
626
- // ✅ Subscribe to routes changes
627
- const routes = useStore(routerService, (state) => state.routes);
628
-
629
- const router = createBrowserRouter(routes);
630
-
631
- return <RouterProvider router={router} />;
632
- }
633
- ```
634
-
635
- #### Example 3: I18nService
636
-
637
- ```typescript
638
- // src/base/services/I18nService.ts
639
- export class I18nService extends StoreInterface<I18nServiceState> {
640
- constructor(protected pathname: string) {
641
- super(() => new I18nServiceState(i18n.language as I18nServiceLocale));
642
- }
643
-
644
- selector = {
645
- loading: (state: I18nServiceState) => state.loading,
646
- language: (state: I18nServiceState) => state.language
647
- };
648
-
649
- override async changeLanguage(lng: string): Promise<void> {
650
- // ✅ Publish loading state
651
- this.emit(this.cloneState({ loading: true }));
652
-
653
- await i18n.changeLanguage(lng);
654
-
655
- // ✅ Publish complete state
656
- this.emit({
657
- language: lng as I18nServiceLocale,
658
- loading: false
659
- });
660
- }
661
- }
662
-
663
- // Usage
664
- function LanguageSwitcher() {
665
- const i18nService = useIOC(IOCIdentifier.I18nServiceInterface);
666
-
667
- // ✅ Only subscribe to loading state
668
- const loading = useStore(i18nService, i18nService.selector.loading);
669
-
670
- return (
671
- <Select
672
- value={i18n.language}
673
- loading={loading}
674
- onChange={(lng) => i18nService.changeLanguage(lng)}
675
- />
676
- );
677
- }
678
- ```
679
-
680
- ---
681
-
682
- ## 🧪 Testing
683
-
684
- ### Core Advantage: Store can be tested independently, UI can mock Store
685
-
686
- #### 1. Test Service and Store (Logic Test)
687
-
688
- ```typescript
689
- // __tests__/src/base/services/UserService.test.ts
690
- import { describe, it, expect, vi, beforeEach } from 'vitest';
691
- import { UserService } from '@/base/services/UserService';
692
-
693
- describe('UserService (Logic Test)', () => {
694
- let userService: UserService;
695
- let mockApi: any;
696
-
697
- beforeEach(() => {
698
- mockApi = {
699
- login: vi.fn(),
700
- getUserInfo: vi.fn()
701
- };
702
-
703
- userService = new UserService(mockApi, mockConfig, mockStorage);
704
- });
705
-
706
- it('should update store state when login success', async () => {
707
- // ✅ Test state changes
708
- mockApi.login.mockResolvedValue({
709
- user: { name: 'John', id: 1 },
710
- token: 'test-token'
711
- });
712
-
713
- // Subscribe to state changes
714
- const states: any[] = [];
715
- userService.subscribe((state) => {
716
- states.push({ ...state });
717
- });
718
-
719
- // Call login
720
- await userService.login('user', 'pass');
721
-
722
- // ✅ Verify state change sequence
723
- expect(states).toHaveLength(2);
724
-
725
- // First emit: loading = true
726
- expect(states[0]).toEqual({
727
- user: null,
728
- loading: true,
729
- error: null
730
- });
731
-
732
- // Second emit: loading = false, user = John
733
- expect(states[1]).toEqual({
734
- user: { name: 'John', id: 1 },
735
- loading: false,
736
- error: null
737
- });
738
- });
739
-
740
- it('should update store state when login fails', async () => {
741
- mockApi.login.mockRejectedValue(new Error('Invalid credentials'));
742
-
743
- const states: any[] = [];
744
- userService.subscribe((state) => states.push({ ...state }));
745
-
746
- await expect(userService.login('user', 'wrong')).rejects.toThrow();
747
-
748
- // ✅ Verify error state
749
- expect(states[1]).toEqual({
750
- user: null,
751
- loading: false,
752
- error: expect.any(Error)
753
- });
754
- });
755
-
756
- it('should emit logout state', () => {
757
- // First set user logged in
758
- userService.emit({
759
- user: { name: 'John', id: 1 },
760
- loading: false,
761
- error: null
762
- });
763
-
764
- // Logout
765
- userService.logout();
766
-
767
- // ✅ Verify state reset
768
- expect(userService.getState()).toEqual({
769
- user: null,
770
- loading: false,
771
- error: null
772
- });
773
- });
774
- });
775
-
776
- // ✅✅✅ Advantages:
777
- // 1. Don't need to render UI
778
- // 2. Can test all state changes
779
- // 3. Can verify emit call sequence
780
- // 4. Tests run fast
781
- ```
782
-
783
- #### 2. Test UI Component (UI Test)
784
-
785
- ```typescript
786
- // __tests__/src/pages/LoginPage.test.tsx
787
- import { describe, it, expect, vi } from 'vitest';
788
- import { render, screen, fireEvent, waitFor } from '@testing-library/react';
789
- import { LoginPage } from '@/pages/LoginPage';
790
- import { IOCProvider } from '@/uikit/contexts/IOCContext';
791
-
792
- describe('LoginPage (UI Test)', () => {
793
- it('should show loading when login', async () => {
794
- // ✅ Mock Service and Store
795
- const mockStore = {
796
- user: null,
797
- loading: false,
798
- error: null
799
- };
800
-
801
- const mockUserService = {
802
- login: vi.fn().mockImplementation(() => {
803
- // Simulate state change
804
- mockStore.loading = true;
805
- return Promise.resolve();
806
- }),
807
- subscribe: vi.fn(),
808
- getState: () => mockStore
809
- };
810
-
811
- const mockIOC = (identifier: string) => {
812
- if (identifier === 'UserServiceInterface') return mockUserService;
813
- };
814
-
815
- // ✅ Render component
816
- const { rerender } = render(
817
- <IOCProvider value={mockIOC}>
818
- <LoginPage />
819
- </IOCProvider>
820
- );
821
-
822
- // Click login button
823
- const loginButton = screen.getByText('Login');
824
- fireEvent.click(loginButton);
825
-
826
- // ✅ Verify Service was called
827
- expect(mockUserService.login).toHaveBeenCalled();
828
-
829
- // Simulate state update
830
- mockStore.loading = true;
831
- rerender(
832
- <IOCProvider value={mockIOC}>
833
- <LoginPage />
834
- </IOCProvider>
835
- );
836
-
837
- // ✅ Verify UI shows loading state
838
- expect(screen.getByText('Loading...')).toBeInTheDocument();
839
- });
840
-
841
- it('should show error message when login fails', () => {
842
- const mockStore = {
843
- user: null,
844
- loading: false,
845
- error: new Error('Invalid credentials')
846
- };
847
-
848
- const mockUserService = {
849
- login: vi.fn(),
850
- subscribe: vi.fn(),
851
- getState: () => mockStore
852
- };
853
-
854
- const mockIOC = (identifier: string) => {
855
- if (identifier === 'UserServiceInterface') return mockUserService;
856
- };
857
-
858
- render(
859
- <IOCProvider value={mockIOC}>
860
- <LoginPage />
861
- </IOCProvider>
862
- );
863
-
864
- // ✅ Verify error message displayed
865
- expect(screen.getByText('Error: Invalid credentials')).toBeInTheDocument();
866
- });
867
- });
868
-
869
- // ✅✅✅ Advantages:
870
- // 1. Don't need real Service implementation
871
- // 2. Can easily simulate various states
872
- // 3. UI tests focus on UI logic
873
- ```
874
-
875
- #### 3. Combination Testing (Integration Test)
876
-
877
- ```typescript
878
- // __tests__/src/integration/UserLogin.test.tsx
879
- import { describe, it, expect } from 'vitest';
880
- import { render, screen, fireEvent, waitFor } from '@testing-library/react';
881
- import { LoginPage } from '@/pages/LoginPage';
882
- import { UserService } from '@/base/services/UserService';
883
- import { IOCProvider } from '@/uikit/contexts/IOCContext';
884
-
885
- describe('User Login Flow (Integration Test)', () => {
886
- it('should complete login flow', async () => {
887
- // ✅ Use real Service and Store
888
- const mockApi = {
889
- login: vi.fn().mockResolvedValue({
890
- user: { name: 'John', id: 1 },
891
- token: 'test-token'
892
- })
893
- };
894
-
895
- const userService = new UserService(mockApi, mockConfig, mockStorage);
896
-
897
- const mockIOC = (identifier: string) => {
898
- if (identifier === 'UserServiceInterface') return userService;
899
- };
900
-
901
- // ✅ Render real UI
902
- render(
903
- <IOCProvider value={mockIOC}>
904
- <LoginPage />
905
- </IOCProvider>
906
- );
907
-
908
- // ✅ Simulate user action
909
- const loginButton = screen.getByText('Login');
910
- fireEvent.click(loginButton);
911
-
912
- // ✅ Verify loading state
913
- await waitFor(() => {
914
- expect(screen.getByText('Loading...')).toBeInTheDocument();
915
- });
916
-
917
- // ✅ Verify login success
918
- await waitFor(() => {
919
- expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
920
- expect(userService.getState().user).toEqual({
921
- name: 'John',
922
- id: 1
923
- });
924
- });
925
-
926
- // ✅ Verify API was called
927
- expect(mockApi.login).toHaveBeenCalled();
928
- });
929
- });
930
-
931
- // ✅✅✅ Advantages:
932
- // 1. Test real user flow
933
- // 2. Verify Service and UI integration
934
- // 3. Discover integration issues
935
- ```
936
-
937
- ### Testing Strategy Summary
938
-
939
- ```
940
- ┌────────────────────────────────────────┐
941
- │ Testing Pyramid │
942
- │ │
943
- │ △ UI Tests (10%) │
944
- │ ╱ ╲ │
945
- │ ╱ ╲ Integration Tests (20%) │
946
- │ ╱ ╲ │
947
- │ ╱───────╲ │
948
- │ ╱ ╲ Store + Service Tests (70%)│
949
- │╱═══════════╲ │
950
- │ │
951
- │ Store Tests: Test state change logic │
952
- │ Integration Tests: Test Service + UI │
953
- │ UI Tests: Test UI interaction │
954
- └────────────────────────────────────────┘
955
- ```
956
-
957
- ---
958
-
959
- ## 💎 Best Practices
960
-
961
- ### 1. ✅ Service Extends StoreInterface
962
-
963
- ```typescript
964
- // ✅ Good practice: Service extends StoreInterface
965
- @injectable()
966
- export class UserService extends StoreInterface<UserState> {
967
- constructor() {
968
- super(() => ({
969
- user: null,
970
- loading: false
971
- }));
972
- }
973
-
974
- async login(username: string, password: string) {
975
- this.emit({ ...this.state, loading: true });
976
- // ...
977
- }
978
- }
979
-
980
- // ❌ Bad practice: Service doesn't extend StoreInterface
981
- @injectable()
982
- export class UserService {
983
- private user: UserInfo | null = null;
984
-
985
- // Problem: UI cannot subscribe to state
986
- }
987
- ```
988
-
989
- ### 2. ✅ Use emit to Publish State
990
-
991
- ```typescript
992
- // ✅ Good practice: Publish state via emit
993
- async login(username: string, password: string) {
994
- this.emit({ ...this.state, loading: true });
995
-
996
- const response = await this.api.login({ username, password });
997
-
998
- this.emit({
999
- user: response.user,
1000
- loading: false
1001
- });
1002
- }
1003
-
1004
- // ❌ Bad practice: Directly modify state
1005
- async login(username: string, password: string) {
1006
- this.state.loading = true; // ❌ Won't notify subscribers
1007
-
1008
- const response = await this.api.login({ username, password });
1009
-
1010
- this.state.user = response.user; // ❌ Won't notify subscribers
1011
- }
1012
- ```
1013
-
1014
- ### 3. ✅ Use cloneState to Simplify Updates
1015
-
1016
- ```typescript
1017
- // ✅ Good practice: Use cloneState
1018
- setUser(user: UserInfo) {
1019
- this.emit(this.cloneState({ user }));
1020
- }
1021
-
1022
- setLoading(loading: boolean) {
1023
- this.emit(this.cloneState({ loading }));
1024
- }
1025
-
1026
- // ⚠️ Also acceptable: Manual spread
1027
- setUser(user: UserInfo) {
1028
- this.emit({ ...this.state, user });
1029
- }
1030
- ```
1031
-
1032
- ### 4. ✅ Define Selectors
1033
-
1034
- ```typescript
1035
- // ✅ Good practice: Define selectors
1036
- @injectable()
1037
- export class UserService extends StoreInterface<UserState> {
1038
- selector = {
1039
- user: (state: UserState) => state.user,
1040
- loading: (state: UserState) => state.loading,
1041
- isLoggedIn: (state: UserState) => state.user !== null
1042
- };
1043
- }
1044
-
1045
- // Usage
1046
- const isLoggedIn = useStore(userService, userService.selector.isLoggedIn);
1047
-
1048
- // ❌ Bad practice: Inline selectors
1049
- const isLoggedIn = useStore(userService, (state) => state.user !== null);
1050
- // Problem: Creates new function every render
1051
- ```
1052
-
1053
- ### 5. ✅ Use Selectors for Performance Optimization
1054
-
1055
- ```typescript
1056
- // ✅ Good practice: Only subscribe to needed state
1057
- function UserName() {
1058
- const userService = useIOC('UserServiceInterface');
1059
-
1060
- // Only subscribe to user, loading changes won't trigger re-render
1061
- const user = useStore(userService, (state) => state.user);
1062
-
1063
- return <span>{user?.name}</span>;
1064
- }
1065
-
1066
- // ❌ Bad practice: Subscribe to complete state
1067
- function UserName() {
1068
- const userService = useIOC('UserServiceInterface');
1069
-
1070
- // loading changes will also trigger re-render
1071
- const { user, loading } = useStore(userService);
1072
-
1073
- return <span>{user?.name}</span>;
1074
- }
1075
- ```
1076
-
1077
- ### 6. ✅ Keep State Immutable
1078
-
1079
- ```typescript
1080
- // ✅ Good practice: Create new object
1081
- updateUser(changes: Partial<UserInfo>) {
1082
- this.emit({
1083
- ...this.state,
1084
- user: {
1085
- ...this.state.user,
1086
- ...changes
1087
- }
1088
- });
1089
- }
1090
-
1091
- // ❌ Bad practice: Directly modify object
1092
- updateUser(changes: Partial<UserInfo>) {
1093
- this.state.user.name = changes.name; // ❌ Direct modification
1094
- this.emit(this.state); // ❌ Same reference, may not trigger update
1095
- }
1096
- ```
1097
-
1098
- ### 7. ✅ Reasonably Divide State
1099
-
1100
- ```typescript
1101
- // ✅ Good practice: Each Service manages its own state
1102
- class UserService extends StoreInterface<UserState> {
1103
- // Only manage user-related state
1104
- }
1105
-
1106
- class ThemeService extends StoreInterface<ThemeState> {
1107
- // Only manage theme-related state
1108
- }
1109
-
1110
- class I18nService extends StoreInterface<I18nState> {
1111
- // Only manage i18n-related state
1112
- }
1113
-
1114
- // ❌ Bad practice: Global large Store
1115
- class GlobalStore extends StoreInterface<GlobalState> {
1116
- // Contains all state: user, theme, i18n, etc.
1117
- // Problem: Any state change affects all subscribers
1118
- }
1119
- ```
1120
-
1121
- ---
1122
-
1123
- ## ❓ FAQ
1124
-
1125
- ### Q1: Why not use Redux?
1126
-
1127
- **A:**
1128
-
1129
- | Feature | Redux | Store (SliceStore) |
1130
- | ---------------------- | ------------------------------------- | ---------------------------- |
1131
- | **Complexity** | ❌ High (Action, Reducer, Middleware) | ✅ Low (emit + subscribe) |
1132
- | **Learning Curve** | ❌ Steep | ✅ Gentle |
1133
- | **TypeScript Support** | ⚠️ Needs extra config | ✅ Native support |
1134
- | **IOC Integration** | ⚠️ Needs extra work | ✅ Natural integration |
1135
- | **Performance** | ✅ Good | ✅ Good |
1136
- | **Use Case** | Large applications | Small to medium applications |
1137
-
1138
- **Our Choice:**
1139
-
1140
- - Project already uses IOC, don't need Redux's global state management
1141
- - Each Service manages its own state, clearer
1142
- - SliceStore is simple and powerful enough
1143
-
1144
- ### Q2: What's the difference between Store and React Context?
1145
-
1146
- **A:**
1147
-
1148
- | Feature | React Context | Store |
1149
- | ----------------------- | ---------------------------------- | --------------------------------------- |
1150
- | **Scope** | Component tree | Global (through IOC) |
1151
- | **Performance** | ⚠️ Any value change re-renders all | ✅ Only subscribed value changes render |
1152
- | **Selectors** | ❌ None | ✅ Yes |
1153
- | **Service Integration** | ⚠️ Need manual | ✅ Natural integration |
1154
-
1155
- **Recommendation:**
1156
-
1157
- - Use Store to manage application state (Service state)
1158
- - Use Context to manage UI state (like modals, temporary form data)
1159
-
1160
- ### Q3: How to avoid redundant renders?
1161
-
1162
- **A:** Use selectors
1163
-
1164
- ```typescript
1165
- // ❌ Problem: Subscribe to complete state
1166
- const { user, loading, error } = useStore(userService);
1167
- // loading changes will cause component re-render
1168
-
1169
- // ✅ Solution: Only subscribe to needed state
1170
- const user = useStore(userService, (state) => state.user);
1171
- // Only user changes will cause re-render
1172
- ```
1173
-
1174
- ### Q4: Can I call emit outside Service?
1175
-
1176
- **A:** Not recommended
1177
-
1178
- ```typescript
1179
- // ❌ Bad practice
1180
- function SomeComponent() {
1181
- const userService = useIOC('UserServiceInterface');
1182
-
1183
- // ❌ Directly call emit
1184
- userService.emit({ user: newUser, loading: false });
1185
- }
1186
-
1187
- // ✅ Good practice: Through Service method
1188
- function SomeComponent() {
1189
- const userService = useIOC('UserServiceInterface');
1190
-
1191
- // ✅ Call Service method
1192
- userService.setUser(newUser);
1193
- }
1194
-
1195
- // In Service
1196
- @injectable()
1197
- export class UserService extends StoreInterface<UserState> {
1198
- setUser(user: UserInfo) {
1199
- this.emit(this.cloneState({ user }));
1200
- }
1201
- }
1202
- ```
1203
-
1204
- **Reasons:**
1205
-
1206
- - Maintain encapsulation
1207
- - Easier to test
1208
- - Business logic centralized in Service
1209
-
1210
- ### Q5: Store state update not working?
1211
-
1212
- **A:** Check these points:
1213
-
1214
- ```typescript
1215
- // ❌ Common mistake 1: Directly modify state
1216
- this.state.loading = true; // Won't trigger update
1217
-
1218
- // ✅ Correct: Use emit
1219
- this.emit({ ...this.state, loading: true });
1220
-
1221
- // ❌ Common mistake 2: Not creating new object
1222
- const state = this.state;
1223
- state.loading = true;
1224
- this.emit(state); // Same reference, may not trigger update
1225
-
1226
- // ✅ Correct: Create new object
1227
- this.emit({ ...this.state, loading: true });
1228
-
1229
- // ❌ Common mistake 3: Forgot to subscribe
1230
- function MyComponent() {
1231
- const userService = useIOC('UserServiceInterface');
1232
- // Not calling useStore, cannot receive updates
1233
-
1234
- return <div>{userService.getState().user?.name}</div>;
1235
- }
1236
-
1237
- // ✅ Correct: Use useStore to subscribe
1238
- function MyComponent() {
1239
- const userService = useIOC('UserServiceInterface');
1240
- const user = useStore(userService, (state) => state.user);
1241
-
1242
- return <div>{user?.name}</div>;
1243
- }
1244
- ```
1245
-
1246
- ### Q6: How to share state between Services?
1247
-
1248
- **A:** Through IOC injection
1249
-
1250
- ```typescript
1251
- // Service A
1252
- @injectable()
1253
- export class UserService extends StoreInterface<UserState> {
1254
- // ...
1255
- }
1256
-
1257
- // Service B depends on Service A
1258
- @injectable()
1259
- export class ProfileService {
1260
- constructor(
1261
- @inject('UserServiceInterface')
1262
- private userService: UserService
1263
- ) {}
1264
-
1265
- async updateProfile(data: ProfileData) {
1266
- // ✅ Access UserService state
1267
- const user = this.userService.getState().user;
1268
-
1269
- // ✅ Can also subscribe to UserService state
1270
- this.userService.subscribe((state) => {
1271
- console.log('User state changed:', state);
1272
- });
1273
- }
1274
- }
1275
- ```
1276
-
1277
- ---
1278
-
1279
- ## 📚 Related Documentation
1280
-
1281
- - [Project Architecture Design](./index.md) - Understand overall architecture
1282
- - [IOC Container](./ioc.md) - Dependency injection and UI separation
1283
- - [Bootstrap Initializer](./bootstrap.md) - Application startup and initialization
1284
- - [Testing Guide](./test-guide.md) - Detailed testing strategies
1285
-
1286
- ---
1287
-
1288
- ## 🎉 Summary
1289
-
1290
- Core value of Store state management:
1291
-
1292
- 1. **Solve Communication Problem** 📡 - Application layer notifies UI layer while maintaining separation
1293
- 2. **Publish-Subscribe Pattern** 🔔 - Service emits, UI uses useStore
1294
- 3. **Auto-update UI** ⚡ - UI automatically re-renders when state changes
1295
- 4. **Maintain Decoupling** 🔗 - Service doesn't know which UIs are listening
1296
- 5. **Easy to Test** 🧪 - Store can be tested independently
1297
- 6. **Performance Optimization** 🚀 - Selectors only subscribe to needed state
1298
- 7. **Type Safety** 🔒 - Full TypeScript support
1299
-
1300
- **Remember the core pattern:**
1301
-
1302
- ```typescript
1303
- // 1. Service extends StoreInterface
1304
- class MyService extends StoreInterface<MyState> {
1305
- // 2. Publish state via emit
1306
- doSomething() {
1307
- this.emit({ ...this.state, data: newData });
1308
- }
1309
- }
1310
-
1311
- // 3. UI subscribes to state via useStore
1312
- function MyComponent() {
1313
- const myService = useIOC('MyServiceInterface');
1314
- const data = useStore(myService, (state) => state.data);
1315
-
1316
- return <div>{data}</div>;
1317
- }
1318
- ```
1319
-
1320
- **Core Principles:**
1321
-
1322
- - ✅ Service publishes state via emit
1323
- - ✅ UI subscribes to state via useStore
1324
- - ✅ Use selectors for performance optimization
1325
- - ✅ Keep state immutable
1326
- - ✅ Each Service manages its own state
1327
-
1328
- ---
1329
-
1330
- **Feedback:**
1331
- If you have any questions or suggestions about Store state management, please discuss in the team channel or submit an Issue.