create-warlock 4.0.116 → 4.0.119

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 (49) hide show
  1. package/cjs/helpers/app.d.ts.map +1 -1
  2. package/cjs/helpers/app.js +0 -1
  3. package/cjs/helpers/app.js.map +1 -1
  4. package/esm/helpers/app.d.ts.map +1 -1
  5. package/esm/helpers/app.js +0 -1
  6. package/esm/helpers/app.js.map +1 -1
  7. package/package.json +1 -1
  8. package/templates/warlock/.husky/pre-commit +4 -0
  9. package/templates/warlock/.vscode/settings.json +41 -0
  10. package/templates/warlock/docs/new-module.md +509 -509
  11. package/templates/warlock/package.json +12 -10
  12. package/templates/warlock/src/app/auth/controllers/forgot-password.controller.ts +11 -2
  13. package/templates/warlock/src/app/auth/controllers/login.controller.ts +4 -1
  14. package/templates/warlock/src/app/auth/controllers/logout-all.controller.ts +10 -2
  15. package/templates/warlock/src/app/auth/controllers/logout.controller.ts +10 -2
  16. package/templates/warlock/src/app/auth/controllers/me.controller.ts +9 -2
  17. package/templates/warlock/src/app/auth/controllers/refresh-token.controller.ts +11 -2
  18. package/templates/warlock/src/app/auth/controllers/reset-password.controller.ts +4 -1
  19. package/templates/warlock/src/app/auth/main.ts +6 -2
  20. package/templates/warlock/src/app/auth/routes.ts +1 -1
  21. package/templates/warlock/src/app/auth/services/otp.service.ts +15 -5
  22. package/templates/warlock/src/app/posts/controllers/create-new-post.controller.ts +5 -2
  23. package/templates/warlock/src/app/posts/controllers/update-post.controller.ts +7 -3
  24. package/templates/warlock/src/app/posts/models/post/migrations/09-01-2026_02-07-51-post.migration.ts +1 -1
  25. package/templates/warlock/src/app/posts/models/post/post.model.ts +25 -0
  26. package/templates/warlock/src/app/posts/resources/post.resource.ts +14 -0
  27. package/templates/warlock/src/app/posts/routes.ts +5 -2
  28. package/templates/warlock/src/app/{utils → shared/utils}/router.ts +0 -10
  29. package/templates/warlock/src/app/uploads/controllers/fetch-uploaded-file.controller.ts +4 -1
  30. package/templates/warlock/src/app/users/controllers/create-new-user.controller.ts +4 -1
  31. package/templates/warlock/src/app/users/controllers/get-users.controller.ts +9 -2
  32. package/templates/warlock/src/app/users/events/inject-created-by-user.into-model.event.ts +1 -1
  33. package/templates/warlock/src/app/users/events/sync.ts +1 -1
  34. package/templates/warlock/src/app/users/main.ts +1 -20
  35. package/templates/warlock/src/app/users/models/user/user.model.ts +14 -6
  36. package/templates/warlock/src/app/users/routes.ts +1 -1
  37. package/templates/warlock/src/app/users/services/login-social.ts +4 -1
  38. package/templates/warlock/src/config/app.ts +1 -1
  39. package/templates/warlock/src/config/auth.ts +1 -1
  40. package/templates/warlock/src/config/cache.ts +3 -2
  41. package/templates/warlock/src/config/database.ts +6 -3
  42. package/templates/warlock/src/config/http.ts +1 -1
  43. package/templates/warlock/src/config/mail.ts +1 -1
  44. package/templates/warlock/src/config/storage.ts +6 -1
  45. package/templates/warlock/.env +0 -39
  46. package/templates/warlock/.prettierrc +0 -8
  47. package/templates/warlock/AGENTS.md +0 -11
  48. package/templates/warlock/src/app/posts/models/post/psot.model.ts +0 -42
  49. package/templates/warlock/src/app/utils/cloud-upload.middleware.ts +0 -14
@@ -1,509 +1,509 @@
1
- # Create a new module
2
-
3
- Each module should consist of the following structure:
4
-
5
- ```
6
- src/app/[module-name]/
7
- ├── main.ts # Entry point (auto-imported by Warlock.js)
8
- ├── routes.ts # Main routing file (auto-imported by Warlock.js)
9
- ├── controllers/ # Request handlers
10
- │ ├── [feature]/ # Grouped by feature (e.g., auth, profile)
11
- │ │ └── *.controller.ts
12
- │ └── [module].restful.ts # RESTful resource controller (optional)
13
- ├── services/ # Business logic layer
14
- │ ├── *.service.ts
15
- ├── mail/ # Business logic layer
16
- │ └── *.ts
17
- ├── repositories/ # Data access layer
18
- │ └── [resource].repository.ts
19
- ├── models/ # Database models
20
- │ └── [model-name]/
21
- │ ├── index.ts
22
- │ ├── [model-name].model.ts
23
- │ └── migrations/
24
- │ └── [date]_[model-name].migration.ts
25
- ├── requests/ # Request validation schemas
26
- │ ├── index.ts # Simple validations (if all in one file)
27
- │ └── *.request.ts # Individual validation files
28
- ├── resources/ # Response resource transformers
29
- │ └── *.resource.ts
30
- ├── events/ # Event handlers/listeners (auto-imported by Warlock.js)
31
- │ └── *.ts
32
- ├── components/ # Reusable components for use within mails
33
- │ └── *.ts
34
- ├── types/ # TypeScript type definitions
35
- │ └── *.ts
36
- └── utils/ # Module-specific utilities
37
- ├── locales.ts # Translations (auto-imported by Warlock.js)
38
- └── flags.ts # Constants/flags
39
- ```
40
-
41
- ## Directory Structure Details
42
-
43
- ### 1. main.ts (Auto-imported)
44
-
45
- Entry point file that is automatically imported by Warlock.js when the module loads. Use this for module initialization, event listeners, or setup code that should run when the module is loaded.
46
-
47
- **Example:**
48
-
49
- ```typescript
50
- import { onceConnected } from "@warlock.js/cascade";
51
-
52
- // This function will be called once the app is connected to the database
53
- onceConnected(async () => {
54
- // Module initialization code
55
- // Register event listeners
56
- // Setup module-specific configurations
57
- });
58
- ```
59
-
60
- ### 2. routes.ts (Auto-imported)
61
-
62
- Main routing file that defines all module endpoints. This file is automatically imported by Warlock.js. Use router guards (`guarded`, `guardedAdmin`, `guardedGuest`, etc.) to protect routes.
63
-
64
- **Example:**
65
-
66
- ```typescript
67
- import { router } from "@warlock.js/core";
68
- import { guarded, guardedGuest } from "app/utils/router";
69
- import { createAccountController } from "./controllers/auth/create-account.controller";
70
- import { myProfile } from "./controllers/profile/my-profile.controller";
71
-
72
- guardedGuest(() => {
73
- router.post("/register", createAccountController);
74
- });
75
-
76
- guarded(() => {
77
- router.get("/me", myProfile);
78
- });
79
- ```
80
-
81
- ### 3. controllers/
82
-
83
- Request handlers that process HTTP requests. Organize controllers by feature (e.g., `auth/`, `profile/`).
84
-
85
- **Controller Structure:**
86
-
87
- ```typescript
88
- import type { Request, RequestHandler, Response } from "@warlock.js/core";
89
- import { someService } from "app/[module]/services/some.service";
90
- import { someRequestSchema, type SomeRequest } from "app/[module]/requests/some.request";
91
-
92
- export const someController: RequestHandler = async (request: SomeRequest, response: Response) => {
93
- const data = await someService(request.validated());
94
- return response.success({ data });
95
- };
96
-
97
- someController.validation = {
98
- schema: someRequestSchema,
99
- };
100
- ```
101
-
102
- **For RESTful Resources:**
103
-
104
- ```typescript
105
- import { Restful, type RouteResource, v } from "@warlock.js/core";
106
- import { SomeModel } from "../models/some";
107
- import { someRepository } from "../repositories/some.repository";
108
-
109
- class RestfulSome extends Restful<SomeModel> implements RouteResource {
110
- protected repository = someRepository;
111
-
112
- public validation: RouteResource["validation"] = {
113
- create: {
114
- schema: v.object({
115
- name: v.string().required(),
116
- }),
117
- },
118
- };
119
- }
120
-
121
- export const restfulSome = new RestfulSome();
122
- ```
123
-
124
- ### 4. services/
125
-
126
- Business logic layer. Services handle the core functionality and interact with repositories.
127
-
128
- **Service Structure:**
129
-
130
- ```typescript
131
- import type { SomeModel } from "app/[module]/models/some";
132
- import { someRepository } from "app/[module]/repositories/some.repository";
133
-
134
- export async function someService(data: Record<string, any>): Promise<SomeModel> {
135
- return await someRepository.create(data);
136
- }
137
- ```
138
-
139
- ### 5. repositories/
140
-
141
- Data access layer that extends `RepositoryManager`. Handles database queries and filtering.
142
-
143
- **Repository Structure:**
144
-
145
- ```typescript
146
- import type { FilterByOptions, RepositoryOptions } from "@warlock.js/core";
147
- import { RepositoryManager } from "@warlock.js/core";
148
- import { SomeModel } from "../models/some";
149
-
150
- export class SomeRepository extends RepositoryManager<SomeModel> {
151
- public source = SomeModel;
152
-
153
- protected defaultOptions: RepositoryOptions = this.withDefaultOptions({});
154
-
155
- protected filterBy: FilterByOptions = this.withDefaultFilters({
156
- name: "like",
157
- isActive: "bool",
158
- });
159
- }
160
-
161
- export const someRepository = new SomeRepository();
162
- ```
163
-
164
- ### 6. models/
165
-
166
- Database models that extend base model classes (e.g., `Model`, `Auth`). Include migrations in the `migrations/` subdirectory.
167
-
168
- **Model Structure:**
169
-
170
- ```typescript
171
- import { Model } from "@warlock.js/core";
172
- import type { StrictMode } from "@warlock.js/cascade";
173
- import { SomeResource } from "../../resources/some.resource";
174
- import { v, type Infer } from "@warlock.js/seal";
175
-
176
- const someModelSchema = v.object({
177
- name: v.string().required(),
178
- email: v.email().required(),
179
- });
180
-
181
- type SomeModelType = Infer<typeof someModelSchema>;
182
-
183
- export class SomeModel extends Model<SomeModelType> {
184
- public static table = "somes";
185
- public static strictMode: StrictMode = "fail";
186
- public static resource = SomeResource;
187
-
188
- public static schema = someModelSchema;
189
-
190
- public embed = ["id", "name"];
191
- }
192
- ```
193
-
194
- ### 7. requests/
195
-
196
- Request handlers that process HTTP requests. Organize controllers by feature (e.g., `auth/`, `profile/`).
197
-
198
- **Request Structure:**
199
-
200
- ```typescript
201
- import type { Request, RequestHandler, Response } from "@warlock.js/core";
202
- import { someService } from "app/[module]/services/some.service";
203
- import { someRequestSchema, type SomeRequest } from "app/[module]/requests/some.request";
204
-
205
- export const someController: RequestHandler = async (request: SomeRequest, response: Response) => {
206
- const data = await someService(request.validated());
207
- return response.success({ data });
208
- };
209
-
210
- someController.validation = {
211
- schema: someRequestSchema,
212
- };
213
- ```
214
-
215
- **Important Rules:**
216
-
217
- - If a module has simple validations, use `index.ts` to export all schemas
218
- - If requests are complex or numerous, use separate `.request.ts` files
219
- - Always export the schema and its inferred type
220
- - Use `Infer<typeof schema>` (similar to Zod) to generate TypeScript types
221
-
222
- **Simple Validation (index.ts):**
223
-
224
- ```typescript
225
- import { v, type Infer } from "@warlock.js/seal";
226
- import { type Request } from "@warlock.js/core";
227
- import { type User } from "app/users/models/user";
228
-
229
- export const createSchema = v.object({
230
- name: v.string().minLength(2).required(),
231
- });
232
-
233
- export const updateSchema = v.object({
234
- name: v.string().minLength(2).optional(),
235
- });
236
-
237
- export type CreateData = Infer<typeof createSchema>;
238
- export type UpdateData = Infer<typeof updateSchema>;
239
-
240
- export type CreatePostRequest<User, CreateData>;
241
- export type UpdatePostRequest<User, UpdateData>;
242
- ```
243
-
244
- **Individual Validation Files:**
245
-
246
- ```typescript
247
- // requests/create-account.request.ts
248
- import { v, type Infer } from "@warlock.js/seal";
249
- import { type Request } from "@warlock.js/core";
250
-
251
- export const createAccountSchema = v.object({
252
- name: v.string().minLength(2).required(),
253
- email: vemail().required(),
254
- password: v.string().required().strongPassword(),
255
- });
256
-
257
- export type CreateAccountData = Infer<typeof createAccountSchema>;
258
-
259
- export type CreateAccountRequest<undefined, CreateAccountData>;
260
- ```
261
-
262
- **Usage in Controller:**
263
-
264
- ```typescript
265
- import type { Request, RequestHandler, Response } from "@warlock.js/core";
266
- import type { User } from "app/[module]/models/user";
267
- import {
268
- createAccountSchema,
269
- type CreateAccountRequest,
270
- } from "app/[module]/requests/create-account.request";
271
-
272
- export const createAccountController: RequestHandler = async (
273
- request: CreateAccountRequest,
274
- response: Response,
275
- ) => {
276
- // request.validated() is now typed as CreateAccountData
277
- const data = request.validated();
278
- // ...
279
- };
280
-
281
- createAccountController.validation = {
282
- schema: createAccountSchema,
283
- };
284
- ```
285
-
286
- ### 8. resources/
287
-
288
- Resources define how data is transformed before being sent to clients.
289
-
290
- **Resource Structure:**
291
-
292
- ```typescript
293
- import { Resource } from "@warlock.js/core";
294
- import {} from "app/utils/output";
295
-
296
- export class SomeResource extends Resource {
297
- public schema = {
298
- name: "string",
299
- id: "int",
300
- type: () => "user", // custom output value
301
- image: (value) => (value.startsWith("/") ? value : "/" + value),
302
- };
303
- }
304
- ```
305
-
306
- ### 9. events/ (Auto-imported)
307
-
308
- Event handlers and listeners for model events or application events. **All files in this folder are automatically imported by Warlock.js** - no need to manually import them elsewhere.
309
-
310
- > During development server, It's very important to use export const cleanup for events unbinding to prevent duplicate events registery on hmr.
311
-
312
- **Event Structure:**
313
-
314
- ```typescript
315
- import { Response } from "@warlock.js/core";
316
-
317
- export function someEventHandler(response: Response) {
318
- // Event handling logic
319
- }
320
-
321
- const event = Response.on("sending", someEventHandler);
322
-
323
- export const cleanup = [event];
324
- // or use a callback if the event is not an unsubscribe function
325
- export const cleanup = () => event.unsbcribe();
326
- ```
327
-
328
- **Note:** Files in the `events/` folder are auto-imported, similar to `routes.ts`, `main.ts`, and `utils/locales.ts`. Just create the file and it will be loaded automatically.
329
-
330
- ### 10. services/mail/ (or separate mail/ folder)
331
-
332
- Email service functions for sending notifications. **Mail files should be suffixed with `.mail.ts`** (e.g., `welcome.mail.ts`, `reset-password.mail.ts`).
333
-
334
- You can organize mail services either as:
335
-
336
- - Files in `services/mail/` folder (if you have multiple mail-related services) - **Recommended**
337
- - Individual files in `services/` folder (if you have few mail services)
338
- - A separate `mail/` folder at module root (legacy pattern, still supported)
339
-
340
- **Mail Structure:**
341
-
342
- ```tsx
343
- // services/mail/welcome.mail.ts
344
- import { sendMail } from "@warlock.js/core";
345
- import type { SomeModel } from "../models/some";
346
- import { SomeEmailComponent } from "../components/some-email.component";
347
-
348
- export default async function sendWelcomeEmail(model: SomeModel) {
349
- await sendMail({
350
- to: model.get("email"),
351
- subject: "Subject",
352
- component: <SomeEmailComponent name={model.get("name")} />,
353
- });
354
- }
355
- ```
356
-
357
- **Recommendation:** Use `services/mail/` folder for better organization when you have multiple mail templates, and always suffix mail files with `.mail.ts`.
358
-
359
- ### 11. components/
360
-
361
- Reusable components for use within email templates. These are typically functions that return HTML strings or React-like components that can be used in mail services.
362
-
363
- **Component Structure:**
364
-
365
- ```tsx
366
- export function WelcomeEmailComponent(data: { name: string; email: string }) {
367
- return `
368
- <div>
369
- <h1>Welcome, ${data.name}!</h1>
370
- <p>Your email: ${data.email}</p>
371
- </div>
372
- `;
373
- }
374
- ```
375
-
376
- **Usage in Mail:**
377
-
378
- ```tsx
379
- import { sendMail } from "@warlock.js/core";
380
- import { WelcomeEmailComponent } from "../components/welcome-email.component";
381
-
382
- export default async function sendWelcomeEmail(user: User) {
383
- await sendMail({
384
- to: user.get("email"),
385
- subject: "Welcome",
386
- component: <WelcomeEmailComponent name={user.get("name")} email={user.get("email")} />,
387
- });
388
- }
389
- ```
390
-
391
- ### 12. types/
392
-
393
- TypeScript type definitions specific to the module. Use this folder for:
394
-
395
- - Request/response types
396
- - Service parameter types
397
- - Module-specific interfaces
398
- - Type utilities
399
-
400
- **Types Structure:**
401
-
402
- ```typescript
403
- // types/user.types.ts
404
- export interface UserPreferences {
405
- theme: "light" | "dark";
406
- notifications: boolean;
407
- }
408
-
409
- export type UserRole = "admin" | "user" | "guest";
410
- ```
411
-
412
- ### 13. utils/
413
-
414
- Module-specific utility functions. **Must include `locales.ts`** which is automatically imported by Warlock.js.
415
-
416
- **utils/locales.ts (Auto-imported):**
417
-
418
- ```typescript
419
- import { groupedTranslations } from "@mongez/localization";
420
-
421
- groupedTranslations("moduleName", {
422
- key1: {
423
- en: "English translation",
424
- ar: "Arabic translation",
425
- },
426
- invalidCredentials: {
427
- en: "Invalid credentials",
428
- ar: "بيانات الدخول غير صحيحة",
429
- },
430
- });
431
- ```
432
-
433
- **utils/flags.ts (Constants):**
434
-
435
- ```typescript
436
- // Module-specific constants and flags
437
- export const USER_STATUS = {
438
- ACTIVE: "active",
439
- INACTIVE: "inactive",
440
- PENDING: "pending",
441
- } as const;
442
-
443
- export const USER_ROLES = {
444
- ADMIN: "admin",
445
- USER: "user",
446
- GUEST: "guest",
447
- } as const;
448
- ```
449
-
450
- **Other Utilities:**
451
-
452
- ```typescript
453
- // utils/helpers.ts or similar
454
- export function formatUserName(user: User): string {
455
- return `${user.get("firstName")} ${user.get("lastName")}`;
456
- }
457
- ```
458
-
459
- **Note:** The `utils/locales.ts` file is automatically imported by Warlock.js, similar to `routes.ts`, `main.ts`, and files in the `events/` folder.
460
-
461
- ## Best Practices
462
-
463
- 1. **Naming Conventions:**
464
- - Use kebab-case for file names
465
- - Use PascalCase for class names
466
- - Use camelCase for functions and variables
467
-
468
- 2. **Controller Organization:**
469
- - Group related controllers in subdirectories (e.g., `auth/`, `profile/`)
470
- - Keep controllers thin - delegate business logic to services
471
-
472
- 3. **Service Layer:**
473
- - Services should contain reusable business logic
474
- - Services interact with repositories, not directly with models
475
-
476
- 4. **Repository Pattern:**
477
- - All database operations should go through repositories
478
- - Define filter options in the repository class
479
-
480
- 5. **Validation:**
481
- - **All validation schemas must be in the `validation/` folder**
482
- - Always attach validation to controllers using `controller.validation = { schema }`
483
- - Export TypeScript types from validation files using `Infer<typeof schema>`
484
- - Use the inferred type as a generic parameter in `Request<Model, ValidationType>`
485
- - Use `index.ts` for simple validations, separate `.validation.ts` files for complex ones
486
-
487
- 6. **Models:**
488
- - Define casts for all fields
489
- - Specify embedded fields for performance
490
- - Include default values when needed
491
-
492
- 7. **Routes:**
493
- - Use appropriate guards (`guarded`, `guardedAdmin`, `guardedGuest`)
494
- - Group related routes together
495
- - Use RESTful resources when appropriate
496
- - Routes file is auto-imported by Warlock.js
497
-
498
- 8. **Auto-imported Files:**
499
- - `main.ts` - Module entry point
500
- - `routes.ts` - Route definitions
501
- - `utils/locales.ts` - Translations
502
- - All files in `events/` folder - Event handlers
503
- - No need to manually import these files elsewhere
504
-
505
- 9. **Organization:**
506
- - Keep mail services organized: use `services/mail/` folder if multiple, or individual files in `services/` if few
507
- - Use `components/` folder for reusable email components
508
- - Define module-specific types in `types/` folder
509
- - Use `utils/flags.ts` for constants and configuration flags
1
+ # Create a new module
2
+
3
+ Each module should consist of the following structure:
4
+
5
+ ```
6
+ src/app/[module-name]/
7
+ ├── main.ts # Entry point (auto-imported by Warlock.js)
8
+ ├── routes.ts # Main routing file (auto-imported by Warlock.js)
9
+ ├── controllers/ # Request handlers
10
+ │ ├── [feature]/ # Grouped by feature (e.g., auth, profile)
11
+ │ │ └── *.controller.ts
12
+ │ └── [module].restful.ts # RESTful resource controller (optional)
13
+ ├── services/ # Business logic layer
14
+ │ ├── *.service.ts
15
+ ├── mail/ # Business logic layer
16
+ │ └── *.ts
17
+ ├── repositories/ # Data access layer
18
+ │ └── [resource].repository.ts
19
+ ├── models/ # Database models
20
+ │ └── [model-name]/
21
+ │ ├── index.ts
22
+ │ ├── [model-name].model.ts
23
+ │ └── migrations/
24
+ │ └── [date]_[model-name].migration.ts
25
+ ├── requests/ # Request validation schemas
26
+ │ ├── index.ts # Simple validations (if all in one file)
27
+ │ └── *.request.ts # Individual validation files
28
+ ├── resources/ # Response resource transformers
29
+ │ └── *.resource.ts
30
+ ├── events/ # Event handlers/listeners (auto-imported by Warlock.js)
31
+ │ └── *.ts
32
+ ├── components/ # Reusable components for use within mails
33
+ │ └── *.ts
34
+ ├── types/ # TypeScript type definitions
35
+ │ └── *.ts
36
+ └── utils/ # Module-specific utilities
37
+ ├── locales.ts # Translations (auto-imported by Warlock.js)
38
+ └── flags.ts # Constants/flags
39
+ ```
40
+
41
+ ## Directory Structure Details
42
+
43
+ ### 1. main.ts (Auto-imported)
44
+
45
+ Entry point file that is automatically imported by Warlock.js when the module loads. Use this for module initialization, event listeners, or setup code that should run when the module is loaded.
46
+
47
+ **Example:**
48
+
49
+ ```typescript
50
+ import { onceConnected } from "@warlock.js/cascade";
51
+
52
+ // This function will be called once the app is connected to the database
53
+ onceConnected(async () => {
54
+ // Module initialization code
55
+ // Register event listeners
56
+ // Setup module-specific configurations
57
+ });
58
+ ```
59
+
60
+ ### 2. routes.ts (Auto-imported)
61
+
62
+ Main routing file that defines all module endpoints. This file is automatically imported by Warlock.js. Use router guards (`guarded`, `guardedAdmin`, `guardedGuest`, etc.) to protect routes.
63
+
64
+ **Example:**
65
+
66
+ ```typescript
67
+ import { router } from "@warlock.js/core";
68
+ import { guarded, guardedGuest } from "app/utils/router";
69
+ import { createAccountController } from "./controllers/auth/create-account.controller";
70
+ import { myProfile } from "./controllers/profile/my-profile.controller";
71
+
72
+ guardedGuest(() => {
73
+ router.post("/register", createAccountController);
74
+ });
75
+
76
+ guarded(() => {
77
+ router.get("/me", myProfile);
78
+ });
79
+ ```
80
+
81
+ ### 3. controllers/
82
+
83
+ Request handlers that process HTTP requests. Organize controllers by feature (e.g., `auth/`, `profile/`).
84
+
85
+ **Controller Structure:**
86
+
87
+ ```typescript
88
+ import type { Request, RequestHandler, Response } from "@warlock.js/core";
89
+ import { someService } from "app/[module]/services/some.service";
90
+ import { someRequestSchema, type SomeRequest } from "app/[module]/requests/some.request";
91
+
92
+ export const someController: RequestHandler = async (request: SomeRequest, response: Response) => {
93
+ const data = await someService(request.validated());
94
+ return response.success({ data });
95
+ };
96
+
97
+ someController.validation = {
98
+ schema: someRequestSchema,
99
+ };
100
+ ```
101
+
102
+ **For RESTful Resources:**
103
+
104
+ ```typescript
105
+ import { Restful, type RouteResource, v } from "@warlock.js/core";
106
+ import { SomeModel } from "../models/some";
107
+ import { someRepository } from "../repositories/some.repository";
108
+
109
+ class RestfulSome extends Restful<SomeModel> implements RouteResource {
110
+ protected repository = someRepository;
111
+
112
+ public validation: RouteResource["validation"] = {
113
+ create: {
114
+ schema: v.object({
115
+ name: v.string().required(),
116
+ }),
117
+ },
118
+ };
119
+ }
120
+
121
+ export const restfulSome = new RestfulSome();
122
+ ```
123
+
124
+ ### 4. services/
125
+
126
+ Business logic layer. Services handle the core functionality and interact with repositories.
127
+
128
+ **Service Structure:**
129
+
130
+ ```typescript
131
+ import type { SomeModel } from "app/[module]/models/some";
132
+ import { someRepository } from "app/[module]/repositories/some.repository";
133
+
134
+ export async function someService(data: Record<string, any>): Promise<SomeModel> {
135
+ return await someRepository.create(data);
136
+ }
137
+ ```
138
+
139
+ ### 5. repositories/
140
+
141
+ Data access layer that extends `RepositoryManager`. Handles database queries and filtering.
142
+
143
+ **Repository Structure:**
144
+
145
+ ```typescript
146
+ import type { FilterByOptions, RepositoryOptions } from "@warlock.js/core";
147
+ import { RepositoryManager } from "@warlock.js/core";
148
+ import { SomeModel } from "../models/some";
149
+
150
+ export class SomeRepository extends RepositoryManager<SomeModel> {
151
+ public source = SomeModel;
152
+
153
+ protected defaultOptions: RepositoryOptions = this.withDefaultOptions({});
154
+
155
+ protected filterBy: FilterByOptions = this.withDefaultFilters({
156
+ name: "like",
157
+ isActive: "bool",
158
+ });
159
+ }
160
+
161
+ export const someRepository = new SomeRepository();
162
+ ```
163
+
164
+ ### 6. models/
165
+
166
+ Database models that extend base model classes (e.g., `Model`, `Auth`). Include migrations in the `migrations/` subdirectory.
167
+
168
+ **Model Structure:**
169
+
170
+ ```typescript
171
+ import { Model } from "@warlock.js/core";
172
+ import type { StrictMode } from "@warlock.js/cascade";
173
+ import { SomeResource } from "../../resources/some.resource";
174
+ import { v, type Infer } from "@warlock.js/seal";
175
+
176
+ const someModelSchema = v.object({
177
+ name: v.string().required(),
178
+ email: v.email().required(),
179
+ });
180
+
181
+ type SomeModelType = Infer<typeof someModelSchema>;
182
+
183
+ export class SomeModel extends Model<SomeModelType> {
184
+ public static table = "somes";
185
+ public static strictMode: StrictMode = "fail";
186
+ public static resource = SomeResource;
187
+
188
+ public static schema = someModelSchema;
189
+
190
+ public embed = ["id", "name"];
191
+ }
192
+ ```
193
+
194
+ ### 7. requests/
195
+
196
+ Request handlers that process HTTP requests. Organize controllers by feature (e.g., `auth/`, `profile/`).
197
+
198
+ **Request Structure:**
199
+
200
+ ```typescript
201
+ import type { Request, RequestHandler, Response } from "@warlock.js/core";
202
+ import { someService } from "app/[module]/services/some.service";
203
+ import { someRequestSchema, type SomeRequest } from "app/[module]/requests/some.request";
204
+
205
+ export const someController: RequestHandler = async (request: SomeRequest, response: Response) => {
206
+ const data = await someService(request.validated());
207
+ return response.success({ data });
208
+ };
209
+
210
+ someController.validation = {
211
+ schema: someRequestSchema,
212
+ };
213
+ ```
214
+
215
+ **Important Rules:**
216
+
217
+ - If a module has simple validations, use `index.ts` to export all schemas
218
+ - If requests are complex or numerous, use separate `.request.ts` files
219
+ - Always export the schema and its inferred type
220
+ - Use `Infer<typeof schema>` (similar to Zod) to generate TypeScript types
221
+
222
+ **Simple Validation (index.ts):**
223
+
224
+ ```typescript
225
+ import { v, type Infer } from "@warlock.js/seal";
226
+ import { type Request } from "@warlock.js/core";
227
+ import { type User } from "app/users/models/user";
228
+
229
+ export const createSchema = v.object({
230
+ name: v.string().minLength(2).required(),
231
+ });
232
+
233
+ export const updateSchema = v.object({
234
+ name: v.string().minLength(2).optional(),
235
+ });
236
+
237
+ export type CreateData = Infer<typeof createSchema>;
238
+ export type UpdateData = Infer<typeof updateSchema>;
239
+
240
+ export type CreatePostRequest<User, CreateData>;
241
+ export type UpdatePostRequest<User, UpdateData>;
242
+ ```
243
+
244
+ **Individual Validation Files:**
245
+
246
+ ```typescript
247
+ // requests/create-account.request.ts
248
+ import { v, type Infer } from "@warlock.js/seal";
249
+ import { type Request } from "@warlock.js/core";
250
+
251
+ export const createAccountSchema = v.object({
252
+ name: v.string().minLength(2).required(),
253
+ email: vemail().required(),
254
+ password: v.string().required().strongPassword(),
255
+ });
256
+
257
+ export type CreateAccountData = Infer<typeof createAccountSchema>;
258
+
259
+ export type CreateAccountRequest<undefined, CreateAccountData>;
260
+ ```
261
+
262
+ **Usage in Controller:**
263
+
264
+ ```typescript
265
+ import type { Request, RequestHandler, Response } from "@warlock.js/core";
266
+ import type { User } from "app/[module]/models/user";
267
+ import {
268
+ createAccountSchema,
269
+ type CreateAccountRequest,
270
+ } from "app/[module]/requests/create-account.request";
271
+
272
+ export const createAccountController: RequestHandler = async (
273
+ request: CreateAccountRequest,
274
+ response: Response,
275
+ ) => {
276
+ // request.validated() is now typed as CreateAccountData
277
+ const data = request.validated();
278
+ // ...
279
+ };
280
+
281
+ createAccountController.validation = {
282
+ schema: createAccountSchema,
283
+ };
284
+ ```
285
+
286
+ ### 8. resources/
287
+
288
+ Resources define how data is transformed before being sent to clients.
289
+
290
+ **Resource Structure:**
291
+
292
+ ```typescript
293
+ import { Resource } from "@warlock.js/core";
294
+ import {} from "app/utils/output";
295
+
296
+ export class SomeResource extends Resource {
297
+ public schema = {
298
+ name: "string",
299
+ id: "int",
300
+ type: () => "user", // custom output value
301
+ image: (value) => (value.startsWith("/") ? value : "/" + value),
302
+ };
303
+ }
304
+ ```
305
+
306
+ ### 9. events/ (Auto-imported)
307
+
308
+ Event handlers and listeners for model events or application events. **All files in this folder are automatically imported by Warlock.js** - no need to manually import them elsewhere.
309
+
310
+ > During development server, It's very important to use export const cleanup for events unbinding to prevent duplicate events registery on hmr.
311
+
312
+ **Event Structure:**
313
+
314
+ ```typescript
315
+ import { Response } from "@warlock.js/core";
316
+
317
+ export function someEventHandler(response: Response) {
318
+ // Event handling logic
319
+ }
320
+
321
+ const event = Response.on("sending", someEventHandler);
322
+
323
+ export const cleanup = [event];
324
+ // or use a callback if the event is not an unsubscribe function
325
+ export const cleanup = () => event.unsbcribe();
326
+ ```
327
+
328
+ **Note:** Files in the `events/` folder are auto-imported, similar to `routes.ts`, `main.ts`, and `utils/locales.ts`. Just create the file and it will be loaded automatically.
329
+
330
+ ### 10. services/mail/ (or separate mail/ folder)
331
+
332
+ Email service functions for sending notifications. **Mail files should be suffixed with `.mail.ts`** (e.g., `welcome.mail.ts`, `reset-password.mail.ts`).
333
+
334
+ You can organize mail services either as:
335
+
336
+ - Files in `services/mail/` folder (if you have multiple mail-related services) - **Recommended**
337
+ - Individual files in `services/` folder (if you have few mail services)
338
+ - A separate `mail/` folder at module root (legacy pattern, still supported)
339
+
340
+ **Mail Structure:**
341
+
342
+ ```tsx
343
+ // services/mail/welcome.mail.ts
344
+ import { sendMail } from "@warlock.js/core";
345
+ import type { SomeModel } from "../models/some";
346
+ import { SomeEmailComponent } from "../components/some-email.component";
347
+
348
+ export default async function sendWelcomeEmail(model: SomeModel) {
349
+ await sendMail({
350
+ to: model.get("email"),
351
+ subject: "Subject",
352
+ component: <SomeEmailComponent name={model.get("name")} />,
353
+ });
354
+ }
355
+ ```
356
+
357
+ **Recommendation:** Use `services/mail/` folder for better organization when you have multiple mail templates, and always suffix mail files with `.mail.ts`.
358
+
359
+ ### 11. components/
360
+
361
+ Reusable components for use within email templates. These are typically functions that return HTML strings or React-like components that can be used in mail services.
362
+
363
+ **Component Structure:**
364
+
365
+ ```tsx
366
+ export function WelcomeEmailComponent(data: { name: string; email: string }) {
367
+ return `
368
+ <div>
369
+ <h1>Welcome, ${data.name}!</h1>
370
+ <p>Your email: ${data.email}</p>
371
+ </div>
372
+ `;
373
+ }
374
+ ```
375
+
376
+ **Usage in Mail:**
377
+
378
+ ```tsx
379
+ import { sendMail } from "@warlock.js/core";
380
+ import { WelcomeEmailComponent } from "../components/welcome-email.component";
381
+
382
+ export default async function sendWelcomeEmail(user: User) {
383
+ await sendMail({
384
+ to: user.get("email"),
385
+ subject: "Welcome",
386
+ component: <WelcomeEmailComponent name={user.get("name")} email={user.get("email")} />,
387
+ });
388
+ }
389
+ ```
390
+
391
+ ### 12. types/
392
+
393
+ TypeScript type definitions specific to the module. Use this folder for:
394
+
395
+ - Request/response types
396
+ - Service parameter types
397
+ - Module-specific interfaces
398
+ - Type utilities
399
+
400
+ **Types Structure:**
401
+
402
+ ```typescript
403
+ // types/user.types.ts
404
+ export interface UserPreferences {
405
+ theme: "light" | "dark";
406
+ notifications: boolean;
407
+ }
408
+
409
+ export type UserRole = "admin" | "user" | "guest";
410
+ ```
411
+
412
+ ### 13. utils/
413
+
414
+ Module-specific utility functions. **Must include `locales.ts`** which is automatically imported by Warlock.js.
415
+
416
+ **utils/locales.ts (Auto-imported):**
417
+
418
+ ```typescript
419
+ import { groupedTranslations } from "@mongez/localization";
420
+
421
+ groupedTranslations("moduleName", {
422
+ key1: {
423
+ en: "English translation",
424
+ ar: "Arabic translation",
425
+ },
426
+ invalidCredentials: {
427
+ en: "Invalid credentials",
428
+ ar: "بيانات الدخول غير صحيحة",
429
+ },
430
+ });
431
+ ```
432
+
433
+ **utils/flags.ts (Constants):**
434
+
435
+ ```typescript
436
+ // Module-specific constants and flags
437
+ export const USER_STATUS = {
438
+ ACTIVE: "active",
439
+ INACTIVE: "inactive",
440
+ PENDING: "pending",
441
+ } as const;
442
+
443
+ export const USER_ROLES = {
444
+ ADMIN: "admin",
445
+ USER: "user",
446
+ GUEST: "guest",
447
+ } as const;
448
+ ```
449
+
450
+ **Other Utilities:**
451
+
452
+ ```typescript
453
+ // utils/helpers.ts or similar
454
+ export function formatUserName(user: User): string {
455
+ return `${user.get("firstName")} ${user.get("lastName")}`;
456
+ }
457
+ ```
458
+
459
+ **Note:** The `utils/locales.ts` file is automatically imported by Warlock.js, similar to `routes.ts`, `main.ts`, and files in the `events/` folder.
460
+
461
+ ## Best Practices
462
+
463
+ 1. **Naming Conventions:**
464
+ - Use kebab-case for file names
465
+ - Use PascalCase for class names
466
+ - Use camelCase for functions and variables
467
+
468
+ 2. **Controller Organization:**
469
+ - Group related controllers in subdirectories (e.g., `auth/`, `profile/`)
470
+ - Keep controllers thin - delegate business logic to services
471
+
472
+ 3. **Service Layer:**
473
+ - Services should contain reusable business logic
474
+ - Services interact with repositories, not directly with models
475
+
476
+ 4. **Repository Pattern:**
477
+ - All database operations should go through repositories
478
+ - Define filter options in the repository class
479
+
480
+ 5. **Validation:**
481
+ - **All validation schemas must be in the `validation/` folder**
482
+ - Always attach validation to controllers using `controller.validation = { schema }`
483
+ - Export TypeScript types from validation files using `Infer<typeof schema>`
484
+ - Use the inferred type as a generic parameter in `Request<Model, ValidationType>`
485
+ - Use `index.ts` for simple validations, separate `.validation.ts` files for complex ones
486
+
487
+ 6. **Models:**
488
+ - Define casts for all fields
489
+ - Specify embedded fields for performance
490
+ - Include default values when needed
491
+
492
+ 7. **Routes:**
493
+ - Use appropriate guards (`guarded`, `guardedAdmin`, `guardedGuest`)
494
+ - Group related routes together
495
+ - Use RESTful resources when appropriate
496
+ - Routes file is auto-imported by Warlock.js
497
+
498
+ 8. **Auto-imported Files:**
499
+ - `main.ts` - Module entry point
500
+ - `routes.ts` - Route definitions
501
+ - `utils/locales.ts` - Translations
502
+ - All files in `events/` folder - Event handlers
503
+ - No need to manually import these files elsewhere
504
+
505
+ 9. **Organization:**
506
+ - Keep mail services organized: use `services/mail/` folder if multiple, or individual files in `services/` if few
507
+ - Use `components/` folder for reusable email components
508
+ - Define module-specific types in `types/` folder
509
+ - Use `utils/flags.ts` for constants and configuration flags