codeweaver 2.0.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.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- **Codeweaver** is a lightweight microframework created with `Express`, `TypeScript`, and `Zod` (v4), seamlessly integrated with `Swagger` for comprehensive API documentation. Its modular architecture for routers promotes scalability and organized development, making it easy to expand and maintain.
5
+ **Codeweaver** is an unopinionated microframework built with `Express`, `TypeScript`, and `Zod` (v4), seamlessly integrated with `Swagger` for comprehensive API documentation. Its modular architecture for routers promotes scalability and organized development, making it easy to expand and maintain. Routers are automatically discovered and wired up through a conventional folder structure, simplifying project organization and reducing boilerplate. Routers can be nested, allowing you to compose complex route trees by placing sub-routers inside parent router folders. It also uses `utils-decorators`, a collection of middleware utilities (throttling, caching, and error handling) designed to strengthen application resilience.
6
6
 
7
7
  ## Features and Technologies Used
8
8
 
@@ -13,8 +13,7 @@
13
13
  - **Swagger Integration**: Automatically generates interactive API documentation, facilitating easier understanding of available endpoints for developers and consumers.
14
14
  - **Async Handlers**: Utilizes async/await syntax for cleaner, more maintainable asynchronous code without callback nesting.
15
15
  - **Zod**: Implements schema validation for input data.
16
- - **ts-zod-decorators**: Enables validation using Zod schemas through decorators.
17
- - **utils-decorators**: Provides middleware utilities such as throttling and error handling for a more robust application.
16
+ - **utils-decorators**: A collection of middleware utilities (throttling, caching, and error handling) designed to strengthen application resilience.
18
17
 
19
18
  ## Installation
20
19
 
@@ -134,8 +133,9 @@ const userController = new UserController();
134
133
  router.post(
135
134
  "/",
136
135
  asyncHandler(async (req: Request, res: Response) => {
137
- const user = await userController.create(req.body);
138
- res.status(201).json(user);
136
+ const user = await userController.validateUserCreationDto(req.body);
137
+ await userController.create(user);
138
+ res.status(201).send();
139
139
  })
140
140
  );
141
141
 
@@ -160,7 +160,8 @@ router.post(
160
160
  router.get(
161
161
  "/:id",
162
162
  asyncHandler(async (req: Request, res: Response) => {
163
- const user = await userController.get(req.params.id);
163
+ const id = await userController.validateId(req.params.id);
164
+ const user = await userController.get(id);
164
165
  res.json(user);
165
166
  })
166
167
  );
@@ -190,136 +191,44 @@ export = router;
190
191
  **Controllers** in this Express TypeScript framework act as the intermediary between the incoming HTTP requests and the application logic. Each controller is responsible for handling specific routes and defining the behavior associated with those routes. This organization promotes a clean architecture by separating business logic, validation, and routing concerns.
191
192
 
192
193
  Controllers can be organized within the router folders, allowing them to stay closely related to their respective routes. However, they are not limited to this structure and can be placed anywhere within the `src` folder as needed, providing flexibility in organizing the codebase.
193
-
194
- Controllers leverage decorators from the `ts-zod-decorators` package to implement input validation and error handling gracefully. With validators like `@Validate`, controllers can ensure that incoming data adheres to defined schemas before any processing occurs, preventing invalid data from reaching the service layer. This capability enhances data integrity and application reliability.
195
-
196
- For example, in the provided `UserController`, the `createUser` method demonstrates how to apply input validation and error handling through decorators. It employs `@rateLimit` to restrict the number of allowed requests within a specified timeframe, effectively guarding against too many rapid submissions. When an error arises, the `@onError` decorator provides a systematic way to handle exceptions, allowing for logging or other error management processes to be performed centrally.
194
+ Controllers leverage decorators from the `utils-decorators` package to implement throttling, caching, and error handling gracefully.
195
+ For example, in the provided `UserController`, the `createUser` method demonstrates how to apply error handling through decorators. It also employs `@rateLimit` to restrict the number of allowed requests within a specified timeframe, effectively guarding against too many rapid submissions. When an error arises, the `@onError` decorator provides a systematic way to handle exceptions, allowing for logging or other error management processes to be performed centrally.
197
196
 
198
197
  Here’s a brief breakdown of key components used in the `UserController`:
199
198
 
200
- - **Validation**: The `CreateUserDto` is validated against incoming data using the `@ZodInput` decorator, ensuring that only well-formed data is passed to the business logic, which is crucial for maintaining application stability.
201
-
202
- ```typescript
203
- import { z } from "zod";
204
-
205
- /**
206
- * Zod schema for User entity
207
- * @typedef {Object} ZodUser
208
- * @property {number} id - Unique identifier (min 1)
209
- * @property {string} username - Username (min 3 chars)
210
- * @property {string} email - Valid email format
211
- * @property {string} password - Password (min 6 chars)
212
- */
213
- export const ZodUser = z.object({
214
- id: z.number().min(1).int(),
215
- username: z.string().min(3),
216
- email: z.email(),
217
- password: z.string().min(6),
218
- });
219
-
220
- export const ZodUserCreationDto = ZodUser.omit({ id: true });
221
- export const ZodUserDto = ZodUser.omit({ password: true });
222
-
223
- export type User = z.infer<typeof ZodUser>;
224
- export type UserCreationDto = z.infer<typeof ZodUserCreationDto>;
225
- export type UserDto = z.infer<typeof ZodUserDto>;
226
- ```
227
-
228
- - **Throttling and Rate Limiting**: The `@rateLimit` decorator is applied to safeguard the application's endpoints from abuse by limiting how frequently a particular method can be invoked.
229
-
230
- - **Error Handling**: The `@onError` decorator captures any exceptions that occur during the execution of the createUser method, allowing for centralized error management, which can greatly simplify debugging and improve maintainability.
231
-
232
- By using a well-organized controller structure, this project makes it easier to add, modify, and manage endpoints as the application grows. Developers can focus on implementing business logic while the controllers handle the intricacies of request parsing, validation, and response formatting. Additionally, this separation of concerns improves unit testing, as controllers can be tested independently from the rest of the application logic, ensuring robust and reliable API behavior.
233
-
234
- Here is a quick reference to the UserController in practice:
235
-
236
199
  ```typescript
237
200
  import {
238
- User,
239
201
  ZodUserCreationDto,
240
202
  UserCreationDto,
241
203
  UserDto,
204
+ ZodUserDto,
242
205
  } from "./dto/user.dto";
243
206
  import { memoizeAsync, onError, rateLimit, timeout } from "utils-decorators";
244
- import { Validate, ZodInput } from "ts-zod4-decorators";
245
- import { ResponseError } from "@/types";
246
- import { parseId } from "@/utilities/error-handling";
207
+ import { ResponseError } from "@/utilities/error-handling";
208
+ import { convert, stringToInteger } from "@/utilities/conversion";
247
209
  import config from "@/config";
248
-
249
- // Array to store users (as a mock database)
250
- const users = [
251
- {
252
- id: 1,
253
- username: "johndoe",
254
- email: "johndoe@gmail.com",
255
- password: "S3cur3P@ssw0rd",
256
- },
257
- {
258
- id: 2,
259
- username: "janesmith",
260
- email: "janesmith@yahoo.com",
261
- password: "P@ssw0rd2024",
262
- },
263
- {
264
- id: 3,
265
- username: "michael89",
266
- email: "michael89@hotmail.com",
267
- password: "M1chael!2024",
268
- },
269
- {
270
- id: 4,
271
- username: "lisa.wong",
272
- email: "lisa.wong@example.com",
273
- password: "L1saW0ng!2024",
274
- },
275
- {
276
- id: 5,
277
- username: "alex_k",
278
- email: "alex.k@gmail.com",
279
- password: "A1ex#Key2024",
280
- },
281
- {
282
- id: 6,
283
- username: "emilyj",
284
- email: "emilyj@hotmail.com",
285
- password: "Em!ly0101",
286
- },
287
- {
288
- id: 7,
289
- username: "davidparker",
290
- email: "david.parker@yahoo.com",
291
- password: "D@v!d2024",
292
- },
293
- {
294
- id: 8,
295
- username: "sophia_m",
296
- email: "sophia.m@gmail.com",
297
- password: "Sophi@2024",
298
- },
299
- {
300
- id: 9,
301
- username: "chrisw",
302
- email: "chrisw@outlook.com",
303
- password: "Chri$Wong21",
304
- },
305
- {
306
- id: 10,
307
- username: "natalie_b",
308
- email: "natalie_b@gmail.com",
309
- password: "N@talie#B2024",
310
- },
311
- ];
210
+ import { users } from "@/db";
211
+ import { User } from "@/entities/user.entity";
212
+ import { MapAsyncCache } from "@/utilities/cache/memory-cache";
312
213
 
313
214
  function exceedHandler() {
314
215
  const message = "Too much call in allowed window";
315
216
  throw new ResponseError(message, 429);
316
217
  }
317
218
 
318
- function getUserErrorHandler(e: Error) {
219
+ function userNotFoundHandler(e: ResponseError) {
319
220
  const message = "User not found.";
320
- throw new ResponseError(message, 404, e.message);
221
+ throw new ResponseError(message, 404, e?.message);
222
+ }
223
+
224
+ function invalidInputHandler(e: ResponseError) {
225
+ const message = "Invalid input";
226
+ throw new ResponseError(message, 400, e?.message);
321
227
  }
322
228
 
229
+ const usersCache = new MapAsyncCache<UserDto[]>(config.cacheSize);
230
+ const userCache = new MapAsyncCache<UserDto>(config.cacheSize);
231
+
323
232
  /**
324
233
  * Controller for handling user-related operations
325
234
  * @class UserController
@@ -328,63 +237,94 @@ function getUserErrorHandler(e: Error) {
328
237
  export default class UserController {
329
238
  // constructor(private readonly userService: UserService) { }
330
239
 
240
+ @onError({
241
+ func: invalidInputHandler,
242
+ })
243
+ /**
244
+ * Validates a string ID and converts it to a number.
245
+ *
246
+ * @param {string} id - The ID to validate and convert.
247
+ * @returns {number} The numeric value of the provided ID.
248
+ */
249
+ public async validateId(id: string): Promise<number> {
250
+ return stringToInteger(id);
251
+ }
252
+
253
+ @onError({
254
+ func: invalidInputHandler,
255
+ })
256
+ /**
257
+ * Validates and creates a new User from the given DTO.
258
+ *
259
+ * @param {UserCreationDto} user - The incoming UserCreationDto to validate and transform.
260
+ * @returns {User} A fully formed User object ready for persistence.
261
+ */
262
+ public async validateUserCreationDto(user: UserCreationDto): Promise<User> {
263
+ const newUser = await ZodUserCreationDto.parseAsync(user);
264
+ return { ...newUser, id: users.length + 1 };
265
+ }
266
+
331
267
  @rateLimit({
332
268
  timeSpanMs: config.rateLimitTimeSpan,
333
269
  allowedCalls: config.rateLimitAllowedCalls,
334
270
  exceedHandler,
335
271
  })
336
- @Validate
337
272
  /**
338
273
  * Create a new user
339
- * @param {UserCreationDto} user - User creation data validated by Zod schema
274
+ * @param {User} user - User creation data validated by Zod schema
340
275
  * @returns {Promise<void>}
341
276
  * @throws {ResponseError} 500 - When rate limit exceeded
342
277
  * @throws {ResponseError} 400 - Invalid input data
343
278
  */
344
- public async create(@ZodInput(ZodUserCreationDto) user: UserCreationDto) {
345
- users.push({ ...user, id: users.length + 1 });
279
+ public async create(user: User): Promise<void> {
280
+ users.push(user);
281
+ await userCache.set(user.id.toString(), user as User);
282
+ await usersCache.delete("key");
346
283
  }
347
284
 
348
- @memoizeAsync(config.memoizeTime)
349
- @onError({
350
- func: getUserErrorHandler,
285
+ @memoizeAsync({
286
+ cache: usersCache,
287
+ keyResolver: () => "key",
288
+ expirationTimeMs: config.memoizeTime,
351
289
  })
290
+ @timeout(config.timeout)
352
291
  @rateLimit({
353
292
  timeSpanMs: config.rateLimitTimeSpan,
354
293
  allowedCalls: config.rateLimitAllowedCalls,
355
294
  exceedHandler,
356
295
  })
357
296
  /**
358
- * Get user by ID
359
- * @param {string} id - User ID as string
360
- * @returns {Promise<User>} User details or error object
361
- * @throws {ResponseError} 404 - User not found
362
- * @throws {ResponseError} 400 - Invalid ID format
297
+ * Get all users
298
+ * @returns {Promise<UserDto[]>} List of users with hidden password fields
299
+ * @throws {ResponseError} 500 - When rate limit exceeded
363
300
  */
364
- public async get(id: string): Promise<UserDto> {
365
- const response = parseId(id);
366
- const user = users.find((user) => user.id === response);
367
- if (user == null) throw new ResponseError("User dose not exist.", 404);
368
- return user satisfies User;
301
+ public async getAll(): Promise<UserDto[]> {
302
+ return users as UserDto[];
369
303
  }
370
304
 
371
- @memoizeAsync(config.memoizeTime)
372
- @timeout(config.timeout)
305
+ @memoizeAsync({
306
+ cache: userCache,
307
+ keyResolver: (id: number) => id.toString(),
308
+ expirationTimeMs: config.memoizeTime,
309
+ })
373
310
  @rateLimit({
374
311
  timeSpanMs: config.rateLimitTimeSpan,
375
312
  allowedCalls: config.rateLimitAllowedCalls,
376
313
  exceedHandler,
377
314
  })
378
315
  /**
379
- * Get all users with masked passwords
380
- * @returns {Promise<UserDto[]>} List of users with hidden password fields
381
- * @throws {ResponseError} 500 - When rate limit exceeded
316
+ * Get user by ID
317
+ * @param {number} id - User ID as string
318
+ * @returns {Promise<UserDto>} User details or error object
319
+ * @throws {ResponseError} 404 - User not found
320
+ * @throws {ResponseError} 400 - Invalid ID format
382
321
  */
383
- public async getAll(): Promise<UserDto[]> {
384
- return users.map((user) => ({
385
- ...user,
386
- password: "?",
387
- }));
322
+ public async get(id: number): Promise<UserDto> {
323
+ const user = users.find((user) => user.id === id);
324
+ if (user == null) {
325
+ throw new ResponseError("Product not found");
326
+ }
327
+ return convert(user!, ZodUserDto);
388
328
  }
389
329
  }
390
330
  ```
@@ -401,7 +341,7 @@ Once the application is running, visit the Swagger UI at http://localhost:3000/a
401
341
 
402
342
  ### Decorators
403
343
 
404
- To prevent abuse of your API, you can utilize throttling, and validation decorators from the `utils-decorators` and `ts-zod-decorators` packages respectively. This packages provides decorators that can be applied directly to your service and controller classes.
344
+ To prevent abuse of your API, you can utilize throttling, caching, and error handling decorators from the `utils-decorators` packages respectively. This packages provides decorators that can be applied directly to your service and controller classes.
405
345
 
406
346
  ### Contributing
407
347
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeweaver",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "main": "src/main.ts",
5
5
  "bin": {
6
6
  "create-codeweaver-app": "./command.js"
@@ -26,21 +26,24 @@
26
26
  "nested routers",
27
27
  "decorator",
28
28
  "swagger",
29
- "zod"
29
+ "zod",
30
+ "utils-decorators"
30
31
  ],
31
32
  "author": "js-code-crafter",
32
33
  "license": "MIT",
33
34
  "description": "A lightweight framework built on top of Express and TypeScript",
34
35
  "dependencies": {
36
+ "cors": "^2.8.5",
35
37
  "dotenv": "^17.2.3",
36
38
  "express": "^5.1.0",
37
39
  "express-async-handler": "^1.2.0",
40
+ "ioredis": "^5.8.2",
38
41
  "swagger-jsdoc": "^3.7.0",
39
- "ts-zod4-decorators": "^1.0.0",
40
42
  "utils-decorators": "^2.10.0",
41
43
  "zod": "^4.0.14"
42
44
  },
43
45
  "devDependencies": {
46
+ "@types/cors": "^2.8.19",
44
47
  "@types/express": "^5.0.3",
45
48
  "@types/node": "^24.1.0",
46
49
  "copyfiles": "^2.4.1",
package/src/app.ts ADDED
@@ -0,0 +1,3 @@
1
+ // TODO:
2
+
3
+ export = null;
package/src/config.ts CHANGED
@@ -5,53 +5,10 @@ import {
5
5
  rateLimitAllowedCalls,
6
6
  timeout,
7
7
  portNumber,
8
+ cacheSize,
8
9
  } from "./constants";
9
-
10
- /**
11
- * Server configuration interface
12
- * @interface
13
- * @property {string} url - Base server URL
14
- */
15
- interface Server {
16
- url: string;
17
- }
18
-
19
- /**
20
- * API information structure
21
- * @interface
22
- * @property {string} title - API title
23
- * @property {string} version - API version
24
- * @property {string} description - API description
25
- */
26
- interface Info {
27
- title: string;
28
- version: string;
29
- description: string;
30
- }
31
-
32
- /**
33
- * Swagger definition structure
34
- * @interface
35
- * @property {string} openApi - OpenAPI specification version
36
- * @property {Info} info - API information
37
- * @property {Server[]} servers - List of server configurations
38
- */
39
- interface SwaggerDefinition {
40
- openApi: string;
41
- info: Info;
42
- servers: Server[];
43
- }
44
-
45
- /**
46
- * Swagger configuration options
47
- * @interface
48
- * @property {SwaggerDefinition} swaggerDefinition - Swagger definition object
49
- * @property {string[]} apis - Paths to API documentation files
50
- */
51
- interface SwaggerOptions {
52
- swaggerDefinition: SwaggerDefinition;
53
- apis: string[];
54
- }
10
+ import { SwaggerOptions } from "./swagger-options";
11
+ import { stringToBoolean } from "./utilities/conversion";
55
12
 
56
13
  /**
57
14
  * Main application configuration
@@ -63,11 +20,13 @@ interface SwaggerOptions {
63
20
  interface Config {
64
21
  devMode: boolean;
65
22
  port: number;
23
+ swagger: boolean;
66
24
  swaggerOptions: SwaggerOptions;
67
25
  timeout: number;
68
26
  rateLimitTimeSpan: number;
69
27
  rateLimitAllowedCalls: number;
70
28
  memoizeTime: number;
29
+ cacheSize: number;
71
30
  }
72
31
 
73
32
  const port = Number(process.env.PORT) || portNumber;
@@ -75,6 +34,7 @@ const port = Number(process.env.PORT) || portNumber;
75
34
  let config: Config = {
76
35
  devMode: process.env.NODE_ENV !== productionEnvironment,
77
36
  port,
37
+ swagger: stringToBoolean(process.env.SWAGGER || "true"),
78
38
  swaggerOptions: {
79
39
  swaggerDefinition: {
80
40
  openApi: "3.0.0",
@@ -90,10 +50,10 @@ let config: Config = {
90
50
  ],
91
51
  },
92
52
  apis: [
93
- "./src/routers/index.ts",
94
- "./src/routers/**/*.ts",
95
- "./src/routers/index.js",
96
- "./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",
97
57
  ], // Path to the API docs
98
58
  },
99
59
  timeout: Number(process.env.TIMEOUT) || timeout,
@@ -101,6 +61,7 @@ let config: Config = {
101
61
  rateLimitAllowedCalls:
102
62
  Number(process.env.RATE_LIMIT) || rateLimitAllowedCalls,
103
63
  memoizeTime: Number(process.env.MEMOIZE_TIME) || memoizeTime,
64
+ cacheSize: Number(process.env.CACHE_SIZE) || cacheSize,
104
65
  };
105
66
 
106
67
  export default config;
package/src/constants.ts CHANGED
@@ -2,5 +2,6 @@ export const timeout = 20000;
2
2
  export const rateLimitTimeSpan = 60000;
3
3
  export const rateLimitAllowedCalls = 300;
4
4
  export const memoizeTime = 1000 * 60 * 60;
5
+ export const cacheSize = 1000;
5
6
  export const productionEnvironment = "production";
6
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
+ ];