create-warlock 4.0.131 → 4.0.133

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-warlock",
3
- "version": "4.0.131",
3
+ "version": "4.0.133",
4
4
  "main": "./esm/index.js",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,5 +1,11 @@
1
1
  # Create a new module
2
2
 
3
+ You can create a new module using the following command:
4
+
5
+ ```bash
6
+ warlock create.module <module-name>
7
+ ```
8
+
3
9
  Each module should consist of the following structure:
4
10
 
5
11
  ```
@@ -22,9 +28,11 @@ src/app/[module-name]/
22
28
  │ ├── [model-name].model.ts
23
29
  │ └── migrations/
24
30
  │ └── [date]_[model-name].migration.ts
25
- ├── requests/ # Request validation schemas
31
+ ├── requests/ # Request type definitions
32
+ │ └── *.request.ts # Individual request types
33
+ ├── validation/ # Validation schemas
26
34
  │ ├── index.ts # Simple validations (if all in one file)
27
- │ └── *.request.ts # Individual validation files
35
+ │ └── *.schema.ts # Individual validation schema files
28
36
  ├── resources/ # Response resource transformers
29
37
  │ └── *.resource.ts
30
38
  ├── events/ # Event handlers/listeners (auto-imported by Warlock.js)
@@ -87,9 +95,13 @@ Request handlers that process HTTP requests. Organize controllers by feature (e.
87
95
  ```typescript
88
96
  import type { Request, RequestHandler, Response } from "@warlock.js/core";
89
97
  import { someService } from "app/[module]/services/some.service";
90
- import { someRequestSchema, type SomeRequest } from "app/[module]/requests/some.request";
98
+ import { someRequestSchema } from "app/[module]/validation/some.schema";
99
+ import { type SomeRequest } from "app/[module]/requests/some.request";
91
100
 
92
- export const someController: RequestHandler = async (request: SomeRequest, response: Response) => {
101
+ export const someController: RequestHandler = async (
102
+ request: SomeRequest,
103
+ response: Response,
104
+ ) => {
93
105
  const data = await someService(request.validated());
94
106
  return response.success({ data });
95
107
  };
@@ -131,7 +143,9 @@ Business logic layer. Services handle the core functionality and interact with r
131
143
  import type { SomeModel } from "app/[module]/models/some";
132
144
  import { someRepository } from "app/[module]/repositories/some.repository";
133
145
 
134
- export async function someService(data: Record<string, any>): Promise<SomeModel> {
146
+ export async function someService(
147
+ data: Record<string, any>,
148
+ ): Promise<SomeModel> {
135
149
  return await someRepository.create(data);
136
150
  }
137
151
  ```
@@ -143,19 +157,19 @@ Data access layer that extends `RepositoryManager`. Handles database queries and
143
157
  **Repository Structure:**
144
158
 
145
159
  ```typescript
146
- import type { FilterByOptions, RepositoryOptions } from "@warlock.js/core";
160
+ import type { FilterRules, RepositoryOptions } from "@warlock.js/core";
147
161
  import { RepositoryManager } from "@warlock.js/core";
148
162
  import { SomeModel } from "../models/some";
149
163
 
150
164
  export class SomeRepository extends RepositoryManager<SomeModel> {
151
165
  public source = SomeModel;
152
166
 
153
- protected defaultOptions: RepositoryOptions = this.withDefaultOptions({});
167
+ protected defaultOptions: RepositoryOptions = {};
154
168
 
155
- protected filterBy: FilterByOptions = this.withDefaultFilters({
169
+ protected filterBy: FilterRules = {
156
170
  name: "like",
157
171
  isActive: "bool",
158
- });
172
+ };
159
173
  }
160
174
 
161
175
  export const someRepository = new SomeRepository();
@@ -191,31 +205,28 @@ export class SomeModel extends Model<SomeModelType> {
191
205
  }
192
206
  ```
193
207
 
194
- ### 7. requests/
208
+ ### 7. validation/
195
209
 
196
- Request handlers that process HTTP requests. Organize controllers by feature (e.g., `auth/`, `profile/`).
210
+ Validation schemas should be defined here.
197
211
 
198
- **Request Structure:**
212
+ **Validation Structure:**
199
213
 
200
214
  ```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";
215
+ import { v, type Infer } from "@warlock.js/seal";
204
216
 
205
- export const someController: RequestHandler = async (request: SomeRequest, response: Response) => {
206
- const data = await someService(request.validated());
207
- return response.success({ data });
208
- };
217
+ export const createAccountSchema = v.object({
218
+ name: v.string().minLength(2).required(),
219
+ email: v.email().required(),
220
+ password: v.string().required().strongPassword(),
221
+ });
209
222
 
210
- someController.validation = {
211
- schema: someRequestSchema,
212
- };
223
+ export type CreateAccountSchema = Infer<typeof createAccountSchema>;
213
224
  ```
214
225
 
215
226
  **Important Rules:**
216
227
 
217
228
  - 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
229
+ - If requests are complex or numerous, use separate `.schema.ts` files
219
230
  - Always export the schema and its inferred type
220
231
  - Use `Infer<typeof schema>` (similar to Zod) to generate TypeScript types
221
232
 
@@ -223,8 +234,6 @@ someController.validation = {
223
234
 
224
235
  ```typescript
225
236
  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
237
 
229
238
  export const createSchema = v.object({
230
239
  name: v.string().minLength(2).required(),
@@ -236,45 +245,48 @@ export const updateSchema = v.object({
236
245
 
237
246
  export type CreateData = Infer<typeof createSchema>;
238
247
  export type UpdateData = Infer<typeof updateSchema>;
239
-
240
- export type CreatePostRequest<User, CreateData>;
241
- export type UpdatePostRequest<User, UpdateData>;
242
248
  ```
243
249
 
244
250
  **Individual Validation Files:**
245
251
 
246
252
  ```typescript
247
- // requests/create-account.request.ts
253
+ // validation/create-account.schema.ts
248
254
  import { v, type Infer } from "@warlock.js/seal";
249
- import { type Request } from "@warlock.js/core";
250
255
 
251
256
  export const createAccountSchema = v.object({
252
257
  name: v.string().minLength(2).required(),
253
- email: vemail().required(),
258
+ email: v.email().required(),
254
259
  password: v.string().required().strongPassword(),
255
260
  });
256
261
 
257
- export type CreateAccountData = Infer<typeof createAccountSchema>;
262
+ export type CreateAccountSchema = Infer<typeof createAccountSchema>;
263
+ ```
258
264
 
259
- export type CreateAccountRequest<undefined, CreateAccountData>;
265
+ ### 8. requests/
266
+
267
+ High level Request types that link validation schemas to the Request object.
268
+
269
+ **Request Structure:**
270
+
271
+ ```typescript
272
+ import type { Request } from "@warlock.js/core";
273
+ import { type CreateAccountSchema } from "app/[module]/validation/create-account.schema";
274
+
275
+ export type CreateAccountRequest = Request<CreateAccountSchema>;
260
276
  ```
261
277
 
262
278
  **Usage in Controller:**
263
279
 
264
280
  ```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";
281
+ import type { RequestHandler, Response } from "@warlock.js/core";
282
+ import { type CreateAccountRequest } from "app/[module]/requests/create-account.request";
283
+ import { createAccountSchema } from "app/[module]/validation/create-account.schema";
271
284
 
272
285
  export const createAccountController: RequestHandler = async (
273
286
  request: CreateAccountRequest,
274
287
  response: Response,
275
288
  ) => {
276
- // request.validated() is now typed as CreateAccountData
277
- const data = request.validated();
289
+ const data = request.validated(); // data is typed as CreateAccountSchema
278
290
  // ...
279
291
  };
280
292
 
@@ -283,27 +295,42 @@ createAccountController.validation = {
283
295
  };
284
296
  ```
285
297
 
286
- ### 8. resources/
298
+ ### 9. resources/
287
299
 
288
300
  Resources define how data is transformed before being sent to clients.
289
301
 
290
302
  **Resource Structure:**
291
303
 
292
304
  ```typescript
293
- import { Resource } from "@warlock.js/core";
294
- import {} from "app/utils/output";
305
+ **import** { Resource, RegisterResource } from "@warlock.js/core";
295
306
 
307
+ @RegisterResource()
296
308
  export class SomeResource extends Resource {
297
- public schema = {
309
+ public static schema = {
298
310
  name: "string",
299
311
  id: "int",
300
312
  type: () => "user", // custom output value
301
- image: (value) => (value.startsWith("/") ? value : "/" + value),
313
+ image: value => (value.startsWith("/") ? value : "/" + value),
302
314
  };
303
315
  }
304
316
  ```
305
317
 
306
- ### 9. events/ (Auto-imported)
318
+ Or even making it simpler by using `defineResource` function (FP style, recommended for most resources)
319
+
320
+ ```typescript
321
+ import { defineResource } from "@warlock.js/core";
322
+
323
+ export const SomeResource = defineResource({
324
+ schema: {
325
+ name: "string",
326
+ id: "int",
327
+ type: () => "user", // custom output value
328
+ image: value => (value.startsWith("/") ? value : "/" + value),
329
+ },
330
+ });
331
+ ```
332
+
333
+ ### 10. events/ (Auto-imported)
307
334
 
308
335
  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
336
 
@@ -327,7 +354,7 @@ export const cleanup = () => event.unsbcribe();
327
354
 
328
355
  **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
356
 
330
- ### 10. services/mail/ (or separate mail/ folder)
357
+ ### 11. services/mail/ (or separate mail/ folder)
331
358
 
332
359
  Email service functions for sending notifications. **Mail files should be suffixed with `.mail.ts`** (e.g., `welcome.mail.ts`, `reset-password.mail.ts`).
333
360
 
@@ -356,7 +383,7 @@ export default async function sendWelcomeEmail(model: SomeModel) {
356
383
 
357
384
  **Recommendation:** Use `services/mail/` folder for better organization when you have multiple mail templates, and always suffix mail files with `.mail.ts`.
358
385
 
359
- ### 11. components/
386
+ ### 12. components/
360
387
 
361
388
  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
389
 
@@ -383,12 +410,17 @@ export default async function sendWelcomeEmail(user: User) {
383
410
  await sendMail({
384
411
  to: user.get("email"),
385
412
  subject: "Welcome",
386
- component: <WelcomeEmailComponent name={user.get("name")} email={user.get("email")} />,
413
+ component: (
414
+ <WelcomeEmailComponent
415
+ name={user.get("name")}
416
+ email={user.get("email")}
417
+ />
418
+ ),
387
419
  });
388
420
  }
389
421
  ```
390
422
 
391
- ### 12. types/
423
+ ### 13. types/
392
424
 
393
425
  TypeScript type definitions specific to the module. Use this folder for:
394
426
 
@@ -409,7 +441,7 @@ export interface UserPreferences {
409
441
  export type UserRole = "admin" | "user" | "guest";
410
442
  ```
411
443
 
412
- ### 13. utils/
444
+ ### 14. utils/
413
445
 
414
446
  Module-specific utility functions. **Must include `locales.ts`** which is automatically imported by Warlock.js.
415
447
 
@@ -461,41 +493,51 @@ export function formatUserName(user: User): string {
461
493
  ## Best Practices
462
494
 
463
495
  1. **Naming Conventions:**
496
+
464
497
  - Use kebab-case for file names
465
498
  - Use PascalCase for class names
466
499
  - Use camelCase for functions and variables
467
500
 
468
501
  2. **Controller Organization:**
502
+
469
503
  - Group related controllers in subdirectories (e.g., `auth/`, `profile/`)
470
504
  - Keep controllers thin - delegate business logic to services
471
505
 
472
506
  3. **Service Layer:**
507
+
473
508
  - Services should contain reusable business logic
474
509
  - Services interact with repositories, not directly with models
475
510
 
476
511
  4. **Repository Pattern:**
512
+
477
513
  - All database operations should go through repositories
478
514
  - Define filter options in the repository class
479
515
 
480
516
  5. **Validation:**
517
+
481
518
  - **All validation schemas must be in the `validation/` folder**
519
+ - **All request types must be in the `requests/` folder**
482
520
  - Always attach validation to controllers using `controller.validation = { schema }`
483
521
  - 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
522
+ - Import validation schema in controller from `validation/` folder
523
+ - Import request type in controller from `requests/` folder
524
+ - Use `index.ts` for simple validations, separate `.schema.ts` files for complex ones
486
525
 
487
526
  6. **Models:**
527
+
488
528
  - Define casts for all fields
489
529
  - Specify embedded fields for performance
490
530
  - Include default values when needed
491
531
 
492
532
  7. **Routes:**
533
+
493
534
  - Use appropriate guards (`guarded`, `guardedAdmin`, `guardedGuest`)
494
535
  - Group related routes together
495
536
  - Use RESTful resources when appropriate
496
537
  - Routes file is auto-imported by Warlock.js
497
538
 
498
539
  8. **Auto-imported Files:**
540
+
499
541
  - `main.ts` - Module entry point
500
542
  - `routes.ts` - Route definitions
501
543
  - `utils/locales.ts` - Translations
@@ -23,13 +23,13 @@
23
23
  "@mongez/reinforcements": "^2.3.17",
24
24
  "@mongez/localization": "^3.2.1",
25
25
  "@mongez/supportive-is": "^2.0.4",
26
- "@warlock.js/auth": "4.0.131",
27
- "@warlock.js/cache": "4.0.131",
28
- "@warlock.js/cascade": "4.0.131",
29
- "@warlock.js/scheduler": "4.0.131",
30
- "@warlock.js/core": "4.0.131",
31
- "@warlock.js/logger": "4.0.131",
32
- "@warlock.js/seal": "4.0.131",
26
+ "@warlock.js/auth": "4.0.133",
27
+ "@warlock.js/cache": "4.0.133",
28
+ "@warlock.js/cascade": "4.0.133",
29
+ "@warlock.js/scheduler": "4.0.133",
30
+ "@warlock.js/core": "4.0.133",
31
+ "@warlock.js/logger": "4.0.133",
32
+ "@warlock.js/seal": "4.0.133",
33
33
  "dayjs": "^1.11.19",
34
34
  "mongodb": "^7.0.0"
35
35
  },
@@ -1,6 +1,7 @@
1
1
  import { t, type RequestHandler, type Response } from "@warlock.js/core";
2
- import { loginSchema, type LoginRequest } from "../requests/login.request";
2
+ import { type LoginRequest } from "../requests/login.request";
3
3
  import { loginService } from "../services/auth.service";
4
+ import { loginSchema } from "../validation/login.schema";
4
5
 
5
6
  /**
6
7
  * Login controller
@@ -1,9 +1,7 @@
1
1
  import { t, type Response } from "@warlock.js/core";
2
- import {
3
- resetPasswordSchema,
4
- type ResetPasswordRequest,
5
- } from "../requests/reset-password.request";
2
+ import { type ResetPasswordRequest } from "../requests/reset-password.request";
6
3
  import { resetPasswordService } from "../services/reset-password.service";
4
+ import { resetPasswordSchema } from "../validation/reset-password.schema";
7
5
 
8
6
  /**
9
7
  * Reset password controller
@@ -1,10 +1,4 @@
1
- import { v, type Infer, type Request } from "@warlock.js/core";
2
-
3
- export const loginSchema = v.object({
4
- email: v.email().required(),
5
- password: v.string().required(),
6
- });
7
-
8
- export type LoginSchema = Infer<typeof loginSchema>;
1
+ import type { Request } from "@warlock.js/core";
2
+ import { type LoginSchema } from "../validation/login.schema";
9
3
 
10
4
  export type LoginRequest = Request<LoginSchema>;
@@ -1,11 +1,4 @@
1
- import { v, type Infer, type Request } from "@warlock.js/core";
2
-
3
- export const resetPasswordSchema = v.object({
4
- email: v.string().email().required(),
5
- code: v.string().required(),
6
- newPassword: v.string().min(8).required(),
7
- });
8
-
9
- export type ResetPasswordSchema = Infer<typeof resetPasswordSchema>;
1
+ import type { Request } from "@warlock.js/core";
2
+ import { type ResetPasswordSchema } from "../validation/reset-password.schema";
10
3
 
11
4
  export type ResetPasswordRequest = Request<ResetPasswordSchema>;
@@ -0,0 +1,8 @@
1
+ import { v, type Infer } from "@warlock.js/core";
2
+
3
+ export const loginSchema = v.object({
4
+ email: v.email().required(),
5
+ password: v.string().required(),
6
+ });
7
+
8
+ export type LoginSchema = Infer<typeof loginSchema>;
@@ -0,0 +1,9 @@
1
+ import { v, type Infer } from "@warlock.js/core";
2
+
3
+ export const resetPasswordSchema = v.object({
4
+ email: v.string().email().required(),
5
+ code: v.string().required(),
6
+ newPassword: v.string().min(8).required(),
7
+ });
8
+
9
+ export type ResetPasswordSchema = Infer<typeof resetPasswordSchema>;
@@ -1,8 +1,10 @@
1
- import { type RequestHandler, v } from "@warlock.js/core";
1
+ import { type RequestHandler } from "@warlock.js/core";
2
2
  import { Post } from "../models/post/post.model";
3
+ import { type CreatePostRequest } from "../requests/create-post.request";
4
+ import { createPostSchema } from "../validation/create-post.schema";
3
5
 
4
6
  export const createNewPostController: RequestHandler = async (
5
- request,
7
+ request: CreatePostRequest,
6
8
  response,
7
9
  ) => {
8
10
  const post = await Post.create({
@@ -16,9 +18,5 @@ export const createNewPostController: RequestHandler = async (
16
18
  };
17
19
 
18
20
  createNewPostController.validation = {
19
- schema: v.object({
20
- title: v.string().required(),
21
- description: v.string(),
22
- image: v.file().image().maxSize({ size: 2, unit: "MB" }).saveTo("posts"),
23
- }),
21
+ schema: createPostSchema,
24
22
  };
@@ -1,9 +1,10 @@
1
1
  import { type RequestHandler } from "@warlock.js/core";
2
- import { v } from "@warlock.js/seal";
3
2
  import { Post } from "../models/post/post.model";
3
+ import { type UpdatePostRequest } from "../requests/update-post.request";
4
+ import { updatePostSchema } from "../validation/update-post.schema";
4
5
 
5
6
  export const updatePostController: RequestHandler = async (
6
- request,
7
+ request: UpdatePostRequest,
7
8
  response,
8
9
  ) => {
9
10
  const post = await Post.find(request.int("id"));
@@ -20,9 +21,5 @@ export const updatePostController: RequestHandler = async (
20
21
  };
21
22
 
22
23
  updatePostController.validation = {
23
- schema: v.object({
24
- title: v.string().required(),
25
- description: v.string(),
26
- image: v.file().image().maxSize({ size: 2, unit: "MB" }).saveTo("posts"),
27
- }),
24
+ schema: updatePostSchema,
28
25
  };
@@ -0,0 +1,4 @@
1
+ import type { Request } from "@warlock.js/core";
2
+ import { type CreatePostSchema } from "../validation/create-post.schema";
3
+
4
+ export type CreatePostRequest = Request<CreatePostSchema>;
@@ -0,0 +1,4 @@
1
+ import type { Request } from "@warlock.js/core";
2
+ import { type UpdatePostSchema } from "../validation/update-post.schema";
3
+
4
+ export type UpdatePostRequest = Request<UpdatePostSchema>;
@@ -0,0 +1,9 @@
1
+ import { v, type Infer } from "@warlock.js/core";
2
+
3
+ export const createPostSchema = v.object({
4
+ title: v.string().required(),
5
+ description: v.string(),
6
+ image: v.file().image().maxSize({ size: 2, unit: "MB" }).saveTo("posts"),
7
+ });
8
+
9
+ export type CreatePostSchema = Infer<typeof createPostSchema>;
@@ -0,0 +1,9 @@
1
+ import { v, type Infer } from "@warlock.js/core";
2
+
3
+ export const updatePostSchema = v.object({
4
+ title: v.string().required(),
5
+ description: v.string(),
6
+ image: v.file().image().maxSize({ size: 2, unit: "MB" }).saveTo("posts"),
7
+ });
8
+
9
+ export type UpdatePostSchema = Infer<typeof updatePostSchema>;
@@ -1,5 +1,6 @@
1
- import { v, type RequestHandler } from "@warlock.js/core";
1
+ import { type RequestHandler } from "@warlock.js/core";
2
2
  import { User } from "../models/user";
3
+ import { createUserSchema } from "../validation/create-user.schema";
3
4
 
4
5
  export const createNewUserController: RequestHandler = async (
5
6
  request,
@@ -22,10 +23,5 @@ export const createNewUserController: RequestHandler = async (
22
23
  };
23
24
 
24
25
  createNewUserController.validation = {
25
- schema: v.object({
26
- name: v.string().required(),
27
- email: v.email().required().unique(User),
28
- password: v.string().min(6),
29
- image: v.file().image().required().maxSize({ unit: "MB", size: 1.5 }),
30
- }),
26
+ schema: createUserSchema,
31
27
  };
@@ -0,0 +1,4 @@
1
+ import type { Request } from "@warlock.js/core";
2
+ import { type CreateUserSchema } from "../validation/create-user.schema";
3
+
4
+ export type CreateUserRequest = Request<CreateUserSchema>;
@@ -0,0 +1,11 @@
1
+ import { v, type Infer } from "@warlock.js/core";
2
+ import { User } from "../models/user";
3
+
4
+ export const createUserSchema = v.object({
5
+ name: v.string().required(),
6
+ email: v.email().required().unique(User),
7
+ password: v.string().min(6),
8
+ image: v.file().image().required().maxSize({ unit: "MB", size: 1.5 }),
9
+ });
10
+
11
+ export type CreateUserSchema = Infer<typeof createUserSchema>;