codeweaver 1.1.0 → 2.1.0

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 (37) hide show
  1. package/.vscode/settings.json +3 -0
  2. package/README.md +114 -181
  3. package/package.json +12 -7
  4. package/src/app.ts +2 -112
  5. package/src/config.ts +33 -64
  6. package/src/constants.ts +7 -0
  7. package/src/db.ts +183 -0
  8. package/src/entities/order.entity.ts +68 -0
  9. package/src/entities/product.entity.ts +75 -0
  10. package/src/entities/user.entity.ts +38 -0
  11. package/src/main.ts +85 -0
  12. package/src/routers/orders/dto/order.dto.ts +54 -29
  13. package/src/routers/orders/{index.ts → index.router.ts} +13 -13
  14. package/src/routers/orders/order.controller.ts +118 -120
  15. package/src/routers/products/dto/product.dto.ts +86 -30
  16. package/src/routers/products/{index.ts → index.router.ts} +14 -15
  17. package/src/routers/products/product.controller.ts +136 -161
  18. package/src/routers/users/dto/user.dto.ts +14 -18
  19. package/src/routers/users/{index.ts → index.router.ts} +6 -7
  20. package/src/routers/users/user.controller.ts +87 -118
  21. package/src/swagger-options.ts +39 -0
  22. package/src/utilities/assign.ts +66 -0
  23. package/src/utilities/cache/memory-cache.ts +74 -0
  24. package/src/utilities/cache/redis-cache.ts +111 -0
  25. package/src/utilities/conversion.ts +158 -0
  26. package/src/utilities/error-handling.ts +156 -0
  27. package/src/utilities/router.ts +0 -0
  28. package/tsconfig.json +1 -4
  29. package/tsconfig.paths.json +8 -10
  30. package/src/packages/ts-zod-decorators/index.ts +0 -3
  31. package/src/packages/ts-zod-decorators/validate.decorator.ts +0 -20
  32. package/src/packages/ts-zod-decorators/validator.class.ts +0 -72
  33. package/src/packages/ts-zod-decorators/zod-input.decorator.ts +0 -12
  34. package/src/packages/ts-zod-decorators/zod-output.decorator.ts +0 -11
  35. package/src/types.ts +0 -16
  36. package/src/utilities.ts +0 -47
  37. /package/src/routers/{index.ts → index.router.ts} +0 -0
package/src/config.ts CHANGED
@@ -1,48 +1,14 @@
1
- /**
2
- * Server configuration interface
3
- * @interface
4
- * @property {string} url - Base server URL
5
- */
6
- interface Server {
7
- url: string;
8
- }
9
-
10
- /**
11
- * API information structure
12
- * @interface
13
- * @property {string} title - API title
14
- * @property {string} version - API version
15
- * @property {string} description - API description
16
- */
17
- interface Info {
18
- title: string;
19
- version: string;
20
- description: string;
21
- }
22
-
23
- /**
24
- * Swagger definition structure
25
- * @interface
26
- * @property {string} openApi - OpenAPI specification version
27
- * @property {Info} info - API information
28
- * @property {Server[]} servers - List of server configurations
29
- */
30
- interface SwaggerDefinition {
31
- openApi: string;
32
- info: Info;
33
- servers: Server[];
34
- }
35
-
36
- /**
37
- * Swagger configuration options
38
- * @interface
39
- * @property {SwaggerDefinition} swaggerDefinition - Swagger definition object
40
- * @property {string[]} apis - Paths to API documentation files
41
- */
42
- interface SwaggerOptions {
43
- swaggerDefinition: SwaggerDefinition;
44
- apis: string[];
45
- }
1
+ import {
2
+ memoizeTime,
3
+ productionEnvironment,
4
+ rateLimitTimeSpan,
5
+ rateLimitAllowedCalls,
6
+ timeout,
7
+ portNumber,
8
+ cacheSize,
9
+ } from "./constants";
10
+ import { SwaggerOptions } from "./swagger-options";
11
+ import { stringToBoolean } from "./utilities/conversion";
46
12
 
47
13
  /**
48
14
  * Main application configuration
@@ -53,14 +19,22 @@ interface SwaggerOptions {
53
19
  */
54
20
  interface Config {
55
21
  devMode: boolean;
56
- port: string;
22
+ port: number;
23
+ swagger: boolean;
57
24
  swaggerOptions: SwaggerOptions;
25
+ timeout: number;
26
+ rateLimitTimeSpan: number;
27
+ rateLimitAllowedCalls: number;
28
+ memoizeTime: number;
29
+ cacheSize: number;
58
30
  }
59
31
 
60
- const port = process.env.PORT || "3000";
61
- const config: Config = {
62
- devMode: process.env.NODE_ENV !== "production",
32
+ const port = Number(process.env.PORT) || portNumber;
33
+
34
+ let config: Config = {
35
+ devMode: process.env.NODE_ENV !== productionEnvironment,
63
36
  port,
37
+ swagger: stringToBoolean(process.env.SWAGGER || "true"),
64
38
  swaggerOptions: {
65
39
  swaggerDefinition: {
66
40
  openApi: "3.0.0",
@@ -76,23 +50,18 @@ const config: Config = {
76
50
  ],
77
51
  },
78
52
  apis: [
79
- "./src/routers/index.ts",
80
- "./src/routers/**/*.ts",
81
- "./src/routers/index.js",
82
- "./src/routers/**/*.js",
53
+ "./src/routers/index.router.ts",
54
+ "./src/routers/**/*.router.ts",
55
+ "./src/routers/index.router.js",
56
+ "./src/routers/**/*.router.js",
83
57
  ], // Path to the API docs
84
58
  },
59
+ timeout: Number(process.env.TIMEOUT) || timeout,
60
+ rateLimitTimeSpan: Number(process.env.RATE_LIMIT) || rateLimitTimeSpan,
61
+ rateLimitAllowedCalls:
62
+ Number(process.env.RATE_LIMIT) || rateLimitAllowedCalls,
63
+ memoizeTime: Number(process.env.MEMOIZE_TIME) || memoizeTime,
64
+ cacheSize: Number(process.env.CACHE_SIZE) || cacheSize,
85
65
  };
86
66
 
87
- // Other configurations:
88
- //
89
- // config.jwt_key = config.devMode ? "" : "";
90
- // config.jwt_expiration = config.devMode ? 360000 : 360000;
91
- // config.dbConnectionString = config.devMode ? `mongoDb url` : `mongoDb url`;
92
- // config.mongoDebug = config.devMode;
93
- // config.port = config.devMode ? 3000 : 3000;
94
- // config.host = config.devMode ? "localhost" : "localhost";
95
- // config.env = config.devMode ? "development" : "production";
96
- // config.mongoUrl = config.devMode ? "mongodb://localhost:27017/test" : "mongodb://localhost:27017/test";
97
-
98
67
  export default config;
@@ -0,0 +1,7 @@
1
+ export const timeout = 20000;
2
+ export const rateLimitTimeSpan = 60000;
3
+ export const rateLimitAllowedCalls = 300;
4
+ export const memoizeTime = 1000 * 60 * 60;
5
+ export const cacheSize = 1000;
6
+ export const productionEnvironment = "production";
7
+ export const portNumber = 3000;
package/src/db.ts ADDED
@@ -0,0 +1,183 @@
1
+ import { Order } from "@/entities/order.entity";
2
+ import { Product } from "@/entities/product.entity";
3
+ import { User } from "@/entities/user.entity";
4
+
5
+ // Array to store users (as a mock database)
6
+ export const users: User[] = [
7
+ {
8
+ id: 1,
9
+ username: "johndoe",
10
+ email: "johndoe@gmail.com",
11
+ password: "S3cur3P@ssw0rd",
12
+ },
13
+ {
14
+ id: 2,
15
+ username: "janesmith",
16
+ email: "janesmith@yahoo.com",
17
+ password: "P@ssw0rd2024",
18
+ },
19
+ {
20
+ id: 3,
21
+ username: "michael89",
22
+ email: "michael89@hotmail.com",
23
+ password: "M1chael!2024",
24
+ },
25
+ {
26
+ id: 4,
27
+ username: "lisa.wong",
28
+ email: "lisa.wong@example.com",
29
+ password: "L1saW0ng!2024",
30
+ },
31
+ {
32
+ id: 5,
33
+ username: "alex_k",
34
+ email: "alex.k@gmail.com",
35
+ password: "A1ex#Key2024",
36
+ },
37
+ {
38
+ id: 6,
39
+ username: "emilyj",
40
+ email: "emilyj@hotmail.com",
41
+ password: "Em!ly0101",
42
+ },
43
+ {
44
+ id: 7,
45
+ username: "davidparker",
46
+ email: "david.parker@yahoo.com",
47
+ password: "D@v!d2024",
48
+ },
49
+ {
50
+ id: 8,
51
+ username: "sophia_m",
52
+ email: "sophia.m@gmail.com",
53
+ password: "Sophi@2024",
54
+ },
55
+ {
56
+ id: 9,
57
+ username: "chrisw",
58
+ email: "chrisw@outlook.com",
59
+ password: "Chri$Wong21",
60
+ },
61
+ {
62
+ id: 10,
63
+ username: "natalie_b",
64
+ email: "natalie_b@gmail.com",
65
+ password: "N@talie#B2024",
66
+ },
67
+ ];
68
+
69
+ // Array to store products (as a mock database)
70
+ export const products: Product[] = [
71
+ {
72
+ id: 1,
73
+ name: "ASUS ROG Zephyrus G15",
74
+ price: 45000000,
75
+ description: "Gaming laptop with AMD Ryzen 9 5900HS and RTX 3080 GPU",
76
+ category: "Electronics",
77
+ stock: 15,
78
+ },
79
+ {
80
+ id: 2,
81
+ name: "Sony WH-1000XM5 Wireless Headphones",
82
+ price: 12000000,
83
+ description:
84
+ "Premium noise-canceling over-ear headphones with 30hr battery",
85
+ category: "Electronics",
86
+ stock: 8,
87
+ },
88
+ {
89
+ id: 3,
90
+ name: "LG Smart Inverter Microwave",
91
+ price: 25000000,
92
+ description: "1.7 cu.ft countertop microwave with smart sensor cooking",
93
+ category: "Appliances",
94
+ stock: 5,
95
+ },
96
+ {
97
+ id: 4,
98
+ name: "Trek Marlin 5 Mountain Bike",
99
+ price: 18000000,
100
+ description: "Entry-level mountain bike with aluminum frame and 21 speeds",
101
+ category: "Sports",
102
+ stock: 3,
103
+ },
104
+ {
105
+ id: 5,
106
+ name: "DeLonghi Espresso Machine",
107
+ price: 6500000,
108
+ description: "Compact espresso maker with manual milk frother",
109
+ category: "Kitchen",
110
+ stock: 12,
111
+ },
112
+ {
113
+ id: 6,
114
+ name: "Anker Wireless Charger",
115
+ price: 1200000,
116
+ description: "15W fast wireless charger with anti-slip surface",
117
+ category: "Mobile Accessories",
118
+ stock: 30,
119
+ },
120
+ {
121
+ id: 7,
122
+ name: "Logitech MX Master 3 Mouse",
123
+ price: 4500000,
124
+ description: "Ergonomic wireless mouse with Darkfield tracking",
125
+ category: "Computer Accessories",
126
+ stock: 18,
127
+ },
128
+ {
129
+ id: 8,
130
+ name: "Kindle Paperwhite",
131
+ price: 3800000,
132
+ description: 'Waterproof e-reader with 6.8" 300ppi display',
133
+ category: "Electronics",
134
+ stock: 9,
135
+ },
136
+ {
137
+ id: 9,
138
+ name: "Dyson V11 Vacuum Cleaner",
139
+ price: 32000000,
140
+ description: "Cordless stick vacuum with LCD screen and 60min runtime",
141
+ category: "Home Appliances",
142
+ stock: 7,
143
+ },
144
+ ];
145
+
146
+ // Array to store orders (as a mock database)
147
+ export const orders: Order[] = [
148
+ {
149
+ id: 1,
150
+ userId: 1,
151
+ products: [
152
+ { productId: 2, quantity: 1 },
153
+ { productId: 6, quantity: 2 },
154
+ ],
155
+ status: "Delivered",
156
+ total: 14400,
157
+ createdAt: new Date("2024-01-15"),
158
+ deliveredAt: new Date("2024-02-10"),
159
+ },
160
+ {
161
+ id: 2,
162
+ userId: 3,
163
+ products: [
164
+ { productId: 9, quantity: 1 },
165
+ { productId: 7, quantity: 1 },
166
+ ],
167
+ status: "Processing",
168
+ total: 36500,
169
+ createdAt: new Date("2024-03-20"),
170
+ },
171
+ {
172
+ id: 3,
173
+ userId: 2,
174
+ products: [
175
+ { productId: 1, quantity: 1 },
176
+ { productId: 4, quantity: 2 },
177
+ ],
178
+ status: "Canceled",
179
+ total: 81000,
180
+ createdAt: new Date("2024-05-01"),
181
+ canceledAt: new Date("2024-05-03"),
182
+ },
183
+ ];
@@ -0,0 +1,68 @@
1
+ import z from "zod";
2
+
3
+ /**
4
+ * Zod schema for the Order entity.
5
+ * This schema validates orders to ensure data integrity before processing.
6
+ */
7
+ export const ZodOrder = z.object({
8
+ /** Unique identifier for the order (positive integer). */
9
+ id: z.number().min(1).int(),
10
+
11
+ /** ID of the user who placed the order (positive integer). */
12
+ userId: z.number().min(1).int(),
13
+
14
+ /** Array of ordered products with their quantities. */
15
+ products: z.array(
16
+ z.object({
17
+ /** Product identifier referenced in the catalog */
18
+ productId: z.number().min(1).int(),
19
+
20
+ /** Quantity of the product ordered (at least 1) */
21
+ quantity: z.number().min(1).int(),
22
+ })
23
+ ),
24
+
25
+ /** Current status of the order */
26
+ status: z.enum(["Processing", "Delivered", "Canceled"]),
27
+
28
+ /** Total price of the order (minimum value constraint is 1000) */
29
+ total: z.number().min(1000),
30
+
31
+ /** Timestamp when the order was created */
32
+ createdAt: z.date(),
33
+
34
+ /** Optional timestamp when the order was canceled */
35
+ canceledAt: z.date().optional(),
36
+
37
+ /** Optional timestamp when the order was delivered */
38
+ deliveredAt: z.date().optional(),
39
+ });
40
+
41
+ export type Order = {
42
+ /** Unique identifier for the order */
43
+ id: number;
44
+
45
+ /** User ID who placed the order */
46
+ userId: number;
47
+
48
+ /** List of products in the order with quantities */
49
+ products: {
50
+ productId: number;
51
+ quantity: number;
52
+ }[];
53
+
54
+ /** Order status: Processing, Delivered, or Canceled */
55
+ status: "Processing" | "Delivered" | "Canceled";
56
+
57
+ /** Total price of the order */
58
+ total: number;
59
+
60
+ /** Order creation timestamp */
61
+ createdAt: Date;
62
+
63
+ /** Optional cancellation timestamp (if canceled) */
64
+ canceledAt?: Date | undefined;
65
+
66
+ /** Optional delivery timestamp (if delivered) */
67
+ deliveredAt?: Date | undefined;
68
+ };
@@ -0,0 +1,75 @@
1
+ import z from "zod";
2
+
3
+ /**
4
+ * Zod schema for the Product entity.
5
+ * This schema validates product data to ensure consistency
6
+ * before it is stored or processed.
7
+ */
8
+ export const ZodProduct = z.object({
9
+ /** Unique identifier for the product. Must be a positive integer. */
10
+ id: z.number().min(1).int(),
11
+
12
+ /** Product name. Minimum length of 2 characters. */
13
+ name: z.string().min(2),
14
+
15
+ /** Product price. Minimum value of 1000 (assuming your currency unit). */
16
+ price: z.number().min(1000),
17
+
18
+ /** Optional product description. Minimum length of 10 characters if provided. */
19
+ description: z.string().min(10).optional(),
20
+
21
+ /** Product category from a predefined list. */
22
+ category: z.enum([
23
+ "Electronics",
24
+ "Appliances",
25
+ "Sports",
26
+ "Kitchen",
27
+ "Mobile Accessories",
28
+ "Computer Accessories",
29
+ "Home Appliances",
30
+ "Books",
31
+ ]),
32
+
33
+ /** Stock count in inventory. Non-negative integer. */
34
+ stock: z.number().min(0).int(),
35
+ });
36
+
37
+ /**
38
+ * TypeScript type for a Product entity.
39
+ * Mirrors the Zod schema with an optional description.
40
+ */
41
+ export type Product = {
42
+ /** Unique identifier for the product. */
43
+ id: number;
44
+
45
+ /** Display name of the product. */
46
+ name: string;
47
+
48
+ /** Price of the product in the chosen currency. */
49
+ price: number;
50
+
51
+ /**
52
+ * Category of the product.
53
+ * Must be one of the predefined categories:
54
+ * - Electronics, Appliances, Sports, Kitchen, Mobile Accessories,
55
+ * Computer Accessories, Home Appliances, Books
56
+ */
57
+ category:
58
+ | "Electronics"
59
+ | "Appliances"
60
+ | "Sports"
61
+ | "Kitchen"
62
+ | "Mobile Accessories"
63
+ | "Computer Accessories"
64
+ | "Home Appliances"
65
+ | "Books";
66
+
67
+ /** Current stock level in inventory. Non-negative. */
68
+ stock: number;
69
+
70
+ /**
71
+ * Optional product description.
72
+ * Includes more details about the product when provided.
73
+ */
74
+ description?: string | undefined;
75
+ };
@@ -0,0 +1,38 @@
1
+ import z from "zod";
2
+
3
+ /**
4
+ * Zod schema for the User entity.
5
+ * This schema validates user data to ensure it meets basic integrity requirements
6
+ * before being stored or processed.
7
+ */
8
+ export const ZodUser = z.object({
9
+ /** Unique identifier for the user. Must be a positive integer. */
10
+ id: z.number().min(1).int(),
11
+
12
+ /** Username chosen by the user. Minimum length of 3 characters. */
13
+ username: z.string().min(3),
14
+
15
+ /** Email address of the user. Must be a valid email format. */
16
+ email: z.email(),
17
+
18
+ /** Password for the user. Minimum length of 6 characters. */
19
+ password: z.string().min(6),
20
+ });
21
+
22
+ /**
23
+ * TypeScript type for a User entity.
24
+ * Mirrors the Zod schema.
25
+ */
26
+ export type User = {
27
+ /** Unique identifier for the user. */
28
+ id: number;
29
+
30
+ /** Username chosen by the user. */
31
+ username: string;
32
+
33
+ /** Email address of the user. */
34
+ email: string;
35
+
36
+ /** Password for the user. */
37
+ password: string;
38
+ };
package/src/main.ts ADDED
@@ -0,0 +1,85 @@
1
+ import express, { NextFunction, Request, Response } from "express";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import config from "./config";
5
+ import { ResponseError } from "./utilities/error-handling";
6
+ require("./app");
7
+
8
+ /**
9
+ * Recursively loads Express routers from directory
10
+ * @param {string} routerPath - Directory path to scan
11
+ * @param {string} [basePath=""] - Base route path
12
+ */
13
+ function loadRouters(routerPath: string, basePath: string = "") {
14
+ // Read entries with their type info
15
+ const entries = fs.readdirSync(routerPath, { withFileTypes: true });
16
+
17
+ for (const entry of entries) {
18
+ const fullPath = path.join(routerPath, entry.name);
19
+
20
+ if (entry.isDirectory()) {
21
+ // Recurse into subdirectories
22
+ const subRoutePath = path
23
+ .join(basePath, entry.name)
24
+ .replace(/\\/g, "/")
25
+ .replace(/\/?index$/g, "");
26
+ loadRouters(fullPath, subRoutePath);
27
+ continue;
28
+ }
29
+
30
+ // Only handle router files
31
+ if (
32
+ !entry.name.endsWith(".router.ts") &&
33
+ !entry.name.endsWith(".router.js")
34
+ ) {
35
+ continue;
36
+ }
37
+
38
+ // Build route path safely
39
+ const routePath = path
40
+ .join(basePath, entry.name.replace(/\.router\.[tj]s$/, ""))
41
+ .replace(/\\/g, "/")
42
+ .replace(/\/?index$/g, "");
43
+
44
+ // Optional: skip if the target path would be empty (maps to /)
45
+ const mountPath = "/" + (routePath || "");
46
+
47
+ // Import and mount
48
+ const router = require(fullPath);
49
+ app.use(mountPath, router);
50
+ console.log(`Mounted ${entry.name} at ${mountPath}`);
51
+ }
52
+ }
53
+
54
+ const app = express();
55
+ app.use(express.json());
56
+ app.use(express.urlencoded({ extended: true }));
57
+
58
+ // Automatically import all routers from the /src/routers directory
59
+ const routersPath = path.join(__dirname, "/routers");
60
+ loadRouters(routersPath);
61
+
62
+ // Swagger setup
63
+ if (config.swagger) {
64
+ const swaggerJsDoc = require("swagger-jsdoc");
65
+ const swaggerUi = require("swagger-ui-express");
66
+ const swaggerDocs = swaggerJsDoc(config.swaggerOptions);
67
+ app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerDocs));
68
+ }
69
+
70
+ // General error handler
71
+ app.use(
72
+ (err: ResponseError, req: Request, res: Response, next: NextFunction) => {
73
+ res.status(err.status ?? 500).json(err);
74
+ }
75
+ );
76
+
77
+ // Start the server
78
+ app.listen(config.port, () => {
79
+ console.log(`Server is running on http://localhost:${config.port}`);
80
+ if (config.devMode) {
81
+ console.log(
82
+ `Swagger UI is available at http://localhost:${config.port}/api-docs`
83
+ );
84
+ }
85
+ });
@@ -1,33 +1,10 @@
1
- import { z } from "zod";
1
+ import { ZodOrder } from "@/entities/order.entity";
2
2
 
3
3
  /**
4
- * Zod schema for Order entity
5
- * @typedef {Object} ZodOrder
6
- * @property {number} id - Unique identifier (min 1)
7
- * @property {number} userId - Associated user ID (min 1)
8
- * @property {{productId: number, quantity: number}[]} products - Array of ordered products
9
- * @property {"Processing"|"Delivered"|"Canceled"} status - Order status
10
- * @property {number} total - Total price (min 1000)
11
- * @property {Date} createdAt - Creation timestamp
12
- * @property {Date} [canceledAt] - Cancellation timestamp
13
- * @property {Date} [deliveredAt] - Delivery timestamp
4
+ * DTO for creating an order.
5
+ * This is derived from the full Order schema by omitting fields
6
+ * that are typically generated by the system (id, status, timestamps).
14
7
  */
15
- export const ZodOrder = z.object({
16
- id: z.number().min(1).int(),
17
- userId: z.number().min(1).int(),
18
- products: z.array(
19
- z.object({
20
- productId: z.number().min(1).int(),
21
- quantity: z.number().min(1).int(),
22
- })
23
- ),
24
- status: z.enum(["Processing", "Delivered", "Canceled"]),
25
- total: z.number().min(1000),
26
- createdAt: z.date(),
27
- canceledAt: z.date().optional(),
28
- deliveredAt: z.date().optional(),
29
- });
30
-
31
8
  export const ZodOrderCreationDto = ZodOrder.omit({
32
9
  id: true,
33
10
  status: true,
@@ -36,5 +13,53 @@ export const ZodOrderCreationDto = ZodOrder.omit({
36
13
  deliveredAt: true,
37
14
  });
38
15
 
39
- export type Order = z.infer<typeof ZodOrder>;
40
- export type OrderCreationDto = z.infer<typeof ZodOrderCreationDto>;
16
+ /**
17
+ * Data Transfer Object for creating an order.
18
+ * Represents the minimal data required to create an order.
19
+ */
20
+ export type OrderCreationDto = {
21
+ /** ID of the user placing the order. */
22
+ userId: number;
23
+
24
+ /** List of products included in the order with their quantities. */
25
+ products: {
26
+ productId: number;
27
+ quantity: number;
28
+ }[];
29
+
30
+ /** Total price for the order (validated by the server/persistence layer). */
31
+ total: number;
32
+ };
33
+
34
+ /**
35
+ * Data Transfer Object for a full Order view.
36
+ * Represents the complete order data as returned by the API/service.
37
+ */
38
+ export type OrderDto = {
39
+ /** Unique identifier for the order. */
40
+ id: number;
41
+
42
+ /** User ID who placed the order. */
43
+ userId: number;
44
+
45
+ /** List of products in the order with their quantities. */
46
+ products: {
47
+ productId: number;
48
+ quantity: number;
49
+ }[];
50
+
51
+ /** Current status of the order: Processing, Delivered, or Canceled. */
52
+ status: "Processing" | "Delivered" | "Canceled";
53
+
54
+ /** Total price for the order. */
55
+ total: number;
56
+
57
+ /** Timestamp when the order was created. */
58
+ createdAt: Date;
59
+
60
+ /** Optional timestamp when the order was canceled (if canceled). */
61
+ canceledAt?: Date | undefined;
62
+
63
+ /** Optional timestamp when the order was delivered (if delivered). */
64
+ deliveredAt?: Date | undefined;
65
+ };