create-warlock 4.0.93 → 4.0.95

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.93",
3
+ "version": "4.0.95",
4
4
  "main": "./esm/index.js",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -24,13 +24,13 @@
24
24
  "@mongez/reinforcements": "^2.3.17",
25
25
  "@mongez/localization": "^3.2.1",
26
26
  "@mongez/supportive-is": "^2.0.4",
27
- "@warlock.js/auth": "4.0.93",
28
- "@warlock.js/cache": "4.0.93",
29
- "@warlock.js/cascade": "4.0.93",
30
- "@warlock.js/scheduler": "4.0.93",
31
- "@warlock.js/core": "4.0.93",
32
- "@warlock.js/logger": "4.0.93",
33
- "@warlock.js/seal": "4.0.93",
27
+ "@warlock.js/auth": "4.0.95",
28
+ "@warlock.js/cache": "4.0.95",
29
+ "@warlock.js/cascade": "4.0.95",
30
+ "@warlock.js/scheduler": "4.0.95",
31
+ "@warlock.js/core": "4.0.95",
32
+ "@warlock.js/logger": "4.0.95",
33
+ "@warlock.js/seal": "4.0.95",
34
34
  "dayjs": "^1.11.19"
35
35
  },
36
36
  "devDependencies": {
@@ -3,17 +3,45 @@ import { OTP } from "../otp.model";
3
3
 
4
4
  export default migrate(OTP, {
5
5
  name: "otp",
6
- createdAt: "2025-12-22T10:30:20", // ISO Date
6
+ createdAt: "2025-12-22T10:30:20",
7
7
  up() {
8
+ // Create table
9
+ this.createTableIfNotExists();
10
+
11
+ // Primary key
12
+ this.id();
13
+
14
+ // OTP fields
15
+ this.string("code", 20);
16
+ this.string("type", 50);
17
+ this.string("target", 255);
18
+ this.string("channel", 50);
19
+ this.integer("userId");
20
+ this.string("userType", 50);
21
+ this.timestamp("expiresAt");
22
+ this.timestamp("usedAt").nullable();
23
+ this.integer("attempts").default(0);
24
+ this.integer("maxAttempts").default(5);
25
+ this.json("metadata").nullable();
26
+
27
+ // Status
28
+ this.boolean("isActive").default(true);
29
+
30
+ // Embedded user references (JSONB)
31
+ this.json("createdBy").nullable();
32
+ this.json("updatedBy").nullable();
33
+ this.json("deletedBy").nullable();
34
+
35
+ // Timestamps
36
+ this.timestamps();
37
+
38
+ // Indexes for common queries
8
39
  this.index("code");
9
40
  this.index(["target", "type"]);
10
41
  this.index("expiresAt");
11
42
  this.index("userId");
12
43
  },
13
44
  down() {
14
- this.dropIndex("code");
15
- this.dropIndex(["target", "type"]);
16
- this.dropIndex("expiresAt");
17
- this.dropIndex("userId");
45
+ this.dropTableIfExists();
18
46
  },
19
47
  });
@@ -4,6 +4,7 @@ import { Post } from "../models/post/psot.model";
4
4
  export const createNewPostController: RequestHandler = async (request, response) => {
5
5
  const post = await Post.create({
6
6
  ...request.validated(),
7
+ authorId: request.user.id,
7
8
  });
8
9
 
9
10
  return response.success({
@@ -0,0 +1,36 @@
1
+ import { Migration } from "@warlock.js/cascade";
2
+ import { Post } from "../psot.model";
3
+
4
+ export default class PostsMigration extends Migration.for(Post) {
5
+ public up() {
6
+ // Create table
7
+ this.createTableIfNotExists();
8
+
9
+ // Primary key
10
+ this.id();
11
+
12
+ // Post fields
13
+ this.string("title", 255);
14
+ this.text("description");
15
+ this.string("slug", 255).unique();
16
+ this.string("image", 500).nullable();
17
+
18
+ // Status
19
+ this.boolean("isActive").default(true);
20
+
21
+ this.int("authorId").notNullable();
22
+
23
+ // Embedded user references (JSONB for PostgreSQL)
24
+ this.json("createdBy").nullable();
25
+ this.json("updatedBy").nullable();
26
+ this.json("deletedBy").nullable();
27
+
28
+ // Timestamps
29
+ this.timestamps();
30
+ this.timestamp("deletedAt").nullable();
31
+ }
32
+
33
+ public down() {
34
+ this.dropTableIfExists();
35
+ }
36
+ }
@@ -1,13 +1,15 @@
1
- import { Model, RegisterModel } from "@warlock.js/cascade";
1
+ import { belongsTo, Model, RegisterModel } from "@warlock.js/cascade";
2
2
  import { defineResource, useComputedSlug } from "@warlock.js/core";
3
3
  import { type Infer, v } from "@warlock.js/seal";
4
4
  import { globalColumnsSchema } from "app/shared/utils/global-columns-schema";
5
+ import { type User } from "app/users/models/user";
5
6
 
6
7
  export const postSchema = globalColumnsSchema.extend({
7
8
  title: v.string().required(),
8
9
  description: v.string().required(),
9
10
  slug: v.computed(useComputedSlug()),
10
11
  image: v.string().required(),
12
+ authorId: v.int().required(),
11
13
  });
12
14
 
13
15
  export type PostSchema = Infer<typeof postSchema>;
@@ -18,6 +20,12 @@ export class Post extends Model<PostSchema> {
18
20
 
19
21
  public static schema = postSchema;
20
22
 
23
+ public author?: User;
24
+
25
+ public static relations = {
26
+ author: belongsTo("User"),
27
+ };
28
+
21
29
  public static resource = defineResource({
22
30
  schema: {
23
31
  id: "number",
@@ -25,6 +33,7 @@ export class Post extends Model<PostSchema> {
25
33
  title: "string",
26
34
  description: "string",
27
35
  image: "storageUrl",
36
+ authorId: "number",
28
37
  createdBy: "object",
29
38
  updatedBy: "object",
30
39
  isActive: "boolean",
@@ -1,11 +1,8 @@
1
1
  import { router } from "@warlock.js/core";
2
- import { cloudUpload, guarded } from "app/utils/router";
2
+ import { guarded } from "app/utils/router";
3
3
  import { createNewPostController } from "./controllers/create-new-post.controller";
4
4
  import { updatePostController } from "./controllers/update-post.controller";
5
5
 
6
6
  guarded(() => {
7
- cloudUpload(() => {
8
- router.post("/posts", createNewPostController);
9
- });
10
- router.put("/posts/:id", updatePostController);
7
+ router.route("/posts").create(createNewPostController).update(updatePostController);
11
8
  });
@@ -2,4 +2,4 @@ import { modelSync } from "@warlock.js/cascade";
2
2
  import { Post } from "app/posts/models/post/psot.model";
3
3
  import { User } from "../models/user";
4
4
 
5
- modelSync.sync(User, Post, "createdBy");
5
+ export const cleanup = [modelSync.sync(User, Post, "createdBy")];
@@ -1,5 +1,24 @@
1
1
  import { onceConnected } from "@warlock.js/cascade";
2
+ import { Post } from "app/posts/models/post/psot.model";
2
3
 
3
4
  onceConnected(async () => {
4
5
  // TODO: Review cloud storage deleteDirectory not working as expected
6
+
7
+ // // Lazy loading
8
+ // const user = (await User.first())!;
9
+ // await user.load("posts");
10
+
11
+ // console.log(user.posts!.length);
12
+
13
+ // // Eager loading
14
+ // const user2 = await User.with("posts").first();
15
+
16
+ // console.log(user2?.posts?.length);
17
+
18
+ // const post = await Post.with("author").first();
19
+
20
+ // console.log(post?.author);
21
+ const post = await Post.joinWith("author").first();
22
+ console.log(post?.data); // { id, title, authorId, ... } - no author
23
+ console.log(post?.author); // User model instance
5
24
  });
@@ -3,12 +3,33 @@ import { User } from "../user.model";
3
3
 
4
4
  export default class UsersMigration extends Migration.for(User) {
5
5
  public up() {
6
- this.int("id").unique().autoIncrement();
7
- this.unique("email");
6
+ // Create table
7
+ this.createTableIfNotExists();
8
+
9
+ // Primary key
10
+ this.id(); // Creates "id" SERIAL PRIMARY KEY
11
+
12
+ // User fields
13
+ this.string("name", 255);
14
+ this.string("email", 255).unique();
15
+ this.string("password", 255);
16
+ this.string("image", 500).nullable();
17
+ this.json("imageMetadata").nullable();
18
+
19
+ // Status
20
+ this.boolean("isActive").default(true);
21
+
22
+ // Embedded user references (JSONB for PostgreSQL)
23
+ this.json("createdBy").nullable();
24
+ this.json("updatedBy").nullable();
25
+ this.json("deletedBy").nullable();
26
+
27
+ // Timestamps (auto-managed by the model)
28
+ this.timestamps(); // Creates createdAt and updatedAt
29
+ this.timestamp("deletedAt").nullable(); // Soft deletes
8
30
  }
9
31
 
10
32
  public down() {
11
- this.dropIndex("id");
12
- this.dropUnique("email");
33
+ this.dropTableIfExists();
13
34
  }
14
35
  }
@@ -1,7 +1,8 @@
1
1
  import { Auth } from "@warlock.js/auth";
2
- import { RegisterModel } from "@warlock.js/cascade";
2
+ import { hasMany, RegisterModel } from "@warlock.js/cascade";
3
3
  import { defineResource, uploadedFileMetadataSchema, useHashedPassword } from "@warlock.js/core";
4
4
  import { type Infer, v } from "@warlock.js/seal";
5
+ import { type Post } from "app/posts/models/post/psot.model";
5
6
  import { globalColumnsSchema } from "app/shared/utils/global-columns-schema";
6
7
 
7
8
  const UserResource = defineResource({
@@ -19,7 +20,7 @@ const UserResource = defineResource({
19
20
 
20
21
  export const userSchema = globalColumnsSchema.extend({
21
22
  name: v.string().required().trim(),
22
- email: v.email().requiredIfEmpty("id"),
23
+ email: v.email().requiredIfEmpty("id").unique("User"),
23
24
  image: v.string(),
24
25
  imageMetadata: uploadedFileMetadataSchema,
25
26
  password: v.string().min(6).requiredIfEmpty("id").addTransformer(useHashedPassword()),
@@ -39,6 +40,12 @@ export class User extends Auth<UserSchema> {
39
40
  */
40
41
  public static schema = userSchema;
41
42
 
43
+ public static relations = {
44
+ posts: hasMany("Post", { foreignKey: "authorId" }),
45
+ };
46
+
47
+ public posts?: Post[];
48
+
42
49
  /**
43
50
  * Embed fields when saving in another model
44
51
  */
@@ -69,5 +76,9 @@ export class User extends Auth<UserSchema> {
69
76
  this.addScope("verified", (query) => {
70
77
  query.where("emailVerified", true);
71
78
  });
79
+
80
+ // this.addGlobalScope("active", (query) => {
81
+ // query.where("isActive", true);
82
+ // });
72
83
  }
73
84
  }
@@ -3,8 +3,6 @@ import { guarded } from "app/utils/router";
3
3
  import { createNewUserController } from "./controllers/create-new-user.controller";
4
4
  import { getUsersController } from "./controllers/get-users.controller";
5
5
 
6
- router.post("/users", createNewUserController);
7
-
8
6
  guarded(() => {
9
- router.get("/users", getUsersController);
7
+ router.route("/users").get(getUsersController).post(createNewUserController);
10
8
  });
@@ -10,5 +10,5 @@ export async function cloudUploadMiddleware(request: Request, _response: Respons
10
10
  });
11
11
 
12
12
  // Example 2: Or just set prefix for same driver
13
- // storageDriverContext.setPrefix(`tenant-${request.user?.id}`);
13
+ storageDriverContext.setPrefix(`tenant-${request.user?.id}`);
14
14
  }
@@ -6,7 +6,7 @@ import type {
6
6
  } from "@warlock.js/cascade";
7
7
 
8
8
  const databaseConfigurations: ConnectionOptions<MongoDriverOptions, MongoClientOptions> = {
9
- driver: "mongodb",
9
+ driver: "postgres",
10
10
  name: "default",
11
11
  database: env("DB_NAME"),
12
12
  host: env("DB_HOST", "localhost"),
@@ -21,15 +21,44 @@ const databaseConfigurations: ConnectionOptions<MongoDriverOptions, MongoClientO
21
21
  counterCollection: "counters",
22
22
  },
23
23
 
24
- defaultDeleteStrategy: "trash",
24
+ defaultDeleteStrategy: "permanent",
25
25
 
26
26
  clientOptions: {
27
27
  replicaSet: env("DB_REPLICA_SET"),
28
28
  },
29
29
 
30
+ // ============================================================================
31
+ // Model Defaults Configuration
32
+ // ============================================================================
33
+ // These settings override driver defaults and apply to all models.
34
+ // Individual models can override these by setting static properties.
35
+ //
36
+ // Configuration hierarchy (highest to lowest):
37
+ // 1. Model static property (e.g., User.createdAtColumn = "creation_date")
38
+ // 2. modelOptions (below) - Database-wide overrides
39
+ // 3. Driver defaults (PostgreSQL: snake_case, MongoDB: camelCase)
40
+ // 4. Framework defaults
41
+ // ============================================================================
30
42
  modelOptions: {
43
+ // ID Generation (for MongoDB)
31
44
  randomIncrement: true,
32
45
  initialId: 1,
46
+
47
+ // Timestamps - PostgreSQL driver already defaults to snake_case
48
+ // (created_at, updated_at) so these are optional unless overriding
49
+ timestamps: true,
50
+ // createdAtColumn: "created_at", // Already driver default
51
+ // updatedAtColumn: "updated_at", // Already driver default
52
+
53
+ // Deletion Strategy
54
+ // deleteStrategy: "soft", // Uncomment to enable soft deletes globally
55
+ // deletedAtColumn: "deleted_at", // Already driver default
56
+
57
+ // Validation
58
+ strictMode: "strip",
59
+
60
+ // Naming Convention
61
+ // namingConvention: "snake_case", // Already driver default for PostgreSQL
33
62
  },
34
63
  };
35
64
 
@@ -1,8 +1,15 @@
1
- import { registerAuthCleanupCommand, registerJWTSecretGeneratorCommand } from "@warlock.js/auth";
1
+ import {
2
+ authMigrations,
3
+ registerAuthCleanupCommand,
4
+ registerJWTSecretGeneratorCommand,
5
+ } from "@warlock.js/auth";
2
6
  import { defineConfig } from "@warlock.js/core";
3
7
 
4
8
  export default defineConfig({
5
9
  cli: {
6
10
  commands: [registerJWTSecretGeneratorCommand(), registerAuthCleanupCommand()],
7
11
  },
12
+ database: {
13
+ migrations: authMigrations,
14
+ },
8
15
  });