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.
- package/cjs/helpers/app.d.ts.map +1 -1
- package/cjs/helpers/app.js +0 -1
- package/cjs/helpers/app.js.map +1 -1
- package/esm/helpers/app.d.ts.map +1 -1
- package/esm/helpers/app.js +0 -1
- package/esm/helpers/app.js.map +1 -1
- package/package.json +1 -1
- package/templates/warlock/.husky/pre-commit +4 -0
- package/templates/warlock/.vscode/settings.json +41 -0
- package/templates/warlock/docs/new-module.md +509 -509
- package/templates/warlock/package.json +12 -10
- package/templates/warlock/src/app/auth/controllers/forgot-password.controller.ts +11 -2
- package/templates/warlock/src/app/auth/controllers/login.controller.ts +4 -1
- package/templates/warlock/src/app/auth/controllers/logout-all.controller.ts +10 -2
- package/templates/warlock/src/app/auth/controllers/logout.controller.ts +10 -2
- package/templates/warlock/src/app/auth/controllers/me.controller.ts +9 -2
- package/templates/warlock/src/app/auth/controllers/refresh-token.controller.ts +11 -2
- package/templates/warlock/src/app/auth/controllers/reset-password.controller.ts +4 -1
- package/templates/warlock/src/app/auth/main.ts +6 -2
- package/templates/warlock/src/app/auth/routes.ts +1 -1
- package/templates/warlock/src/app/auth/services/otp.service.ts +15 -5
- package/templates/warlock/src/app/posts/controllers/create-new-post.controller.ts +5 -2
- package/templates/warlock/src/app/posts/controllers/update-post.controller.ts +7 -3
- package/templates/warlock/src/app/posts/models/post/migrations/09-01-2026_02-07-51-post.migration.ts +1 -1
- package/templates/warlock/src/app/posts/models/post/post.model.ts +25 -0
- package/templates/warlock/src/app/posts/resources/post.resource.ts +14 -0
- package/templates/warlock/src/app/posts/routes.ts +5 -2
- package/templates/warlock/src/app/{utils → shared/utils}/router.ts +0 -10
- package/templates/warlock/src/app/uploads/controllers/fetch-uploaded-file.controller.ts +4 -1
- package/templates/warlock/src/app/users/controllers/create-new-user.controller.ts +4 -1
- package/templates/warlock/src/app/users/controllers/get-users.controller.ts +9 -2
- package/templates/warlock/src/app/users/events/inject-created-by-user.into-model.event.ts +1 -1
- package/templates/warlock/src/app/users/events/sync.ts +1 -1
- package/templates/warlock/src/app/users/main.ts +1 -20
- package/templates/warlock/src/app/users/models/user/user.model.ts +14 -6
- package/templates/warlock/src/app/users/routes.ts +1 -1
- package/templates/warlock/src/app/users/services/login-social.ts +4 -1
- package/templates/warlock/src/config/app.ts +1 -1
- package/templates/warlock/src/config/auth.ts +1 -1
- package/templates/warlock/src/config/cache.ts +3 -2
- package/templates/warlock/src/config/database.ts +6 -3
- package/templates/warlock/src/config/http.ts +1 -1
- package/templates/warlock/src/config/mail.ts +1 -1
- package/templates/warlock/src/config/storage.ts +6 -1
- package/templates/warlock/.env +0 -39
- package/templates/warlock/.prettierrc +0 -8
- package/templates/warlock/AGENTS.md +0 -11
- package/templates/warlock/src/app/posts/models/post/psot.model.ts +0 -42
- 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
|