@stone-js/starters 0.8.3

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 (352) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +101 -0
  3. package/basic-react-declarative/LICENSE +21 -0
  4. package/basic-react-declarative/README.md +39 -0
  5. package/basic-react-declarative/app/Application.tsx +89 -0
  6. package/basic-react-declarative/assets/css/index.css +26 -0
  7. package/basic-react-declarative/package.json +39 -0
  8. package/basic-react-declarative/public/logo.png +0 -0
  9. package/basic-react-declarative/tests/Application.spec.ts +96 -0
  10. package/basic-react-declarative/tests/__snapshots__/Application.spec.ts.snap +3 -0
  11. package/basic-react-declarative/tsconfig.json +33 -0
  12. package/basic-react-declarative/vitest.config.ts +24 -0
  13. package/basic-react-imperative/LICENSE +21 -0
  14. package/basic-react-imperative/README.md +39 -0
  15. package/basic-react-imperative/app/Application.tsx +89 -0
  16. package/basic-react-imperative/assets/css/index.css +26 -0
  17. package/basic-react-imperative/package.json +39 -0
  18. package/basic-react-imperative/public/logo.png +0 -0
  19. package/basic-react-imperative/tests/Application.spec.ts +80 -0
  20. package/basic-react-imperative/tests/__snapshots__/Application.spec.ts.snap +3 -0
  21. package/basic-react-imperative/tsconfig.json +33 -0
  22. package/basic-react-imperative/vitest.config.ts +24 -0
  23. package/basic-service-declarative/LICENSE +21 -0
  24. package/basic-service-declarative/README.md +39 -0
  25. package/basic-service-declarative/app/Application.ts +56 -0
  26. package/basic-service-declarative/package.json +34 -0
  27. package/basic-service-declarative/tests/Application.spec.ts +60 -0
  28. package/basic-service-declarative/tsconfig.json +25 -0
  29. package/basic-service-declarative/vitest.config.ts +24 -0
  30. package/basic-service-imperative/LICENSE +21 -0
  31. package/basic-service-imperative/README.md +39 -0
  32. package/basic-service-imperative/app/Application.ts +60 -0
  33. package/basic-service-imperative/package.json +34 -0
  34. package/basic-service-imperative/tests/Application.spec.ts +52 -0
  35. package/basic-service-imperative/tsconfig.json +25 -0
  36. package/basic-service-imperative/vitest.config.ts +24 -0
  37. package/continuum-showcase/README.md +44 -0
  38. package/continuum-showcase/app/env.d.ts +34 -0
  39. package/continuum-showcase/app/layouts/MainLayout.tsx +20 -0
  40. package/continuum-showcase/app/pages/BlogPostPage.tsx +62 -0
  41. package/continuum-showcase/app/pages/HomePage.tsx +58 -0
  42. package/continuum-showcase/package.json +25 -0
  43. package/continuum-showcase/stone.config.mjs +20 -0
  44. package/full-react-declarative/.env.example +1 -0
  45. package/full-react-declarative/.env.public.example +1 -0
  46. package/full-react-declarative/LICENSE +21 -0
  47. package/full-react-declarative/README.md +39 -0
  48. package/full-react-declarative/app/Application.ts +85 -0
  49. package/full-react-declarative/app/clients/AxiosClient.ts +136 -0
  50. package/full-react-declarative/app/clients/CommentClient.ts +61 -0
  51. package/full-react-declarative/app/clients/PostClient.ts +90 -0
  52. package/full-react-declarative/app/clients/SecurityClient.ts +66 -0
  53. package/full-react-declarative/app/clients/UserClient.ts +97 -0
  54. package/full-react-declarative/app/components/ChangePasswordForm/ChangePasswordForm.tsx +50 -0
  55. package/full-react-declarative/app/components/CommentForm/CommentForm.tsx +37 -0
  56. package/full-react-declarative/app/components/CommentItem/CommentItem.tsx +42 -0
  57. package/full-react-declarative/app/components/CommentWidget/CommentWidget.tsx +95 -0
  58. package/full-react-declarative/app/components/LoginForm/LoginForm.css +0 -0
  59. package/full-react-declarative/app/components/LoginForm/LoginForm.tsx +71 -0
  60. package/full-react-declarative/app/components/PostDetails/PostDetails.tsx +44 -0
  61. package/full-react-declarative/app/components/PostForm/PostForm.css +0 -0
  62. package/full-react-declarative/app/components/PostForm/PostForm.tsx +47 -0
  63. package/full-react-declarative/app/components/PostItem/PostItem.css +0 -0
  64. package/full-react-declarative/app/components/PostItem/PostItem.tsx +34 -0
  65. package/full-react-declarative/app/components/RegisterForm/RegisterForm.tsx +110 -0
  66. package/full-react-declarative/app/components/UserForm/UserForm.tsx +47 -0
  67. package/full-react-declarative/app/configurations/AppConfiguration.ts +17 -0
  68. package/full-react-declarative/app/error-pages/DefaultErrorPage/DefaultErrorPage.css +0 -0
  69. package/full-react-declarative/app/error-pages/DefaultErrorPage/DefaultErrorPage.tsx +52 -0
  70. package/full-react-declarative/app/error-pages/ForbiddenErrorPage/ForbiddenErrorPage.css +0 -0
  71. package/full-react-declarative/app/error-pages/ForbiddenErrorPage/ForbiddenErrorPage.tsx +53 -0
  72. package/full-react-declarative/app/error-pages/NotFoundErrorPage/NotFoundErrorPage.css +0 -0
  73. package/full-react-declarative/app/error-pages/NotFoundErrorPage/NotFoundErrorPage.tsx +54 -0
  74. package/full-react-declarative/app/error-pages/UnauthorizedErrorPage/UnauthorizedErrorPage.css +0 -0
  75. package/full-react-declarative/app/error-pages/UnauthorizedErrorPage/UnauthorizedErrorPage.tsx +53 -0
  76. package/full-react-declarative/app/errors/ForbiddenError.ts +11 -0
  77. package/full-react-declarative/app/errors/PostNotFoundError.ts +11 -0
  78. package/full-react-declarative/app/errors/UnauthorizedError.ts +11 -0
  79. package/full-react-declarative/app/errors/UserNotFoundError.ts +11 -0
  80. package/full-react-declarative/app/events/UserEvent.ts +18 -0
  81. package/full-react-declarative/app/layout/AppLayout/AppLayout.css +20 -0
  82. package/full-react-declarative/app/layout/AppLayout/AppLayout.tsx +101 -0
  83. package/full-react-declarative/app/layout/ErrorLayout/ErrorLayout.css +0 -0
  84. package/full-react-declarative/app/layout/ErrorLayout/ErrorLayout.tsx +41 -0
  85. package/full-react-declarative/app/layout/SecurityLayout/SecurityLayout.css +0 -0
  86. package/full-react-declarative/app/layout/SecurityLayout/SecurityLayout.tsx +39 -0
  87. package/full-react-declarative/app/middleware/AuthMiddleware.ts +85 -0
  88. package/full-react-declarative/app/models/Comment.ts +31 -0
  89. package/full-react-declarative/app/models/Post.ts +22 -0
  90. package/full-react-declarative/app/models/User.ts +62 -0
  91. package/full-react-declarative/app/pages/home/HomePage.tsx +44 -0
  92. package/full-react-declarative/app/pages/login/LoginPage.css +0 -0
  93. package/full-react-declarative/app/pages/login/LoginPage.tsx +73 -0
  94. package/full-react-declarative/app/pages/posts/CreatePostPage/CreatePostPage.css +0 -0
  95. package/full-react-declarative/app/pages/posts/CreatePostPage/CreatePostPage.tsx +67 -0
  96. package/full-react-declarative/app/pages/posts/PostPage/PostPage.tsx +60 -0
  97. package/full-react-declarative/app/pages/posts/ShowPostPage/ShowPostPage.tsx +83 -0
  98. package/full-react-declarative/app/pages/posts/UpdatePostPage/UpdatePostPage.tsx +77 -0
  99. package/full-react-declarative/app/pages/register/RegisterPage.tsx +68 -0
  100. package/full-react-declarative/app/pages/users/CreateUserPage.tsx +48 -0
  101. package/full-react-declarative/app/pages/users/ShowUserPage.tsx +62 -0
  102. package/full-react-declarative/app/pages/users/UpdateUserPage.tsx +57 -0
  103. package/full-react-declarative/app/pages/users/UserPage.tsx +57 -0
  104. package/full-react-declarative/app/providers/AppServiceProvider.ts +43 -0
  105. package/full-react-declarative/app/services/CommentService.ts +59 -0
  106. package/full-react-declarative/app/services/PostService.ts +108 -0
  107. package/full-react-declarative/app/services/SecurityService.ts +80 -0
  108. package/full-react-declarative/app/services/TokenService.ts +116 -0
  109. package/full-react-declarative/app/services/UserService.ts +126 -0
  110. package/full-react-declarative/app/utils.ts +17 -0
  111. package/full-react-declarative/assets/css/index.css +301 -0
  112. package/full-react-declarative/assets/img/logo.png +0 -0
  113. package/full-react-declarative/package.json +46 -0
  114. package/full-react-declarative/public/logo.png +0 -0
  115. package/full-react-declarative/public/vite.svg +1 -0
  116. package/full-react-declarative/stone.config.mjs +12 -0
  117. package/full-react-declarative/tsconfig.json +29 -0
  118. package/full-react-declarative/vitest.config.ts +24 -0
  119. package/full-react-imperative/.env.example +1 -0
  120. package/full-react-imperative/.env.public.example +1 -0
  121. package/full-react-imperative/LICENSE +21 -0
  122. package/full-react-imperative/README.md +39 -0
  123. package/full-react-imperative/app/Application.ts +79 -0
  124. package/full-react-imperative/app/clients/AxiosClient.ts +124 -0
  125. package/full-react-imperative/app/clients/CommentClient.ts +57 -0
  126. package/full-react-imperative/app/clients/PostClient.ts +86 -0
  127. package/full-react-imperative/app/clients/SecurityClient.ts +60 -0
  128. package/full-react-imperative/app/clients/UserClient.ts +93 -0
  129. package/full-react-imperative/app/clients/contracts/IAxiosClient.ts +97 -0
  130. package/full-react-imperative/app/clients/contracts/ICommentClient.ts +31 -0
  131. package/full-react-imperative/app/clients/contracts/IPostClient.ts +55 -0
  132. package/full-react-imperative/app/clients/contracts/ISecurityClient.ts +33 -0
  133. package/full-react-imperative/app/clients/contracts/IUserClient.ts +61 -0
  134. package/full-react-imperative/app/components/ChangePasswordForm/ChangePasswordForm.tsx +50 -0
  135. package/full-react-imperative/app/components/CommentForm/CommentForm.css +35 -0
  136. package/full-react-imperative/app/components/CommentForm/CommentForm.tsx +45 -0
  137. package/full-react-imperative/app/components/CommentItem/CommentItem.css +74 -0
  138. package/full-react-imperative/app/components/CommentItem/CommentItem.tsx +62 -0
  139. package/full-react-imperative/app/components/CommentWidget/CommentWidget.css +25 -0
  140. package/full-react-imperative/app/components/CommentWidget/CommentWidget.tsx +119 -0
  141. package/full-react-imperative/app/components/Dropdown/Dropdown.css +52 -0
  142. package/full-react-imperative/app/components/Dropdown/Dropdown.tsx +40 -0
  143. package/full-react-imperative/app/components/LayoutHeader/LayoutHeader.css +62 -0
  144. package/full-react-imperative/app/components/LayoutHeader/LayoutHeader.tsx +61 -0
  145. package/full-react-imperative/app/components/LayoutLeftMenu/LayoutLeftMenu.css +25 -0
  146. package/full-react-imperative/app/components/LayoutLeftMenu/LayoutLeftMenu.tsx +32 -0
  147. package/full-react-imperative/app/components/LoginForm/LoginForm.css +0 -0
  148. package/full-react-imperative/app/components/LoginForm/LoginForm.tsx +71 -0
  149. package/full-react-imperative/app/components/NotificationBadge/NotificationBadge.css +41 -0
  150. package/full-react-imperative/app/components/NotificationBadge/NotificationBadge.tsx +63 -0
  151. package/full-react-imperative/app/components/PostCard/PostCard.css +129 -0
  152. package/full-react-imperative/app/components/PostCard/PostCard.tsx +65 -0
  153. package/full-react-imperative/app/components/PostForm/PostForm.css +92 -0
  154. package/full-react-imperative/app/components/PostForm/PostForm.tsx +76 -0
  155. package/full-react-imperative/app/components/PostSkeleton/PostSkeleton.css +65 -0
  156. package/full-react-imperative/app/components/PostSkeleton/PostSkeleton.tsx +25 -0
  157. package/full-react-imperative/app/components/PostWidget/PostWidget.tsx +95 -0
  158. package/full-react-imperative/app/components/ProfileForm/ProfileForm.css +84 -0
  159. package/full-react-imperative/app/components/ProfileForm/ProfileForm.tsx +79 -0
  160. package/full-react-imperative/app/components/RegisterForm/RegisterForm.tsx +110 -0
  161. package/full-react-imperative/app/components/RightAsidePanel/RightAsidePanel.css +32 -0
  162. package/full-react-imperative/app/components/RightAsidePanel/RightAsidePanel.tsx +62 -0
  163. package/full-react-imperative/app/components/SettingsForm/SettingsForm.css +126 -0
  164. package/full-react-imperative/app/components/SettingsForm/SettingsForm.tsx +86 -0
  165. package/full-react-imperative/app/components/UserAvatar/UserAvatar.css +18 -0
  166. package/full-react-imperative/app/components/UserAvatar/UserAvatar.tsx +60 -0
  167. package/full-react-imperative/app/components/UserBadge/UserBadge.css +33 -0
  168. package/full-react-imperative/app/components/UserBadge/UserBadge.tsx +49 -0
  169. package/full-react-imperative/app/components/UserCard/UserCard.css +70 -0
  170. package/full-react-imperative/app/components/UserCard/UserCard.tsx +43 -0
  171. package/full-react-imperative/app/configurations/AppConfiguration.ts +19 -0
  172. package/full-react-imperative/app/error-pages/DefaultErrorPage/DefaultErrorPage.css +0 -0
  173. package/full-react-imperative/app/error-pages/DefaultErrorPage/DefaultErrorPage.tsx +41 -0
  174. package/full-react-imperative/app/error-pages/ForbiddenErrorPage/ForbiddenErrorPage.css +0 -0
  175. package/full-react-imperative/app/error-pages/ForbiddenErrorPage/ForbiddenErrorPage.tsx +42 -0
  176. package/full-react-imperative/app/error-pages/NotFoundErrorPage/NotFoundErrorPage.css +0 -0
  177. package/full-react-imperative/app/error-pages/NotFoundErrorPage/NotFoundErrorPage.tsx +49 -0
  178. package/full-react-imperative/app/error-pages/UnauthorizedErrorPage/UnauthorizedErrorPage.css +0 -0
  179. package/full-react-imperative/app/error-pages/UnauthorizedErrorPage/UnauthorizedErrorPage.tsx +42 -0
  180. package/full-react-imperative/app/errors/ForbiddenError.ts +11 -0
  181. package/full-react-imperative/app/errors/PostNotFoundError.ts +11 -0
  182. package/full-react-imperative/app/errors/UnauthorizedError.ts +11 -0
  183. package/full-react-imperative/app/errors/UserNotFoundError.ts +11 -0
  184. package/full-react-imperative/app/events/UserEvent.ts +18 -0
  185. package/full-react-imperative/app/layout/AppLayout/AppLayout.css +106 -0
  186. package/full-react-imperative/app/layout/AppLayout/AppLayout.tsx +48 -0
  187. package/full-react-imperative/app/layout/ErrorLayout/ErrorLayout.css +0 -0
  188. package/full-react-imperative/app/layout/ErrorLayout/ErrorLayout.tsx +45 -0
  189. package/full-react-imperative/app/layout/SecurityLayout/SecurityLayout.css +0 -0
  190. package/full-react-imperative/app/layout/SecurityLayout/SecurityLayout.tsx +43 -0
  191. package/full-react-imperative/app/layout/SettingsLayout/SettingsLayout.css +99 -0
  192. package/full-react-imperative/app/layout/SettingsLayout/SettingsLayout.tsx +44 -0
  193. package/full-react-imperative/app/middleware/AuthMiddleware.ts +56 -0
  194. package/full-react-imperative/app/models/Comment.ts +35 -0
  195. package/full-react-imperative/app/models/Post.ts +27 -0
  196. package/full-react-imperative/app/models/User.ts +68 -0
  197. package/full-react-imperative/app/pages/Settings/SettingsPage.tsx +23 -0
  198. package/full-react-imperative/app/pages/home/HomePage.tsx +58 -0
  199. package/full-react-imperative/app/pages/login/LoginPage.css +0 -0
  200. package/full-react-imperative/app/pages/login/LoginPage.tsx +65 -0
  201. package/full-react-imperative/app/pages/posts/ShowPostPage/ShowPostPage.tsx +64 -0
  202. package/full-react-imperative/app/pages/posts/UpdatePostPage/UpdatePostPage.tsx +56 -0
  203. package/full-react-imperative/app/pages/register/RegisterPage.tsx +71 -0
  204. package/full-react-imperative/app/pages/users/ShowUserPage/ShowUserPage.css +64 -0
  205. package/full-react-imperative/app/pages/users/ShowUserPage/ShowUserPage.tsx +104 -0
  206. package/full-react-imperative/app/pages/users/UpdateUserPage/UpdateUserPage.css +0 -0
  207. package/full-react-imperative/app/pages/users/UpdateUserPage/UpdateUserPage.tsx +43 -0
  208. package/full-react-imperative/app/pages/users/UserPage/UserPage.css +31 -0
  209. package/full-react-imperative/app/pages/users/UserPage/UserPage.tsx +59 -0
  210. package/full-react-imperative/app/pages/utils.ts +67 -0
  211. package/full-react-imperative/app/providers/AppServiceProvider.ts +40 -0
  212. package/full-react-imperative/app/services/CommentService.ts +53 -0
  213. package/full-react-imperative/app/services/PostService.ts +102 -0
  214. package/full-react-imperative/app/services/SecurityService.ts +72 -0
  215. package/full-react-imperative/app/services/TokenService.ts +108 -0
  216. package/full-react-imperative/app/services/UserService.ts +115 -0
  217. package/full-react-imperative/app/services/contracts/ICommentService.ts +30 -0
  218. package/full-react-imperative/app/services/contracts/IPostService.ts +65 -0
  219. package/full-react-imperative/app/services/contracts/ISecurityService.ts +39 -0
  220. package/full-react-imperative/app/services/contracts/ITokenService.ts +44 -0
  221. package/full-react-imperative/app/services/contracts/IUserService.ts +70 -0
  222. package/full-react-imperative/app/utils.ts +31 -0
  223. package/full-react-imperative/assets/css/index.css +301 -0
  224. package/full-react-imperative/assets/img/logo.png +0 -0
  225. package/full-react-imperative/package.json +48 -0
  226. package/full-react-imperative/public/logo.png +0 -0
  227. package/full-react-imperative/public/vite.svg +1 -0
  228. package/full-react-imperative/stone.config.mjs +12 -0
  229. package/full-react-imperative/tsconfig.json +29 -0
  230. package/full-react-imperative/vitest.config.ts +24 -0
  231. package/full-service-declarative/.env.example +2 -0
  232. package/full-service-declarative/LICENSE +21 -0
  233. package/full-service-declarative/README.md +65 -0
  234. package/full-service-declarative/app/Application.ts +327 -0
  235. package/full-service-declarative/app/commands/ListSessionsCommand.ts +70 -0
  236. package/full-service-declarative/app/commands/ListUsersCommand.ts +56 -0
  237. package/full-service-declarative/app/configurations/AppConfiguration.ts +52 -0
  238. package/full-service-declarative/app/configurations/UserLiveConfiguration.ts +28 -0
  239. package/full-service-declarative/app/database/DatabaseClient.ts +43 -0
  240. package/full-service-declarative/app/database/schema.ts +43 -0
  241. package/full-service-declarative/app/error-handlers/SecurityErrorHandler.ts +61 -0
  242. package/full-service-declarative/app/errors/CredentialsError.ts +12 -0
  243. package/full-service-declarative/app/events/UserEvent.ts +17 -0
  244. package/full-service-declarative/app/handlers/CommentEventHandler.ts +103 -0
  245. package/full-service-declarative/app/handlers/PostEventHandler.ts +104 -0
  246. package/full-service-declarative/app/handlers/SecurityEventHandler.ts +108 -0
  247. package/full-service-declarative/app/handlers/UserEventHandler.ts +159 -0
  248. package/full-service-declarative/app/listeners/UserEventListener.ts +35 -0
  249. package/full-service-declarative/app/middleware/AuthMiddleware.ts +54 -0
  250. package/full-service-declarative/app/models/Comment.ts +38 -0
  251. package/full-service-declarative/app/models/Post.ts +50 -0
  252. package/full-service-declarative/app/models/Session.ts +14 -0
  253. package/full-service-declarative/app/models/User.ts +77 -0
  254. package/full-service-declarative/app/providers/DrizzleServiceProvider.ts +59 -0
  255. package/full-service-declarative/app/repositories/CommentRepository.ts +155 -0
  256. package/full-service-declarative/app/repositories/PostRepository.ts +138 -0
  257. package/full-service-declarative/app/repositories/SessionRepository.ts +123 -0
  258. package/full-service-declarative/app/repositories/UserRepository.ts +99 -0
  259. package/full-service-declarative/app/services/CommentService.ts +112 -0
  260. package/full-service-declarative/app/services/PostService.ts +100 -0
  261. package/full-service-declarative/app/services/SecurityService.ts +232 -0
  262. package/full-service-declarative/app/services/SessionService.ts +130 -0
  263. package/full-service-declarative/app/services/UserService.ts +110 -0
  264. package/full-service-declarative/app/subscribers/UserEventSubscriber.ts +38 -0
  265. package/full-service-declarative/drizzle.config.ts +13 -0
  266. package/full-service-declarative/package.json +48 -0
  267. package/full-service-declarative/stone.config.mjs +12 -0
  268. package/full-service-declarative/tsconfig.json +25 -0
  269. package/full-service-declarative/vitest.config.ts +24 -0
  270. package/full-service-imperative/.env.example +2 -0
  271. package/full-service-imperative/LICENSE +21 -0
  272. package/full-service-imperative/README.md +65 -0
  273. package/full-service-imperative/app/Application.ts +317 -0
  274. package/full-service-imperative/app/commands/ListSessionsCommand.ts +54 -0
  275. package/full-service-imperative/app/commands/ListUsersCommand.ts +42 -0
  276. package/full-service-imperative/app/configurations/AppConfiguration.ts +44 -0
  277. package/full-service-imperative/app/configurations/UserLiveConfiguration.ts +26 -0
  278. package/full-service-imperative/app/database/DatabaseClient.ts +39 -0
  279. package/full-service-imperative/app/database/schema.ts +43 -0
  280. package/full-service-imperative/app/error-handlers/SecurityErrorHandler.ts +58 -0
  281. package/full-service-imperative/app/errors/CredentialsError.ts +12 -0
  282. package/full-service-imperative/app/events/UserEvent.ts +17 -0
  283. package/full-service-imperative/app/handlers/CommentEventHandler.ts +99 -0
  284. package/full-service-imperative/app/handlers/PostEventHandler.ts +99 -0
  285. package/full-service-imperative/app/handlers/SecurityEventHandler.ts +83 -0
  286. package/full-service-imperative/app/handlers/UserEventHandler.ts +132 -0
  287. package/full-service-imperative/app/listeners/UserEventListener.ts +23 -0
  288. package/full-service-imperative/app/middleware/AuthMiddleware.ts +44 -0
  289. package/full-service-imperative/app/models/Comment.ts +38 -0
  290. package/full-service-imperative/app/models/Post.ts +50 -0
  291. package/full-service-imperative/app/models/Session.ts +14 -0
  292. package/full-service-imperative/app/models/User.ts +77 -0
  293. package/full-service-imperative/app/providers/DrizzleServiceProvider.ts +56 -0
  294. package/full-service-imperative/app/repositories/CommentRepository.ts +148 -0
  295. package/full-service-imperative/app/repositories/PostRepository.ts +130 -0
  296. package/full-service-imperative/app/repositories/SessionRepository.ts +115 -0
  297. package/full-service-imperative/app/repositories/UserRepository.ts +91 -0
  298. package/full-service-imperative/app/repositories/contracts/ICommentRepository.ts +72 -0
  299. package/full-service-imperative/app/repositories/contracts/IPostRepository.ts +63 -0
  300. package/full-service-imperative/app/repositories/contracts/ISessionRepository.ts +65 -0
  301. package/full-service-imperative/app/repositories/contracts/IUserRepository.ts +55 -0
  302. package/full-service-imperative/app/services/CommentService.ts +109 -0
  303. package/full-service-imperative/app/services/PostService.ts +97 -0
  304. package/full-service-imperative/app/services/SecurityService.ts +224 -0
  305. package/full-service-imperative/app/services/SessionService.ts +124 -0
  306. package/full-service-imperative/app/services/UserService.ts +105 -0
  307. package/full-service-imperative/app/services/contracts/ICommentService.ts +62 -0
  308. package/full-service-imperative/app/services/contracts/IPostService.ts +53 -0
  309. package/full-service-imperative/app/services/contracts/ISecurityService.ts +97 -0
  310. package/full-service-imperative/app/services/contracts/ISessionService.ts +73 -0
  311. package/full-service-imperative/app/services/contracts/IUserService.ts +57 -0
  312. package/full-service-imperative/app/subscribers/UserEventSubscriber.ts +28 -0
  313. package/full-service-imperative/drizzle.config.ts +13 -0
  314. package/full-service-imperative/package.json +48 -0
  315. package/full-service-imperative/stone.config.mjs +12 -0
  316. package/full-service-imperative/tsconfig.json +25 -0
  317. package/full-service-imperative/vitest.config.ts +24 -0
  318. package/package.json +225 -0
  319. package/standard-react-declarative/LICENSE +21 -0
  320. package/standard-react-declarative/README.md +39 -0
  321. package/standard-react-declarative/app/Application.ts +45 -0
  322. package/standard-react-declarative/app/pages/welcome/WelcomePage.tsx +57 -0
  323. package/standard-react-declarative/app/services/WelcomeService.ts +38 -0
  324. package/standard-react-declarative/assets/css/index.css +26 -0
  325. package/standard-react-declarative/package.json +42 -0
  326. package/standard-react-declarative/tsconfig.json +33 -0
  327. package/standard-react-declarative/vitest.config.ts +24 -0
  328. package/standard-react-imperative/LICENSE +21 -0
  329. package/standard-react-imperative/README.md +39 -0
  330. package/standard-react-imperative/app/Application.ts +41 -0
  331. package/standard-react-imperative/app/pages/welcome/WelcomePage.tsx +50 -0
  332. package/standard-react-imperative/app/services/WelcomeService.ts +38 -0
  333. package/standard-react-imperative/assets/css/index.css +26 -0
  334. package/standard-react-imperative/package.json +42 -0
  335. package/standard-react-imperative/tsconfig.json +33 -0
  336. package/standard-react-imperative/vitest.config.ts +24 -0
  337. package/standard-service-declarative/LICENSE +21 -0
  338. package/standard-service-declarative/README.md +39 -0
  339. package/standard-service-declarative/app/Application.ts +39 -0
  340. package/standard-service-declarative/app/handlers/WelcomeEventHandler.ts +45 -0
  341. package/standard-service-declarative/app/services/WelcomeService.ts +38 -0
  342. package/standard-service-declarative/package.json +36 -0
  343. package/standard-service-declarative/tsconfig.json +25 -0
  344. package/standard-service-declarative/vitest.config.ts +24 -0
  345. package/standard-service-imperative/LICENSE +21 -0
  346. package/standard-service-imperative/README.md +39 -0
  347. package/standard-service-imperative/app/Application.ts +11 -0
  348. package/standard-service-imperative/app/handlers/welcomeEventHandler.ts +31 -0
  349. package/standard-service-imperative/app/services/welcomeService.ts +38 -0
  350. package/standard-service-imperative/package.json +35 -0
  351. package/standard-service-imperative/tsconfig.json +25 -0
  352. package/standard-service-imperative/vitest.config.ts +24 -0
@@ -0,0 +1,112 @@
1
+ import { NotFoundError } from '@stone-js/http-core'
2
+ import { Comment, CommentModel } from '../models/Comment'
3
+ import { IContainer, isNotEmpty, Service } from '@stone-js/core'
4
+ import { CommentRepository } from '../repositories/CommentRepository'
5
+
6
+ /**
7
+ * Comment Service Options
8
+ */
9
+ export interface CommentServiceOptions {
10
+ commentRepository: CommentRepository
11
+ }
12
+
13
+ /**
14
+ * Comment Service
15
+ *
16
+ * Manages comment-related business logic.
17
+ */
18
+ @Service({ alias: 'commentService' })
19
+ export class CommentService {
20
+ private readonly commentRepository: CommentRepository
21
+
22
+ /**
23
+ * Resolve route binding.
24
+ *
25
+ * @param _key - The key of the binding.
26
+ * @param value - The value of the binding.
27
+ * @param container - The container instance.
28
+ */
29
+ static async resolveRouteBinding (_key: string, value: any, container: IContainer): Promise<Comment | undefined> {
30
+ const commentService = container.resolve<CommentService>('commentService')
31
+ return await commentService.findById(Number(value))
32
+ }
33
+
34
+ /**
35
+ * Create a new Comment Service.
36
+ */
37
+ constructor ({ commentRepository }: CommentServiceOptions) {
38
+ this.commentRepository = commentRepository
39
+ }
40
+
41
+ /**
42
+ * List comments.
43
+ *
44
+ * @param limit - The limit of comments to list.
45
+ */
46
+ async list (limit: number = 10): Promise<Comment[]> {
47
+ return await this.commentRepository.list({ limit })
48
+ }
49
+
50
+ /**
51
+ * Retrieve comments for a specific post.
52
+ *
53
+ * @param postId - The post ID.
54
+ * @param limit - The number of comments to retrieve.
55
+ */
56
+ async listByPost (postId: number, limit: number = 10): Promise<Comment[]> {
57
+ return await this.commentRepository.list({ postId, limit })
58
+ }
59
+
60
+ /**
61
+ * Retrieve comments by a specific user.
62
+ *
63
+ * @param authorId - The author ID.
64
+ * @param limit - The number of comments to retrieve.
65
+ */
66
+ async listByAuthor (authorId: number, limit: number = 10): Promise<Comment[]> {
67
+ return await this.commentRepository.list({ authorId, limit })
68
+ }
69
+
70
+ /**
71
+ * Find a comment by ID.
72
+ *
73
+ * @param id - The ID of the comment.
74
+ * @returns The found comment.
75
+ */
76
+ async findById (id: number): Promise<Comment> {
77
+ const comment = await this.commentRepository.findById(id)
78
+ if (isNotEmpty<CommentModel>(comment)) return comment
79
+ throw new NotFoundError(`Comment with ID ${id} not found`)
80
+ }
81
+
82
+ /**
83
+ * Create a new comment.
84
+ *
85
+ * @param payload - The comment data to create.
86
+ */
87
+ async create (payload: Omit<CommentModel, 'id'>): Promise<bigint | undefined> {
88
+ return await this.commentRepository.create({ ...payload, createdAt: Date.now() })
89
+ }
90
+
91
+ /**
92
+ * Update an existing comment.
93
+ *
94
+ * @param id - The ID of the comment.
95
+ * @param payload - The comment data to update.
96
+ * @returns The updated comment.
97
+ */
98
+ async update (id: number, payload: Partial<CommentModel>): Promise<Comment> {
99
+ const comment = await this.commentRepository.update(id, payload)
100
+ if (isNotEmpty<CommentModel>(comment)) return comment
101
+ throw new NotFoundError(`Comment with ID ${id} not found`)
102
+ }
103
+
104
+ /**
105
+ * Delete a comment.
106
+ *
107
+ * @param id - The ID of the comment.
108
+ */
109
+ async delete (id: number): Promise<boolean> {
110
+ return await this.commentRepository.delete(id)
111
+ }
112
+ }
@@ -0,0 +1,100 @@
1
+ import { NotFoundError } from '@stone-js/http-core'
2
+ import { PostRepository } from '../repositories/PostRepository'
3
+ import { IContainer, isNotEmpty, Service } from '@stone-js/core'
4
+ import { Post, PostCreateModel, PostModel } from '../models/Post'
5
+
6
+ /**
7
+ * Post Service Options
8
+ */
9
+ export interface PostServiceOptions {
10
+ postRepository: PostRepository
11
+ }
12
+
13
+ /**
14
+ * Post Service
15
+ *
16
+ * Manages post-related business logic.
17
+ */
18
+ @Service({ alias: 'postService' })
19
+ export class PostService {
20
+ private readonly postRepository: PostRepository
21
+
22
+ /**
23
+ * Resolve route binding.
24
+ *
25
+ * @param _key - The key of the binding.
26
+ * @param value - The value of the binding.
27
+ * @param container - The container instance.
28
+ */
29
+ static async resolveRouteBinding (_key: string, value: any, container: IContainer): Promise<Post | undefined> {
30
+ const postService = container.resolve<PostService>('postService')
31
+ return await postService.findById(Number(value))
32
+ }
33
+
34
+ /**
35
+ * Create a new Post Service.
36
+ */
37
+ constructor ({ postRepository }: PostServiceOptions) {
38
+ this.postRepository = postRepository
39
+ }
40
+
41
+ /**
42
+ * List posts.
43
+ *
44
+ * @param limit - The limit of posts to list.
45
+ */
46
+ async list (limit: number = 10): Promise<Post[]> {
47
+ return await this.postRepository.list({ limit })
48
+ }
49
+
50
+ /**
51
+ * Retrieve posts by a specific user.
52
+ *
53
+ * @param authorId - The author ID.
54
+ * @param limit - The number of posts to retrieve.
55
+ */
56
+ async listByAuthor (authorId: number, limit: number = 10): Promise<Post[]> {
57
+ return await this.postRepository.listByAuthor(authorId, limit)
58
+ }
59
+
60
+ /**
61
+ * Find a post by ID.
62
+ *
63
+ * @param id - The ID of the post.
64
+ * @returns The found post.
65
+ */
66
+ async findById (id: number): Promise<Post> {
67
+ const post = await this.postRepository.findById(id)
68
+ if (isNotEmpty<PostModel>(post)) return post
69
+ throw new NotFoundError(`Post with ID ${id} not found`)
70
+ }
71
+
72
+ /**
73
+ * Create a new post.
74
+ *
75
+ * @param payload - The post data to create.
76
+ */
77
+ async create (payload: Omit<PostCreateModel, 'id'>): Promise<bigint | undefined> {
78
+ return await this.postRepository.create({ ...payload, createdAt: Date.now(), updatedAt: Date.now() })
79
+ }
80
+
81
+ /**
82
+ * Update an existing post.
83
+ *
84
+ * @param id - The ID of the post.
85
+ * @param payload - The post data to update.
86
+ * @returns The updated post.
87
+ */
88
+ async update (id: number, payload: Partial<PostModel>): Promise<boolean> {
89
+ return await this.postRepository.update(id, payload)
90
+ }
91
+
92
+ /**
93
+ * Delete a post.
94
+ *
95
+ * @param id - The ID of the post.
96
+ */
97
+ async delete (id: number): Promise<boolean> {
98
+ return await this.postRepository.delete(id)
99
+ }
100
+ }
@@ -0,0 +1,232 @@
1
+ import bcrypt from 'bcryptjs'
2
+ import jwt from 'jsonwebtoken'
3
+ import { SessionService } from './SessionService'
4
+ import { UserRepository } from '../repositories/UserRepository'
5
+ import { BadCredentialsError } from '../errors/CredentialsError'
6
+ import { IBlueprint, isEmpty, isNotEmpty, Service } from '@stone-js/core'
7
+ import { BadRequestError, IncomingHttpEvent, UnauthorizedError } from '@stone-js/http-core'
8
+ import { UserLogin, UserToken, UserRegister, UserChangePassword, UserModel, UserTokenPayload } from '../models/User'
9
+
10
+ /**
11
+ * Security Service Options
12
+ */
13
+ export interface SecurityServiceOptions {
14
+ blueprint: IBlueprint
15
+ userRepository: UserRepository
16
+ sessionService: SessionService
17
+ }
18
+
19
+ /**
20
+ * Security Service
21
+ *
22
+ * @Service() decorator is used to define a new service
23
+ * @Service() is an alias of @Stone() decorator.
24
+ * The alias is required to get benefits of desctructuring Dependency Injection.
25
+ * And because the front-end class will be minified, we need to use alias to keep the class name.
26
+ */
27
+ @Service({ alias: 'securityService' })
28
+ export class SecurityService {
29
+ private readonly blueprint: IBlueprint
30
+ private readonly userRepository: UserRepository
31
+ private readonly sessionService: SessionService
32
+
33
+ /**
34
+ * Create a new Security Service
35
+ */
36
+ constructor ({ blueprint, userRepository, sessionService }: SecurityServiceOptions) {
37
+ this.blueprint = blueprint
38
+ this.userRepository = userRepository
39
+ this.sessionService = sessionService
40
+ }
41
+
42
+ /**
43
+ * Login a user
44
+ *
45
+ * @param credentials - The user to login
46
+ * @returns The user token
47
+ */
48
+ async login (event: IncomingHttpEvent, credentials: UserLogin): Promise<UserToken> {
49
+ const user = await this.userRepository.findBy(credentials)
50
+
51
+ if (isEmpty(user)) {
52
+ throw new BadCredentialsError(`The user with email ${credentials.email} does not exist`)
53
+ }
54
+
55
+ if (!(await this.comparePassword(credentials.password, user.password))) {
56
+ throw new BadCredentialsError('Invalid user password')
57
+ }
58
+
59
+ return {
60
+ tokenType: 'bearer',
61
+ expiresIn: this.blueprint.get<number>('security.jwt.expiresIn', 3600),
62
+ accessToken: await this.generateToken(user, event.ip, event.userAgent)
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Refresh a token
68
+ *
69
+ * @param token - The token to refresh
70
+ */
71
+ async refresh (token: string): Promise<UserToken> {
72
+ const payload = this.verifyToken(token)
73
+ const user = await this.userRepository.findById(payload.user.id)
74
+
75
+ if (isEmpty(user)) {
76
+ throw new UnauthorizedError('User not found')
77
+ }
78
+
79
+ return {
80
+ tokenType: 'bearer',
81
+ expiresIn: this.blueprint.get<number>('security.jwt.expiresIn', 3600),
82
+ accessToken: await this.generateToken(user, payload.session.ip, payload.session.userAgent ?? undefined)
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Logout a user
88
+ *
89
+ * @param token - The token to logout
90
+ */
91
+ async logout (token: string): Promise<void> {
92
+ const payload = this.verifyToken(token)
93
+ await this.sessionService.close(payload.session)
94
+ }
95
+
96
+ /**
97
+ * Authenticate a user
98
+ *
99
+ * @param token - The token to authenticate
100
+ * @returns The authenticated user
101
+ */
102
+ async authenticate (token: string, ip: string, userAgent?: string): Promise<UserModel> {
103
+ const payload = this.verifyToken(token)
104
+ const user = await this.userRepository.findById(payload.user.id)
105
+
106
+ if (payload.session.ip !== ip) {
107
+ throw new UnauthorizedError('Invalid IP address')
108
+ } else if (payload.session.userAgent !== userAgent) {
109
+ throw new UnauthorizedError('Invalid user agent')
110
+ } else if (isEmpty(user)) {
111
+ throw new UnauthorizedError('User not found')
112
+ }
113
+
114
+ await this.sessionService.updateLastActivity(payload.session)
115
+
116
+ return user
117
+ }
118
+
119
+ /**
120
+ * Hashes a password before storing it in the database.
121
+ *
122
+ * @param password - The plaintext password.
123
+ * @returns The hashed password.
124
+ */
125
+ async hashPassword (password: string): Promise<string> {
126
+ return await bcrypt.hash(password, this.blueprint.get<number>('security.bcrypt.saltRounds', 10))
127
+ }
128
+
129
+ /**
130
+ * Compares a plaintext password with a hashed password.
131
+ *
132
+ * @param password - The plaintext password.
133
+ * @param hashedPassword - The stored hashed password.
134
+ * @returns `true` if the password matches, otherwise `false`.
135
+ */
136
+ async comparePassword (password: string, hashedPassword?: string | null): Promise<boolean> {
137
+ return await bcrypt.compare(password, hashedPassword ?? '')
138
+ }
139
+
140
+ /**
141
+ * Generates a JWT token for a user.
142
+ *
143
+ * @param user - The user information to include in the token.
144
+ * @returns The generated JWT token.
145
+ */
146
+ async generateToken (user: UserModel, ip: string, userAgent?: string): Promise<string> {
147
+ let session = await this.sessionService.getLatest(user)
148
+
149
+ if (
150
+ isEmpty(session) ||
151
+ session?.ip !== ip ||
152
+ isNotEmpty(session.closedAt) ||
153
+ session?.userAgent !== userAgent
154
+ ) {
155
+ session = await this.sessionService.createForUser(user, ip, userAgent)
156
+ } else {
157
+ session = await this.sessionService.extend(
158
+ session,
159
+ this.blueprint.get<number>('security.jwt.expiresIn', 3600)
160
+ )
161
+ }
162
+
163
+ return jwt.sign(
164
+ {
165
+ user: {
166
+ ...user,
167
+ password: undefined
168
+ },
169
+ session
170
+ },
171
+ this.blueprint.get<string>('security.secret', 'secret'),
172
+ this.blueprint.get<jwt.SignOptions>('security.jwt')
173
+ )
174
+ }
175
+
176
+ /**
177
+ * Verifies a JWT token and returns the decoded payload.
178
+ *
179
+ * @param token - The JWT token to verify.
180
+ * @returns The decoded user payload or null if invalid.
181
+ */
182
+ verifyToken = (token: string): UserTokenPayload => {
183
+ try {
184
+ return jwt.verify(
185
+ token,
186
+ this.blueprint.get<string>('security.secret', 'secret')
187
+ ) as UserTokenPayload
188
+ } catch (error: any) {
189
+ throw new UnauthorizedError('Invalid token', { cause: error })
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Register a user
195
+ *
196
+ * @param user - The user to register
197
+ */
198
+ async register (payload: UserRegister): Promise<void> {
199
+ const user = await this.userRepository.findBy({ email: payload.email })
200
+
201
+ if (isNotEmpty(user)) {
202
+ throw new BadRequestError(`The user with email ${payload.email} already exists`)
203
+ }
204
+
205
+ const password = await this.hashPassword(payload.password)
206
+
207
+ await this.userRepository.create({
208
+ ...payload,
209
+ password,
210
+ updatedAt: Date.now(),
211
+ createdAt: Date.now()
212
+ })
213
+ }
214
+
215
+ /**
216
+ * Change the user password
217
+ *
218
+ * @param user - The user to change the password
219
+ * @param password - The password to change
220
+ */
221
+ async changePassword (user?: UserModel, password?: UserChangePassword): Promise<void> {
222
+ if (isEmpty(user)) {
223
+ throw new UnauthorizedError('User not found')
224
+ }
225
+
226
+ if (isEmpty(password) || !(await this.comparePassword(password.password, user.password))) {
227
+ throw new UnauthorizedError('Invalid user password')
228
+ }
229
+
230
+ await this.userRepository.update(user.id, { password: password.newPassword })
231
+ }
232
+ }
@@ -0,0 +1,130 @@
1
+ import { Service } from '@stone-js/core'
2
+ import { randomUUID } from 'node:crypto'
3
+ import { UserModel } from '../models/User'
4
+ import { Session } from '../models/Session'
5
+ import { SessionRepository } from '../repositories/SessionRepository'
6
+
7
+ /**
8
+ * Session Service Options
9
+ */
10
+ export interface SessionServiceOptions {
11
+ sessionRepository: SessionRepository
12
+ }
13
+
14
+ /**
15
+ * Session Service
16
+ *
17
+ * @Service() decorator is used to define a new service
18
+ * @Service() is an alias of @Stone() decorator.
19
+ * The alias is required to get benefits of desctructuring Dependency Injection.
20
+ * And because the front-end class will be minified, we need to use alias to keep the class name.
21
+ */
22
+ @Service({ alias: 'sessionService' })
23
+ export class SessionService {
24
+ private readonly sessionRepository: SessionRepository
25
+
26
+ /**
27
+ * Create a new Session Service
28
+ *
29
+ * @param options - The options to create the service
30
+ */
31
+ constructor ({ sessionRepository }: SessionServiceOptions) {
32
+ this.sessionRepository = sessionRepository
33
+ }
34
+
35
+ /**
36
+ * Finds all sessions.
37
+ *
38
+ * @param limit - The limit of sessions to list
39
+ * @returns The list of sessions
40
+ */
41
+ async list (limit: number): Promise<Session[]> {
42
+ return await this.sessionRepository.list(limit)
43
+ }
44
+
45
+ /**
46
+ * Finds a sessions by user.
47
+ *
48
+ * @param user - The user to look up
49
+ * @param limit - The limit of sessions to list
50
+ * @returns The list of sessions
51
+ */
52
+ async getByUser (user: UserModel, limit: number): Promise<Session[]> {
53
+ return await this.sessionRepository.listByUser(user, limit)
54
+ }
55
+
56
+ /**
57
+ * Finds a session by ID.
58
+ *
59
+ * @param sessionId - The session ID to look up
60
+ * @returns The session data or `undefined` if not found
61
+ */
62
+ async getById (id: number): Promise<Session | undefined> {
63
+ return await this.sessionRepository.findById(id)
64
+ }
65
+
66
+ /**
67
+ * Finds the latest session of a user.
68
+ *
69
+ * @param user - The user to look up
70
+ * @returns The latest session or `undefined` if not found
71
+ */
72
+ async getLatest (user: UserModel): Promise<Session | undefined> {
73
+ return await this.sessionRepository.getLatest(user)
74
+ }
75
+
76
+ /**
77
+ * Creates a new session for a user.
78
+ *
79
+ * @param user - The user to create the session for
80
+ * @param ip - The IP address of the user
81
+ * @param userAgent - The user agent of the user
82
+ * @returns The created session
83
+ */
84
+ async createForUser (user: UserModel, ip: string, userAgent?: string): Promise<Session> {
85
+ const session = {
86
+ ip,
87
+ userAgent,
88
+ userId: user.id,
89
+ createdAt: Date.now(),
90
+ uuid: randomUUID(),
91
+ lastActivityAt: Date.now(),
92
+ expiresAt: Date.now() + 3600
93
+ }
94
+
95
+ return await this.sessionRepository.create(session)
96
+ }
97
+
98
+ /**
99
+ * Updates the last activity timestamp of a session.
100
+ *
101
+ * @param sessionId - The session ID to update
102
+ * @returns `true` if the session was updated, otherwise `false`
103
+ */
104
+ async updateLastActivity (session: Session): Promise<void> {
105
+ await this.sessionRepository.update(session.id, { lastActivityAt: Date.now() })
106
+ }
107
+
108
+ /**
109
+ * Extends a session expiration time.
110
+ *
111
+ * @param sessionId - The session ID to extend
112
+ * @param additionalTime - Additional time in milliseconds
113
+ * @returns `true` if the session was extended, otherwise `false`
114
+ */
115
+ async extend (session: Session, additionalTime: number): Promise<Session> {
116
+ session.expiresAt = Date.now() + additionalTime
117
+ await this.sessionRepository.update(session.id, session)
118
+ return session
119
+ }
120
+
121
+ /**
122
+ * Closes a session.
123
+ *
124
+ * @param sessionId - The session ID to close
125
+ */
126
+ async close (session: Session): Promise<void> {
127
+ session.closedAt = Date.now()
128
+ await this.sessionRepository.update(session.id, session)
129
+ }
130
+ }
@@ -0,0 +1,110 @@
1
+ import { UserModel } from '../models/User'
2
+ import { UserEvent } from '../events/UserEvent'
3
+ import { NotFoundError } from '@stone-js/http-core'
4
+ import { UserRepository } from '../repositories/UserRepository'
5
+ import { EventEmitter, IContainer, isNotEmpty, Service } from '@stone-js/core'
6
+
7
+ /**
8
+ * User Service Options
9
+ */
10
+ export interface UserServiceOptions {
11
+ eventEmitter: EventEmitter
12
+ userRepository: UserRepository
13
+ }
14
+
15
+ /**
16
+ * User Service
17
+ *
18
+ * @Service() decorator is used to define a new service
19
+ * @Service() is an alias of @Stone() decorator.
20
+ * The alias is required to get benefits of desctructuring Dependency Injection.
21
+ * And because the front-end class will be minified, we need to use alias to keep the class name.
22
+ */
23
+ @Service({ alias: 'userService' })
24
+ export class UserService {
25
+ private readonly eventEmitter: EventEmitter
26
+ private readonly userRepository: UserRepository
27
+
28
+ /**
29
+ * Resolve route binding
30
+ *
31
+ * @param key - The key of the binding
32
+ * @param value - The value of the binding
33
+ * @param container - The container
34
+ */
35
+ static async resolveRouteBinding (key: string, value: any, container: IContainer): Promise<UserModel | undefined> {
36
+ const userService = container.resolve<UserService>('userService')
37
+ return await userService.findBy({ [key]: value })
38
+ }
39
+
40
+ /**
41
+ * Create a new User Service
42
+ */
43
+ constructor ({ userRepository, eventEmitter }: UserServiceOptions) {
44
+ this.eventEmitter = eventEmitter
45
+ this.userRepository = userRepository
46
+ }
47
+
48
+ /**
49
+ * List users
50
+ *
51
+ * @param limit - The limit of users to list
52
+ */
53
+ async list (limit: number = 10): Promise<UserModel[]> {
54
+ return await this.userRepository.list(limit)
55
+ }
56
+
57
+ /**
58
+ * Find a user
59
+ *
60
+ * @param conditions - The conditions to find the user
61
+ * @returns The found user
62
+ */
63
+ async findBy (conditions: Record<string, any>): Promise<UserModel> {
64
+ const user = await this.userRepository.findById(conditions.id)
65
+ if (isNotEmpty<UserModel>(user)) return user
66
+ throw new NotFoundError(`User with ID ${String(conditions.id)} not found`)
67
+ }
68
+
69
+ /**
70
+ * Find a user
71
+ *
72
+ * @param conditions - The conditions to find the user
73
+ * @returns The found user
74
+ */
75
+ async findByEmail (conditions: Record<string, any>): Promise<UserModel | undefined> {
76
+ return await this.userRepository.findBy(conditions)
77
+ }
78
+
79
+ /**
80
+ * Create a user
81
+ *
82
+ * @param payload - The user data to create
83
+ */
84
+ async create (payload: Omit<UserModel, 'id'>): Promise<bigint | undefined> {
85
+ await this.eventEmitter.emit(new UserEvent(payload))
86
+ return await this.userRepository.create({ ...payload, createdAt: Date.now(), updatedAt: Date.now() })
87
+ }
88
+
89
+ /**
90
+ * Update a user
91
+ *
92
+ * @param id - The id of the user to update
93
+ * @param payload - The user data to update
94
+ * @returns The updated user
95
+ */
96
+ async update (id: number, payload: UserModel): Promise<UserModel> {
97
+ const user = await this.userRepository.update(id, payload)
98
+ if (isNotEmpty<UserModel>(user)) return user
99
+ throw new NotFoundError(`User with ID ${id} not found`)
100
+ }
101
+
102
+ /**
103
+ * Delete a user
104
+ *
105
+ * @param id - The id of the user to delete
106
+ */
107
+ async delete (id: number): Promise<boolean> {
108
+ return await this.userRepository.delete(id)
109
+ }
110
+ }