@sundaysf/cli-v2 1.0.1 → 1.0.5

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 (191) hide show
  1. package/README.md +178 -178
  2. package/dist/README.md +178 -178
  3. package/dist/bin/generators/class.js.map +1 -1
  4. package/dist/bin/generators/postman.js.map +1 -1
  5. package/dist/bin/index.js +1 -1
  6. package/dist/bin/index.js.map +1 -1
  7. package/dist/templates/backend/.claude/agents/knex-table-implementer.md +113 -113
  8. package/dist/templates/backend/.claude/agents/sundays-backend-builder.md +70 -70
  9. package/dist/templates/backend/.claude/settings.local.json +13 -13
  10. package/dist/templates/backend/.env.example +13 -13
  11. package/dist/templates/backend/.prettierignore +2 -2
  12. package/dist/templates/backend/.prettierrc +9 -9
  13. package/dist/templates/backend/.sundaysrc +7 -0
  14. package/dist/templates/backend/CLAUDE.md +348 -348
  15. package/dist/templates/backend/Dockerfile +14 -14
  16. package/dist/templates/backend/README.md +18 -18
  17. package/dist/templates/backend/eslint.config.js +20 -20
  18. package/dist/templates/backend/src/app.ts +34 -34
  19. package/dist/templates/backend/src/common/config/origins/origins.config.ts +11 -11
  20. package/dist/templates/backend/src/common/utils/environment.resolver.ts +3 -3
  21. package/dist/templates/backend/src/common/utils/version.resolver.ts +4 -4
  22. package/dist/templates/backend/src/controllers/health/health.controller.ts +23 -23
  23. package/dist/templates/backend/src/middlewares/error/error.middleware.ts +21 -21
  24. package/dist/templates/backend/src/routes/health/health.router.ts +16 -16
  25. package/dist/templates/backend/src/routes/index.ts +57 -57
  26. package/dist/templates/backend/src/server.ts +16 -16
  27. package/dist/templates/backend/src/types.d.ts +10 -10
  28. package/dist/templates/backend/tsconfig.json +16 -16
  29. package/dist/templates/backend-db-sql/.claude/agents/knex-table-implementer.md +114 -114
  30. package/dist/templates/backend-db-sql/.claude/agents/sundays-backend-builder.md +70 -70
  31. package/dist/templates/backend-db-sql/.claude/settings.local.json +19 -19
  32. package/dist/templates/backend-db-sql/.env.example +13 -13
  33. package/dist/templates/backend-db-sql/.prettierignore +2 -2
  34. package/dist/templates/backend-db-sql/.prettierrc +9 -9
  35. package/dist/templates/backend-db-sql/.sundaysrc +7 -0
  36. package/dist/templates/backend-db-sql/CLAUDE.md +374 -374
  37. package/dist/templates/backend-db-sql/Dockerfile +17 -17
  38. package/dist/templates/backend-db-sql/README.md +34 -34
  39. package/dist/templates/backend-db-sql/db/knexfile.ts +33 -33
  40. package/dist/templates/backend-db-sql/db/migrations/001_create_sundays_package_version.ts +12 -12
  41. package/dist/templates/backend-db-sql/db/seeds/001_sundays_package_version_seed.ts +10 -10
  42. package/dist/templates/backend-db-sql/db/src/KnexConnection.ts +74 -74
  43. package/dist/templates/backend-db-sql/db/src/d.types.ts +18 -18
  44. package/dist/templates/backend-db-sql/db/src/dao/sundays-package-version/sundays-package-version.dao.ts +71 -71
  45. package/dist/templates/backend-db-sql/db/src/index.ts +9 -9
  46. package/dist/templates/backend-db-sql/db/src/interfaces/sundays-package-version/sundays-package-version.interfaces.ts +6 -6
  47. package/dist/templates/backend-db-sql/db/tsconfig.json +16 -16
  48. package/dist/templates/backend-db-sql/eslint.config.js +20 -20
  49. package/dist/templates/backend-db-sql/src/app.ts +34 -34
  50. package/dist/templates/backend-db-sql/src/common/config/origins/origins.config.ts +11 -11
  51. package/dist/templates/backend-db-sql/src/common/utils/environment.resolver.ts +3 -3
  52. package/dist/templates/backend-db-sql/src/common/utils/version.resolver.ts +4 -4
  53. package/dist/templates/backend-db-sql/src/controllers/health/health.controller.ts +23 -23
  54. package/dist/templates/backend-db-sql/src/middlewares/error/error.middleware.ts +21 -21
  55. package/dist/templates/backend-db-sql/src/routes/health/health.router.ts +16 -16
  56. package/dist/templates/backend-db-sql/src/routes/index.ts +57 -57
  57. package/dist/templates/backend-db-sql/src/server.ts +18 -18
  58. package/dist/templates/backend-db-sql/src/types.d.ts +10 -10
  59. package/dist/templates/backend-db-sql/tsconfig.json +16 -16
  60. package/dist/templates/backend-embedded-db-sql/.claude/agents/knex-table-implementer.md +116 -0
  61. package/dist/templates/backend-embedded-db-sql/.claude/agents/sundays-backend-builder.md +70 -0
  62. package/dist/templates/backend-embedded-db-sql/.claude/settings.local.json +18 -0
  63. package/dist/templates/backend-embedded-db-sql/.env.example +14 -0
  64. package/dist/templates/backend-embedded-db-sql/.prettierignore +3 -0
  65. package/dist/templates/backend-embedded-db-sql/.prettierrc +9 -0
  66. package/dist/templates/backend-embedded-db-sql/.sundaysrc +7 -0
  67. package/dist/templates/backend-embedded-db-sql/CLAUDE.md +371 -0
  68. package/dist/templates/backend-embedded-db-sql/Dockerfile +14 -0
  69. package/dist/templates/backend-embedded-db-sql/README.md +32 -0
  70. package/dist/templates/backend-embedded-db-sql/eslint.config.js +20 -0
  71. package/dist/templates/backend-embedded-db-sql/knexfile.ts +37 -0
  72. package/dist/templates/backend-embedded-db-sql/migrations/.gitkeep +0 -0
  73. package/dist/templates/backend-embedded-db-sql/migrations/001_create_sundays_package_version.ts +13 -0
  74. package/dist/templates/backend-embedded-db-sql/seeds/001_sundays_package_version_seed.ts +11 -0
  75. package/dist/templates/backend-embedded-db-sql/src/app.ts +35 -0
  76. package/dist/templates/backend-embedded-db-sql/src/common/config/origins/origins.config.ts +11 -0
  77. package/dist/templates/backend-embedded-db-sql/src/common/utils/environment.resolver.ts +4 -0
  78. package/dist/templates/backend-embedded-db-sql/src/common/utils/version.resolver.ts +5 -0
  79. package/dist/templates/backend-embedded-db-sql/src/controllers/health/health.controller.ts +24 -0
  80. package/dist/templates/backend-embedded-db-sql/src/db/KnexConnection.ts +74 -0
  81. package/dist/templates/backend-embedded-db-sql/src/db/d.types.ts +18 -0
  82. package/dist/templates/backend-embedded-db-sql/src/db/dao/sundays-package-version/sundays-package-version.dao.ts +71 -0
  83. package/dist/templates/backend-embedded-db-sql/src/db/index.ts +9 -0
  84. package/dist/templates/backend-embedded-db-sql/src/db/interfaces/sundays-package-version/sundays-package-version.interfaces.ts +6 -0
  85. package/dist/templates/backend-embedded-db-sql/src/middlewares/error/error.middleware.ts +21 -0
  86. package/dist/templates/backend-embedded-db-sql/src/routes/health/health.router.ts +17 -0
  87. package/dist/templates/backend-embedded-db-sql/src/routes/index.ts +57 -0
  88. package/dist/templates/backend-embedded-db-sql/src/server.ts +18 -0
  89. package/dist/templates/backend-embedded-db-sql/src/types.d.ts +10 -0
  90. package/dist/templates/backend-embedded-db-sql/tsconfig.json +16 -0
  91. package/dist/templates/db-sql/.claude/agents/knex-table-implementer.md +113 -113
  92. package/dist/templates/db-sql/.claude/agents/sundays-backend-builder.md +70 -70
  93. package/dist/templates/db-sql/.claude/settings.local.json +10 -10
  94. package/dist/templates/db-sql/.env.example +8 -8
  95. package/dist/templates/db-sql/CLAUDE.md +105 -105
  96. package/dist/templates/db-sql/knexfile.ts +33 -33
  97. package/dist/templates/db-sql/migrations/001_create_sundays_package_version.ts +12 -12
  98. package/dist/templates/db-sql/seeds/001_sundays_package_version_seed.ts +10 -10
  99. package/dist/templates/db-sql/src/KnexConnection.ts +74 -74
  100. package/dist/templates/db-sql/src/d.types.ts +18 -18
  101. package/dist/templates/db-sql/src/dao/sundays-package-version/sundays-package-version.dao.ts +71 -71
  102. package/dist/templates/db-sql/src/index.ts +9 -9
  103. package/dist/templates/db-sql/src/interfaces/sundays-package-version/sundays-package-version.interfaces.ts +6 -6
  104. package/dist/templates/db-sql/tsconfig.json +16 -16
  105. package/dist/templates/frontend-nextjs/.sundaysrc +7 -0
  106. package/dist/templates/frontend-nextjs/app/globals.css +125 -0
  107. package/dist/templates/frontend-nextjs/app/layout.tsx +45 -0
  108. package/dist/templates/frontend-nextjs/app/page.tsx +5 -0
  109. package/dist/templates/frontend-nextjs/components/project-generator.tsx +558 -0
  110. package/dist/templates/frontend-nextjs/components/theme-provider.tsx +11 -0
  111. package/dist/templates/frontend-nextjs/components/ui/accordion.tsx +66 -0
  112. package/dist/templates/frontend-nextjs/components/ui/alert-dialog.tsx +157 -0
  113. package/dist/templates/frontend-nextjs/components/ui/alert.tsx +66 -0
  114. package/dist/templates/frontend-nextjs/components/ui/aspect-ratio.tsx +11 -0
  115. package/dist/templates/frontend-nextjs/components/ui/avatar.tsx +53 -0
  116. package/dist/templates/frontend-nextjs/components/ui/badge.tsx +46 -0
  117. package/dist/templates/frontend-nextjs/components/ui/breadcrumb.tsx +109 -0
  118. package/dist/templates/frontend-nextjs/components/ui/button-group.tsx +83 -0
  119. package/dist/templates/frontend-nextjs/components/ui/button.tsx +60 -0
  120. package/dist/templates/frontend-nextjs/components/ui/calendar.tsx +213 -0
  121. package/dist/templates/frontend-nextjs/components/ui/card.tsx +92 -0
  122. package/dist/templates/frontend-nextjs/components/ui/carousel.tsx +241 -0
  123. package/dist/templates/frontend-nextjs/components/ui/chart.tsx +353 -0
  124. package/dist/templates/frontend-nextjs/components/ui/checkbox.tsx +32 -0
  125. package/dist/templates/frontend-nextjs/components/ui/collapsible.tsx +33 -0
  126. package/dist/templates/frontend-nextjs/components/ui/command.tsx +184 -0
  127. package/dist/templates/frontend-nextjs/components/ui/context-menu.tsx +252 -0
  128. package/dist/templates/frontend-nextjs/components/ui/dialog.tsx +143 -0
  129. package/dist/templates/frontend-nextjs/components/ui/drawer.tsx +135 -0
  130. package/dist/templates/frontend-nextjs/components/ui/dropdown-menu.tsx +257 -0
  131. package/dist/templates/frontend-nextjs/components/ui/empty.tsx +104 -0
  132. package/dist/templates/frontend-nextjs/components/ui/field.tsx +244 -0
  133. package/dist/templates/frontend-nextjs/components/ui/form.tsx +167 -0
  134. package/dist/templates/frontend-nextjs/components/ui/hover-card.tsx +44 -0
  135. package/dist/templates/frontend-nextjs/components/ui/input-group.tsx +169 -0
  136. package/dist/templates/frontend-nextjs/components/ui/input-otp.tsx +77 -0
  137. package/dist/templates/frontend-nextjs/components/ui/input.tsx +21 -0
  138. package/dist/templates/frontend-nextjs/components/ui/item.tsx +193 -0
  139. package/dist/templates/frontend-nextjs/components/ui/kbd.tsx +28 -0
  140. package/dist/templates/frontend-nextjs/components/ui/label.tsx +24 -0
  141. package/dist/templates/frontend-nextjs/components/ui/menubar.tsx +276 -0
  142. package/dist/templates/frontend-nextjs/components/ui/navigation-menu.tsx +166 -0
  143. package/dist/templates/frontend-nextjs/components/ui/pagination.tsx +127 -0
  144. package/dist/templates/frontend-nextjs/components/ui/popover.tsx +48 -0
  145. package/dist/templates/frontend-nextjs/components/ui/progress.tsx +31 -0
  146. package/dist/templates/frontend-nextjs/components/ui/radio-group.tsx +45 -0
  147. package/dist/templates/frontend-nextjs/components/ui/resizable.tsx +56 -0
  148. package/dist/templates/frontend-nextjs/components/ui/scroll-area.tsx +58 -0
  149. package/dist/templates/frontend-nextjs/components/ui/select.tsx +185 -0
  150. package/dist/templates/frontend-nextjs/components/ui/separator.tsx +28 -0
  151. package/dist/templates/frontend-nextjs/components/ui/sheet.tsx +139 -0
  152. package/dist/templates/frontend-nextjs/components/ui/sidebar.tsx +726 -0
  153. package/dist/templates/frontend-nextjs/components/ui/skeleton.tsx +13 -0
  154. package/dist/templates/frontend-nextjs/components/ui/slider.tsx +63 -0
  155. package/dist/templates/frontend-nextjs/components/ui/sonner.tsx +25 -0
  156. package/dist/templates/frontend-nextjs/components/ui/spinner.tsx +16 -0
  157. package/dist/templates/frontend-nextjs/components/ui/switch.tsx +31 -0
  158. package/dist/templates/frontend-nextjs/components/ui/table.tsx +116 -0
  159. package/dist/templates/frontend-nextjs/components/ui/tabs.tsx +66 -0
  160. package/dist/templates/frontend-nextjs/components/ui/textarea.tsx +18 -0
  161. package/dist/templates/frontend-nextjs/components/ui/toast.tsx +129 -0
  162. package/dist/templates/frontend-nextjs/components/ui/toaster.tsx +35 -0
  163. package/dist/templates/frontend-nextjs/components/ui/toggle-group.tsx +73 -0
  164. package/dist/templates/frontend-nextjs/components/ui/toggle.tsx +47 -0
  165. package/dist/templates/frontend-nextjs/components/ui/tooltip.tsx +61 -0
  166. package/dist/templates/frontend-nextjs/components/ui/use-mobile.tsx +19 -0
  167. package/dist/templates/frontend-nextjs/components/ui/use-toast.ts +191 -0
  168. package/dist/templates/frontend-nextjs/components.json +21 -0
  169. package/dist/templates/frontend-nextjs/hooks/use-mobile.ts +19 -0
  170. package/dist/templates/frontend-nextjs/hooks/use-toast.ts +191 -0
  171. package/dist/templates/frontend-nextjs/lib/utils.ts +6 -0
  172. package/dist/templates/frontend-nextjs/next.config.mjs +11 -0
  173. package/dist/templates/frontend-nextjs/postcss.config.mjs +8 -0
  174. package/dist/templates/frontend-nextjs/public/apple-icon.png +0 -0
  175. package/dist/templates/frontend-nextjs/public/icon-dark-32x32.png +0 -0
  176. package/dist/templates/frontend-nextjs/public/icon-light-32x32.png +0 -0
  177. package/dist/templates/frontend-nextjs/public/icon.svg +26 -0
  178. package/dist/templates/frontend-nextjs/public/placeholder-logo.png +0 -0
  179. package/dist/templates/frontend-nextjs/public/placeholder-logo.svg +1 -0
  180. package/dist/templates/frontend-nextjs/public/placeholder-user.jpg +0 -0
  181. package/dist/templates/frontend-nextjs/public/placeholder.jpg +0 -0
  182. package/dist/templates/frontend-nextjs/public/placeholder.svg +1 -0
  183. package/dist/templates/frontend-nextjs/styles/globals.css +125 -0
  184. package/dist/templates/frontend-nextjs/tsconfig.json +27 -0
  185. package/dist/templates/module/.claude/agents/knex-table-implementer.md +113 -113
  186. package/dist/templates/module/.claude/agents/sundays-backend-builder.md +70 -70
  187. package/dist/templates/module/.claude/settings.local.json +10 -10
  188. package/dist/templates/module/CLAUDE.md +158 -158
  189. package/dist/templates/module/src/index.ts +9 -9
  190. package/dist/templates/module/tsconfig.json +19 -19
  191. package/package.json +40 -40
@@ -1,374 +1,374 @@
1
- # CLAUDE.md
2
-
3
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
-
5
- ## Commands
6
-
7
- ### Development
8
-
9
- - `npm run start:dev` - Start development server with auto-reload (uses nodemon)
10
- - `npm run build` - Compile TypeScript to JavaScript (output to dist/)
11
- - `npm start` - Run production server from compiled dist/
12
-
13
- ### Code Quality
14
-
15
- - `npm run format` - Format code with Prettier
16
- - No linting command configured - consider using ESLint with the installed configuration
17
-
18
- ### Testing
19
-
20
- - `npm test` - Run tests with Jest (no test files found yet)
21
-
22
- ### Utilities
23
-
24
- - `npm run create:controller` - Generate new controller using Sundays Framework CLI
25
-
26
- ### Database
27
-
28
- - `npm run migrate:create` - Create a new migration file in TypeScript
29
- - `npm run migrate:deploy` - Apply pending migrations
30
- - `npm run seed:create` - Create a new seed file in TypeScript
31
- - `npm run seed:run` - Execute seed files
32
- - `npm run db:build` - Compile the db module TypeScript to JavaScript
33
- - `npm run db:publish` - Publish the db module as an independent npm package
34
-
35
- ## Architecture
36
-
37
- This is a Sundays Framework backend built with Express.js and TypeScript following a modular MVC pattern, with an embedded Knex.js database module in `db/`.
38
-
39
- ### Core Structure
40
-
41
- - **Entry Points**: `src/server.ts` initializes the server and connects to the database, `src/app.ts` configures Express middleware
42
- - **Routing**: Dynamic route loading system in `src/routes/index.ts` that automatically discovers and mounts routers from subdirectories
43
- - **Controllers**: Business logic separated into controller classes (e.g., `HealthController`)
44
- - **Middleware**: Error handling middleware in `src/middlewares/error/`
45
- - **Configuration**: CORS origins managed in `src/common/config/origins/`
46
- - **Database**: `db/` contains the Knex.js module with DAOs, migrations, seeds, and interfaces
47
-
48
- ### Key Patterns
49
-
50
- 1. **Router Auto-Discovery**: The `IndexRouter` class scans the routes directory and automatically mounts any `*.router.ts` files found in subdirectories. Routes are mounted at `/{folder-name}`.
51
-
52
- 2. **Controller Pattern**: Each router has a corresponding controller class that handles the business logic. Controllers are bound to router methods using `.bind()` to maintain proper context.
53
-
54
- 3. **Environment Configuration**: Uses dotenv for environment variables. Server port defaults to 3005 if not specified in .env.
55
-
56
- 4. **CORS Configuration**: Dynamic CORS origin configuration via `getAllowedOrigins()` function.
57
-
58
- 5. **Database Connection**: `src/server.ts` calls `KnexManager.connect()` before starting the Express server. The KnexManager singleton manages connection pooling.
59
-
60
- 6. **Dependencies**:
61
- - Sundays Framework utilities (`@sundaysf/utils`) for pagination and validation
62
- - Standard Express middleware (cors, morgan, etc.)
63
- - Knex.js + PostgreSQL for database access
64
-
65
- ### Database Module (`db/`)
66
-
67
- The `db/` directory is an independently publishable Knex.js module:
68
-
69
- - **KnexManager** (`db/src/KnexConnection.ts`): Singleton for managing database connections with pooling and SSL support
70
- - **DAO Pattern**: Data Access Objects implement `IBaseDAO<T>` with CRUD + pagination
71
- - **Migrations**: `db/migrations/` - Knex migration files in TypeScript
72
- - **Seeds**: `db/seeds/` - Seed data files
73
- - **Interfaces**: `db/src/interfaces/` - Entity type definitions
74
- - **Configuration**: `db/knexfile.ts` - Knex configuration for all environments
75
-
76
- #### File Structure Conventions
77
-
78
- **Interfaces**: `db/src/interfaces/{entity}/{entity}.interfaces.ts`
79
- **DAOs**: `db/src/dao/{entity}/{entity}.dao.ts`
80
-
81
- #### Environment Variables (Database)
82
-
83
- - `SQL_HOST` - PostgreSQL host
84
- - `SQL_PORT` - Database port (defaults to 5432)
85
- - `SQL_USER` - Database user
86
- - `SQL_PASSWORD` - Database password
87
- - `SQL_DB_NAME` - Database name
88
- - `SQL_REJECT_UNAUTHORIZED` - Set to 'false' to disable SSL certificate verification
89
-
90
- ### TypeScript Configuration
91
-
92
- - **Backend** (`tsconfig.json`): Strict mode, ES2022, CommonJS. Source in `./src`, output to `./dist`. Excludes `db/`.
93
- - **Database** (`db/tsconfig.json`): Strict mode, ES2022, CommonJS with declarations. Source in `db/src`, output to `db/dist`.
94
-
95
- ## Controller Implementation Guide
96
-
97
- When creating new controllers, follow the established pattern demonstrated in the CompanyController:
98
-
99
- ### 1. Controller Structure
100
- ```typescript
101
- import { Request, Response, NextFunction } from "express";
102
- import { IBaseController } from "../../types";
103
- import { inputValidator, IInputValidator, paginationHelper } from "@sundaysf/utils";
104
- import { [Entity]DAO, I[Entity], IDataPaginator } from "../../db/src";
105
- import { [Entity]CreateInputDTO } from "../../dto/input/[entity]/[entity].create.dto";
106
- import { [Entity]UpdateInputDTO } from "../../dto/input/[entity]/[entity].update.dto";
107
- import { v4 as uuidv4 } from 'uuid';
108
-
109
- export class [Entity]Controller implements IBaseController {
110
- private _[entity]DAO: [Entity]DAO = new [Entity]DAO();
111
-
112
- // Implement CRUD methods
113
- }
114
- ```
115
-
116
- ### 2. API Response Format
117
-
118
- All API responses must follow a consistent format:
119
-
120
- **Success Response**:
121
- ```json
122
- {
123
- "success": true,
124
- "data": {...} // or "message": "Action completed successfully" for operations without data
125
- }
126
- ```
127
-
128
- **Error Response**:
129
- ```json
130
- {
131
- "success": false,
132
- "message": "Error description"
133
- }
134
- ```
135
-
136
- **Paginated Response** (from IDataPaginator):
137
- ```json
138
- {
139
- "success": true,
140
- "data": [...],
141
- "page": 1,
142
- "limit": 10,
143
- "count": 10,
144
- "totalCount": 100,
145
- "totalPages": 10
146
- }
147
- ```
148
-
149
- Note: The `IDataPaginator` interface already includes the standard response format with `success` and `data` fields, so responses from paginated endpoints should be returned directly without additional wrapping.
150
-
151
- ### 3. Standard CRUD Methods
152
-
153
- **getAll** - List with pagination:
154
- ```typescript
155
- public async getAll(req: Request, res: Response, next: NextFunction): Promise<void> {
156
- try {
157
- const {page, limit} = paginationHelper(req);
158
- const result: IDataPaginator<I[Entity]> = await this._[entity]DAO.getAll(page, limit);
159
- res.status(200).json(result); // IDataPaginator already includes success and data fields
160
- } catch (err: any) {
161
- next(err);
162
- }
163
- }
164
- ```
165
-
166
- **getByUuid** - Get single resource:
167
- ```typescript
168
- public async getByUuid(req: Request, res: Response, next: NextFunction): Promise<void> {
169
- try {
170
- const { uuid } = req.params;
171
- const result = await this._[entity]DAO.getByUuid(uuid);
172
- if (!result) {
173
- res.status(404).json({ success: false, message: "[Entity] not found" });
174
- return;
175
- }
176
- res.status(200).json({
177
- success: true,
178
- data: result
179
- });
180
- } catch (err: any) {
181
- next(err);
182
- }
183
- }
184
- ```
185
-
186
- **create** - Create new resource with DTO validation:
187
- ```typescript
188
- public async create(req: Request, res: Response, next: NextFunction): Promise<void> {
189
- try {
190
- const data = req.body;
191
- const inputDTO = new [Entity]CreateInputDTO(data).build();
192
- const validation: IInputValidator = await inputValidator(inputDTO);
193
- if (!validation.success) {
194
- req.statusCode = 400;
195
- return next(new Error(validation.message));
196
- }
197
- const dataToCreate = {...inputDTO, uuid: uuidv4()};
198
- const result = await this._[entity]DAO.create(dataToCreate);
199
- res.status(201).json({
200
- success: true,
201
- data: result
202
- });
203
- } catch (err: any) {
204
- next(err);
205
- }
206
- }
207
- ```
208
-
209
- **update** - Update existing resource with DTO validation:
210
- ```typescript
211
- public async update(req: Request, res: Response, next: NextFunction): Promise<void> {
212
- try {
213
- const { uuid } = req.params;
214
- const data = req.body;
215
-
216
- // First get the entity by UUID to find its ID
217
- const existing = await this._[entity]DAO.getByUuid(uuid);
218
- if (!existing || !existing.id) {
219
- res.status(404).json({ success: false, message: "[Entity] not found" });
220
- return;
221
- }
222
-
223
- const inputDTO = new [Entity]UpdateInputDTO(data).build();
224
- const validation: IInputValidator = await inputValidator(inputDTO);
225
- if (!validation.success) {
226
- req.statusCode = 400;
227
- return next(new Error(validation.message));
228
- }
229
-
230
- const result = await this._[entity]DAO.update(existing.id, inputDTO);
231
- res.status(200).json({
232
- success: true,
233
- data: result
234
- });
235
- } catch (err: any) {
236
- next(err);
237
- }
238
- }
239
- ```
240
-
241
- **delete** - Delete resource:
242
- ```typescript
243
- public async delete(req: Request, res: Response, next: NextFunction): Promise<void> {
244
- try {
245
- const { uuid } = req.params;
246
-
247
- // First get the entity by UUID to find its ID
248
- const existing = await this._[entity]DAO.getByUuid(uuid);
249
- if (!existing || !existing.id) {
250
- res.status(404).json({ success: false, message: "[Entity] not found" });
251
- return;
252
- }
253
-
254
- const result = await this._[entity]DAO.delete(existing.id);
255
- if (result) {
256
- res.status(200).json({ success: true, message: "[Entity] deleted successfully" });
257
- } else {
258
- res.status(404).json({ success: false, message: "Failed to delete [entity]" });
259
- }
260
- } catch (err: any) {
261
- next(err);
262
- }
263
- }
264
- ```
265
-
266
- ### 4. Router Implementation
267
- Create a corresponding router in `src/routes/[entity]/[entity].router.ts`:
268
-
269
- ```typescript
270
- import { Router } from "express";
271
- import { [Entity]Controller } from "../../controllers/[entity]/[entity].controller";
272
-
273
- export class [Entity]Router {
274
- private _router: Router;
275
- private _[entity]Controller = new [Entity]Controller();
276
-
277
- constructor() {
278
- this._router = Router();
279
- this.initRoutes();
280
- }
281
-
282
- private initRoutes(): void {
283
- this._router.get("/", this._[entity]Controller.getAll.bind(this._[entity]Controller));
284
- this._router.get("/:uuid", this._[entity]Controller.getByUuid.bind(this._[entity]Controller));
285
- this._router.post("/", this._[entity]Controller.create.bind(this._[entity]Controller));
286
- this._router.put("/:uuid", this._[entity]Controller.update.bind(this._[entity]Controller));
287
- this._router.delete("/:uuid", this._[entity]Controller.delete.bind(this._[entity]Controller));
288
- }
289
-
290
- public get router(): Router {
291
- return this._router;
292
- }
293
- }
294
- ```
295
-
296
- ### 5. DTO Implementation
297
-
298
- Create DTOs to validate and sanitize input data:
299
-
300
- **Create DTO** (`src/dto/input/[entity]/[entity].create.dto.ts`):
301
- ```typescript
302
- export class [Entity]CreateInputDTO {
303
- // Define only allowed properties
304
- property1: type;
305
- property2: type;
306
-
307
- constructor(data: any){
308
- this.property1 = data.property1;
309
- this.property2 = data.property2;
310
- // Set defaults for optional properties
311
- }
312
-
313
- public build(): this {
314
- return this;
315
- }
316
- }
317
- ```
318
-
319
- **Update DTO** (`src/dto/input/[entity]/[entity].update.dto.ts`):
320
- ```typescript
321
- export class [Entity]UpdateInputDTO {
322
- // Define optional properties
323
- property1?: type;
324
- property2?: type;
325
-
326
- constructor(data: any){
327
- // Only set properties that are present in the input
328
- if (data.property1 !== undefined) this.property1 = data.property1;
329
- if (data.property2 !== undefined) this.property2 = data.property2;
330
- }
331
-
332
- public build(): this {
333
- // Remove any properties that weren't set
334
- const cleanData: any = {};
335
- if (this.property1 !== undefined) cleanData.property1 = this.property1;
336
- if (this.property2 !== undefined) cleanData.property2 = this.property2;
337
-
338
- // Clear all properties and reassign only the allowed ones
339
- Object.keys(this).forEach(key => delete (this as any)[key]);
340
- Object.assign(this, cleanData);
341
-
342
- return this;
343
- }
344
- }
345
- ```
346
-
347
- ### 6. Working with Related Entities
348
-
349
- When your entity has foreign key relationships, DAOs can include related entities using PostgreSQL's `to_jsonb()` function:
350
-
351
- ```typescript
352
- async getById(id: number): Promise<IEntity | null> {
353
- const result = await this._knex("entity as e")
354
- .leftJoin("related as r", "e.relatedId", "r.id")
355
- .select("e.*", this._knex.raw("to_jsonb(r.*) as related"))
356
- .where("e.id", id)
357
- .first();
358
- return result || null;
359
- }
360
- ```
361
-
362
- ### 7. Important Notes
363
- - Always use `.bind()` when assigning controller methods to router to maintain proper context
364
- - Use UUID for public-facing endpoints (params) but convert to ID for internal DAO operations
365
- - Return appropriate HTTP status codes (200, 201, 404, etc.)
366
- - Pass errors to the next() function for centralized error handling
367
- - Use paginationHelper from @sundaysf/utils for consistent pagination
368
- - Use DTOs to validate and sanitize input data, preventing unwanted fields from being processed
369
- - DTOs ensure only allowed fields are passed to the DAO layer
370
- - Update DTOs should handle partial updates properly
371
- - Always generate UUID in the controller for new resources (not in DTO or client-side)
372
- - All API responses must follow the standard format: `{success: boolean, data?: any, message?: string}`
373
- - Use status 200 for successful DELETE operations (not 204) to include success message
374
- - Import DAOs and interfaces from `../db/src` (relative path from controllers)
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Commands
6
+
7
+ ### Development
8
+
9
+ - `npm run start:dev` - Start development server with auto-reload (uses nodemon)
10
+ - `npm run build` - Compile TypeScript to JavaScript (output to dist/)
11
+ - `npm start` - Run production server from compiled dist/
12
+
13
+ ### Code Quality
14
+
15
+ - `npm run format` - Format code with Prettier
16
+ - No linting command configured - consider using ESLint with the installed configuration
17
+
18
+ ### Testing
19
+
20
+ - `npm test` - Run tests with Jest (no test files found yet)
21
+
22
+ ### Utilities
23
+
24
+ - `npm run create:controller` - Generate new controller using Sundays Framework CLI
25
+
26
+ ### Database
27
+
28
+ - `npm run migrate:create` - Create a new migration file in TypeScript
29
+ - `npm run migrate:deploy` - Apply pending migrations
30
+ - `npm run seed:create` - Create a new seed file in TypeScript
31
+ - `npm run seed:run` - Execute seed files
32
+ - `npm run db:build` - Compile the db module TypeScript to JavaScript
33
+ - `npm run db:publish` - Publish the db module as an independent npm package
34
+
35
+ ## Architecture
36
+
37
+ This is a Sundays Framework backend built with Express.js and TypeScript following a modular MVC pattern, with an embedded Knex.js database module in `db/`.
38
+
39
+ ### Core Structure
40
+
41
+ - **Entry Points**: `src/server.ts` initializes the server and connects to the database, `src/app.ts` configures Express middleware
42
+ - **Routing**: Dynamic route loading system in `src/routes/index.ts` that automatically discovers and mounts routers from subdirectories
43
+ - **Controllers**: Business logic separated into controller classes (e.g., `HealthController`)
44
+ - **Middleware**: Error handling middleware in `src/middlewares/error/`
45
+ - **Configuration**: CORS origins managed in `src/common/config/origins/`
46
+ - **Database**: `db/` contains the Knex.js module with DAOs, migrations, seeds, and interfaces
47
+
48
+ ### Key Patterns
49
+
50
+ 1. **Router Auto-Discovery**: The `IndexRouter` class scans the routes directory and automatically mounts any `*.router.ts` files found in subdirectories. Routes are mounted at `/{folder-name}`.
51
+
52
+ 2. **Controller Pattern**: Each router has a corresponding controller class that handles the business logic. Controllers are bound to router methods using `.bind()` to maintain proper context.
53
+
54
+ 3. **Environment Configuration**: Uses dotenv for environment variables. Server port defaults to 3005 if not specified in .env.
55
+
56
+ 4. **CORS Configuration**: Dynamic CORS origin configuration via `getAllowedOrigins()` function.
57
+
58
+ 5. **Database Connection**: `src/server.ts` calls `KnexManager.connect()` before starting the Express server. The KnexManager singleton manages connection pooling.
59
+
60
+ 6. **Dependencies**:
61
+ - Sundays Framework utilities (`@sundaysf/utils`) for pagination and validation
62
+ - Standard Express middleware (cors, morgan, etc.)
63
+ - Knex.js + PostgreSQL for database access
64
+
65
+ ### Database Module (`db/`)
66
+
67
+ The `db/` directory is an independently publishable Knex.js module:
68
+
69
+ - **KnexManager** (`db/src/KnexConnection.ts`): Singleton for managing database connections with pooling and SSL support
70
+ - **DAO Pattern**: Data Access Objects implement `IBaseDAO<T>` with CRUD + pagination
71
+ - **Migrations**: `db/migrations/` - Knex migration files in TypeScript
72
+ - **Seeds**: `db/seeds/` - Seed data files
73
+ - **Interfaces**: `db/src/interfaces/` - Entity type definitions
74
+ - **Configuration**: `db/knexfile.ts` - Knex configuration for all environments
75
+
76
+ #### File Structure Conventions
77
+
78
+ **Interfaces**: `db/src/interfaces/{entity}/{entity}.interfaces.ts`
79
+ **DAOs**: `db/src/dao/{entity}/{entity}.dao.ts`
80
+
81
+ #### Environment Variables (Database)
82
+
83
+ - `SQL_HOST` - PostgreSQL host
84
+ - `SQL_PORT` - Database port (defaults to 5432)
85
+ - `SQL_USER` - Database user
86
+ - `SQL_PASSWORD` - Database password
87
+ - `SQL_DB_NAME` - Database name
88
+ - `SQL_REJECT_UNAUTHORIZED` - Set to 'false' to disable SSL certificate verification
89
+
90
+ ### TypeScript Configuration
91
+
92
+ - **Backend** (`tsconfig.json`): Strict mode, ES2022, CommonJS. Source in `./src`, output to `./dist`. Excludes `db/`.
93
+ - **Database** (`db/tsconfig.json`): Strict mode, ES2022, CommonJS with declarations. Source in `db/src`, output to `db/dist`.
94
+
95
+ ## Controller Implementation Guide
96
+
97
+ When creating new controllers, follow the established pattern demonstrated in the CompanyController:
98
+
99
+ ### 1. Controller Structure
100
+ ```typescript
101
+ import { Request, Response, NextFunction } from "express";
102
+ import { IBaseController } from "../../types";
103
+ import { inputValidator, IInputValidator, paginationHelper } from "@sundaysf/utils";
104
+ import { [Entity]DAO, I[Entity], IDataPaginator } from "../../db/src";
105
+ import { [Entity]CreateInputDTO } from "../../dto/input/[entity]/[entity].create.dto";
106
+ import { [Entity]UpdateInputDTO } from "../../dto/input/[entity]/[entity].update.dto";
107
+ import { v4 as uuidv4 } from 'uuid';
108
+
109
+ export class [Entity]Controller implements IBaseController {
110
+ private _[entity]DAO: [Entity]DAO = new [Entity]DAO();
111
+
112
+ // Implement CRUD methods
113
+ }
114
+ ```
115
+
116
+ ### 2. API Response Format
117
+
118
+ All API responses must follow a consistent format:
119
+
120
+ **Success Response**:
121
+ ```json
122
+ {
123
+ "success": true,
124
+ "data": {...} // or "message": "Action completed successfully" for operations without data
125
+ }
126
+ ```
127
+
128
+ **Error Response**:
129
+ ```json
130
+ {
131
+ "success": false,
132
+ "message": "Error description"
133
+ }
134
+ ```
135
+
136
+ **Paginated Response** (from IDataPaginator):
137
+ ```json
138
+ {
139
+ "success": true,
140
+ "data": [...],
141
+ "page": 1,
142
+ "limit": 10,
143
+ "count": 10,
144
+ "totalCount": 100,
145
+ "totalPages": 10
146
+ }
147
+ ```
148
+
149
+ Note: The `IDataPaginator` interface already includes the standard response format with `success` and `data` fields, so responses from paginated endpoints should be returned directly without additional wrapping.
150
+
151
+ ### 3. Standard CRUD Methods
152
+
153
+ **getAll** - List with pagination:
154
+ ```typescript
155
+ public async getAll(req: Request, res: Response, next: NextFunction): Promise<void> {
156
+ try {
157
+ const {page, limit} = paginationHelper(req);
158
+ const result: IDataPaginator<I[Entity]> = await this._[entity]DAO.getAll(page, limit);
159
+ res.status(200).json(result); // IDataPaginator already includes success and data fields
160
+ } catch (err: any) {
161
+ next(err);
162
+ }
163
+ }
164
+ ```
165
+
166
+ **getByUuid** - Get single resource:
167
+ ```typescript
168
+ public async getByUuid(req: Request, res: Response, next: NextFunction): Promise<void> {
169
+ try {
170
+ const { uuid } = req.params;
171
+ const result = await this._[entity]DAO.getByUuid(uuid);
172
+ if (!result) {
173
+ res.status(404).json({ success: false, message: "[Entity] not found" });
174
+ return;
175
+ }
176
+ res.status(200).json({
177
+ success: true,
178
+ data: result
179
+ });
180
+ } catch (err: any) {
181
+ next(err);
182
+ }
183
+ }
184
+ ```
185
+
186
+ **create** - Create new resource with DTO validation:
187
+ ```typescript
188
+ public async create(req: Request, res: Response, next: NextFunction): Promise<void> {
189
+ try {
190
+ const data = req.body;
191
+ const inputDTO = new [Entity]CreateInputDTO(data).build();
192
+ const validation: IInputValidator = await inputValidator(inputDTO);
193
+ if (!validation.success) {
194
+ req.statusCode = 400;
195
+ return next(new Error(validation.message));
196
+ }
197
+ const dataToCreate = {...inputDTO, uuid: uuidv4()};
198
+ const result = await this._[entity]DAO.create(dataToCreate);
199
+ res.status(201).json({
200
+ success: true,
201
+ data: result
202
+ });
203
+ } catch (err: any) {
204
+ next(err);
205
+ }
206
+ }
207
+ ```
208
+
209
+ **update** - Update existing resource with DTO validation:
210
+ ```typescript
211
+ public async update(req: Request, res: Response, next: NextFunction): Promise<void> {
212
+ try {
213
+ const { uuid } = req.params;
214
+ const data = req.body;
215
+
216
+ // First get the entity by UUID to find its ID
217
+ const existing = await this._[entity]DAO.getByUuid(uuid);
218
+ if (!existing || !existing.id) {
219
+ res.status(404).json({ success: false, message: "[Entity] not found" });
220
+ return;
221
+ }
222
+
223
+ const inputDTO = new [Entity]UpdateInputDTO(data).build();
224
+ const validation: IInputValidator = await inputValidator(inputDTO);
225
+ if (!validation.success) {
226
+ req.statusCode = 400;
227
+ return next(new Error(validation.message));
228
+ }
229
+
230
+ const result = await this._[entity]DAO.update(existing.id, inputDTO);
231
+ res.status(200).json({
232
+ success: true,
233
+ data: result
234
+ });
235
+ } catch (err: any) {
236
+ next(err);
237
+ }
238
+ }
239
+ ```
240
+
241
+ **delete** - Delete resource:
242
+ ```typescript
243
+ public async delete(req: Request, res: Response, next: NextFunction): Promise<void> {
244
+ try {
245
+ const { uuid } = req.params;
246
+
247
+ // First get the entity by UUID to find its ID
248
+ const existing = await this._[entity]DAO.getByUuid(uuid);
249
+ if (!existing || !existing.id) {
250
+ res.status(404).json({ success: false, message: "[Entity] not found" });
251
+ return;
252
+ }
253
+
254
+ const result = await this._[entity]DAO.delete(existing.id);
255
+ if (result) {
256
+ res.status(200).json({ success: true, message: "[Entity] deleted successfully" });
257
+ } else {
258
+ res.status(404).json({ success: false, message: "Failed to delete [entity]" });
259
+ }
260
+ } catch (err: any) {
261
+ next(err);
262
+ }
263
+ }
264
+ ```
265
+
266
+ ### 4. Router Implementation
267
+ Create a corresponding router in `src/routes/[entity]/[entity].router.ts`:
268
+
269
+ ```typescript
270
+ import { Router } from "express";
271
+ import { [Entity]Controller } from "../../controllers/[entity]/[entity].controller";
272
+
273
+ export class [Entity]Router {
274
+ private _router: Router;
275
+ private _[entity]Controller = new [Entity]Controller();
276
+
277
+ constructor() {
278
+ this._router = Router();
279
+ this.initRoutes();
280
+ }
281
+
282
+ private initRoutes(): void {
283
+ this._router.get("/", this._[entity]Controller.getAll.bind(this._[entity]Controller));
284
+ this._router.get("/:uuid", this._[entity]Controller.getByUuid.bind(this._[entity]Controller));
285
+ this._router.post("/", this._[entity]Controller.create.bind(this._[entity]Controller));
286
+ this._router.put("/:uuid", this._[entity]Controller.update.bind(this._[entity]Controller));
287
+ this._router.delete("/:uuid", this._[entity]Controller.delete.bind(this._[entity]Controller));
288
+ }
289
+
290
+ public get router(): Router {
291
+ return this._router;
292
+ }
293
+ }
294
+ ```
295
+
296
+ ### 5. DTO Implementation
297
+
298
+ Create DTOs to validate and sanitize input data:
299
+
300
+ **Create DTO** (`src/dto/input/[entity]/[entity].create.dto.ts`):
301
+ ```typescript
302
+ export class [Entity]CreateInputDTO {
303
+ // Define only allowed properties
304
+ property1: type;
305
+ property2: type;
306
+
307
+ constructor(data: any){
308
+ this.property1 = data.property1;
309
+ this.property2 = data.property2;
310
+ // Set defaults for optional properties
311
+ }
312
+
313
+ public build(): this {
314
+ return this;
315
+ }
316
+ }
317
+ ```
318
+
319
+ **Update DTO** (`src/dto/input/[entity]/[entity].update.dto.ts`):
320
+ ```typescript
321
+ export class [Entity]UpdateInputDTO {
322
+ // Define optional properties
323
+ property1?: type;
324
+ property2?: type;
325
+
326
+ constructor(data: any){
327
+ // Only set properties that are present in the input
328
+ if (data.property1 !== undefined) this.property1 = data.property1;
329
+ if (data.property2 !== undefined) this.property2 = data.property2;
330
+ }
331
+
332
+ public build(): this {
333
+ // Remove any properties that weren't set
334
+ const cleanData: any = {};
335
+ if (this.property1 !== undefined) cleanData.property1 = this.property1;
336
+ if (this.property2 !== undefined) cleanData.property2 = this.property2;
337
+
338
+ // Clear all properties and reassign only the allowed ones
339
+ Object.keys(this).forEach(key => delete (this as any)[key]);
340
+ Object.assign(this, cleanData);
341
+
342
+ return this;
343
+ }
344
+ }
345
+ ```
346
+
347
+ ### 6. Working with Related Entities
348
+
349
+ When your entity has foreign key relationships, DAOs can include related entities using PostgreSQL's `to_jsonb()` function:
350
+
351
+ ```typescript
352
+ async getById(id: number): Promise<IEntity | null> {
353
+ const result = await this._knex("entity as e")
354
+ .leftJoin("related as r", "e.relatedId", "r.id")
355
+ .select("e.*", this._knex.raw("to_jsonb(r.*) as related"))
356
+ .where("e.id", id)
357
+ .first();
358
+ return result || null;
359
+ }
360
+ ```
361
+
362
+ ### 7. Important Notes
363
+ - Always use `.bind()` when assigning controller methods to router to maintain proper context
364
+ - Use UUID for public-facing endpoints (params) but convert to ID for internal DAO operations
365
+ - Return appropriate HTTP status codes (200, 201, 404, etc.)
366
+ - Pass errors to the next() function for centralized error handling
367
+ - Use paginationHelper from @sundaysf/utils for consistent pagination
368
+ - Use DTOs to validate and sanitize input data, preventing unwanted fields from being processed
369
+ - DTOs ensure only allowed fields are passed to the DAO layer
370
+ - Update DTOs should handle partial updates properly
371
+ - Always generate UUID in the controller for new resources (not in DTO or client-side)
372
+ - All API responses must follow the standard format: `{success: boolean, data?: any, message?: string}`
373
+ - Use status 200 for successful DELETE operations (not 204) to include success message
374
+ - Import DAOs and interfaces from `../db/src` (relative path from controllers)