wynkjs 1.0.1 → 1.0.3
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 +878 -181
- package/dist/dto.d.ts +13 -4
- package/dist/dto.d.ts.map +1 -1
- package/dist/dto.js +4 -8
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/testing/index.d.ts +74 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +106 -0
- package/dist/testing/test-utils.d.ts +31 -0
- package/dist/testing/test-utils.d.ts.map +1 -0
- package/dist/testing/test-utils.js +72 -0
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -2,15 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
<div align="center">
|
|
4
4
|
|
|
5
|
-
**A high-performance TypeScript framework built on Elysia with NestJS-style decorators**
|
|
5
|
+
**A high-performance TypeScript framework built on Elysia for Bun with NestJS-style decorators**
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/wynkjs)
|
|
8
8
|
[](https://opensource.org/licenses/MIT)
|
|
9
9
|
[](https://www.typescriptlang.org/)
|
|
10
|
+
[](https://bun.sh/)
|
|
10
11
|
|
|
11
|
-
_20x faster than Express, easier than
|
|
12
|
+
_20x faster than Express, easier than NestJS, built for Bun_ ⚡
|
|
12
13
|
|
|
13
|
-
[
|
|
14
|
+
[Quick Start](#-quick-start) • [CLI Tools](#️-cli-tools) • [Features](#-features--examples) • [Documentation](#-documentation)
|
|
14
15
|
|
|
15
16
|
</div>
|
|
16
17
|
|
|
@@ -18,9 +19,23 @@ _20x faster than Express, easier than NestJS_ ⚡
|
|
|
18
19
|
|
|
19
20
|
## About
|
|
20
21
|
|
|
21
|
-
WynkJS is a modern, TypeScript-first web framework that brings NestJS-style decorators to the blazing-fast Elysia runtime
|
|
22
|
+
WynkJS is a modern, TypeScript-first web framework that brings NestJS-style decorators to the blazing-fast Elysia runtime built for **Bun**. It's a lightweight NestJS alternative with familiar concepts—controllers, dependency injection, guards, pipes, interceptors, and exception filters—designed for building high‑performance REST APIs and backends on **Bun**. WynkJS embraces ESM, ships first-class types, and keeps things simple so you can move fast without the bloat.
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
**🚀 Get Started in 30 Seconds:**
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
bunx create-wynkjs # Create new project
|
|
28
|
+
cd my-wynkjs-app # Navigate to project
|
|
29
|
+
bun run dev # Start server with hot reload
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Then generate your first API module:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
wynkjs g m product # Generate complete CRUD module
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Keywords: NestJS alternative, Elysia framework, Bun framework, TypeScript decorators, dependency injection (DI), guards, pipes, interceptors, exception filters, fast web framework, REST API, backend.
|
|
24
39
|
|
|
25
40
|
---
|
|
26
41
|
|
|
@@ -28,63 +43,171 @@ Keywords: NestJS alternative, Elysia framework, TypeScript decorators, dependenc
|
|
|
28
43
|
|
|
29
44
|
WynkJS combines the **speed of Elysia** with the **elegant decorator syntax of NestJS**, giving you the best of both worlds:
|
|
30
45
|
|
|
31
|
-
- 🚀 **20x Faster** - Built on Elysia, one of the fastest web frameworks
|
|
46
|
+
- 🚀 **20x Faster** - Built on Elysia, one of the fastest web frameworks for Bun
|
|
32
47
|
- 🎨 **Decorator-Based** - Familiar NestJS-style decorators
|
|
33
48
|
- 💉 **Dependency Injection** - Built-in DI (no need to import reflect-metadata!)
|
|
34
|
-
- 🔒 **
|
|
49
|
+
- 🔒 **TypeScript First** - TypeScript is mandatory, not optional. Full type safety and IntelliSense support
|
|
35
50
|
- 🎯 **Simple & Clean** - Easy to learn, powerful to use
|
|
36
51
|
- 🔌 **Middleware Support** - Guards, interceptors, pipes, filters
|
|
37
|
-
- ⚡ **
|
|
52
|
+
- ⚡ **Bun Only** - Built exclusively for Bun runtime (not Node.js)
|
|
38
53
|
- 📦 **Single Import** - Everything from `wynkjs` (Injectable, Controller, Get, etc.)
|
|
39
54
|
|
|
40
55
|
---
|
|
41
56
|
|
|
42
57
|
## 📦 Installation
|
|
43
58
|
|
|
59
|
+
**Requirements:** Bun 1.0 or higher
|
|
60
|
+
|
|
61
|
+
### Quick Start (Recommended)
|
|
62
|
+
|
|
63
|
+
Create a new WynkJS project with all best practices:
|
|
64
|
+
|
|
44
65
|
```bash
|
|
45
|
-
|
|
66
|
+
bunx create-wynkjs
|
|
67
|
+
# or
|
|
68
|
+
npx create-wynkjs
|
|
46
69
|
```
|
|
47
70
|
|
|
48
|
-
|
|
71
|
+
This will scaffold a complete **TypeScript** project with:
|
|
72
|
+
|
|
73
|
+
- ✅ TypeScript (strict mode, decorators enabled)
|
|
74
|
+
- ✅ ESLint + Prettier (optional)
|
|
75
|
+
- ✅ Hot reload with `bun --watch`
|
|
76
|
+
- ✅ Example code (controllers, services, DTOs)
|
|
77
|
+
- ✅ Git hooks (optional)
|
|
78
|
+
|
|
79
|
+
### Manual Installation
|
|
80
|
+
|
|
81
|
+
Add to an existing project:
|
|
49
82
|
|
|
50
83
|
```bash
|
|
51
84
|
bun add wynkjs elysia
|
|
52
85
|
```
|
|
53
86
|
|
|
87
|
+
**Note**: WynkJS is built specifically for **Bun**. It leverages Elysia's performance optimizations that are designed for Bun runtime. ✨
|
|
88
|
+
|
|
54
89
|
---
|
|
55
90
|
|
|
56
91
|
## 🚀 Quick Start
|
|
57
92
|
|
|
58
|
-
### 1. Create Your
|
|
93
|
+
### 1. Create Your DTOs (Data Transfer Objects)
|
|
59
94
|
|
|
60
95
|
```typescript
|
|
61
|
-
|
|
96
|
+
// user.dto.ts
|
|
97
|
+
import { DTO, CommonDTO } from "wynkjs";
|
|
98
|
+
|
|
99
|
+
export const CreateUserDTO = DTO.Strict({
|
|
100
|
+
name: DTO.Optional(DTO.String({ minLength: 2, maxLength: 50 })),
|
|
101
|
+
email: CommonDTO.Email({
|
|
102
|
+
description: "User email address",
|
|
103
|
+
}),
|
|
104
|
+
mobile: DTO.Optional(
|
|
105
|
+
DTO.String({
|
|
106
|
+
pattern: "^[6-9]{1}[0-9]{9}$",
|
|
107
|
+
errorMessage: "Invalid mobile number",
|
|
108
|
+
})
|
|
109
|
+
),
|
|
110
|
+
age: DTO.Optional(DTO.Number({ minimum: 18 })),
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
export interface CreateUserType {
|
|
114
|
+
name?: string;
|
|
115
|
+
email?: string;
|
|
116
|
+
mobile?: string;
|
|
117
|
+
age?: number;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export const UserIdDto = DTO.Object({
|
|
121
|
+
id: DTO.String({ minLength: 2, maxLength: 50 }),
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
export interface ParamIdType {
|
|
125
|
+
id: string;
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 2. Create a Service with Dependency Injection
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
// email.service.ts
|
|
133
|
+
import { Injectable } from "wynkjs";
|
|
134
|
+
|
|
135
|
+
@Injectable()
|
|
136
|
+
export class EmailService {
|
|
137
|
+
async sendWelcomeEmail(email: string, userName: string): Promise<void> {
|
|
138
|
+
console.log(`📧 Sending welcome email to ${email}`);
|
|
139
|
+
// Your email logic here
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### 3. Create Your Controller
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
// user.controller.ts
|
|
148
|
+
import {
|
|
149
|
+
Controller,
|
|
150
|
+
Get,
|
|
151
|
+
Post,
|
|
152
|
+
Body,
|
|
153
|
+
Param,
|
|
154
|
+
Patch,
|
|
155
|
+
Query,
|
|
156
|
+
Injectable,
|
|
157
|
+
NotFoundException,
|
|
158
|
+
} from "wynkjs";
|
|
159
|
+
import { CreateUserDTO, UserIdDto } from "./user.dto";
|
|
160
|
+
import type { CreateUserType, ParamIdType } from "./user.dto";
|
|
161
|
+
import { EmailService } from "./email.service";
|
|
62
162
|
|
|
63
163
|
@Injectable()
|
|
64
164
|
@Controller("/users")
|
|
65
165
|
export class UserController {
|
|
166
|
+
constructor(private emailService: EmailService) {}
|
|
167
|
+
|
|
66
168
|
@Get("/")
|
|
67
169
|
async list() {
|
|
68
170
|
return { users: ["Alice", "Bob", "Charlie"] };
|
|
69
171
|
}
|
|
70
172
|
|
|
71
|
-
@Post(
|
|
72
|
-
|
|
173
|
+
@Post({
|
|
174
|
+
path: "/",
|
|
175
|
+
body: CreateUserDTO,
|
|
176
|
+
})
|
|
177
|
+
async create(@Body() body: CreateUserType) {
|
|
178
|
+
// Send welcome email using injected service
|
|
179
|
+
if (body.email && body.name) {
|
|
180
|
+
await this.emailService.sendWelcomeEmail(body.email, body.name);
|
|
181
|
+
}
|
|
182
|
+
|
|
73
183
|
return { message: "User created", data: body };
|
|
74
184
|
}
|
|
75
185
|
|
|
76
|
-
@Get("/:id")
|
|
186
|
+
@Get({ path: "/:id", params: UserIdDto })
|
|
77
187
|
async findOne(@Param("id") id: string) {
|
|
78
188
|
return { user: { id, name: "Alice" } };
|
|
79
189
|
}
|
|
190
|
+
|
|
191
|
+
@Patch({
|
|
192
|
+
path: "/:id",
|
|
193
|
+
params: UserIdDto,
|
|
194
|
+
})
|
|
195
|
+
async update(@Param("id") id: string, @Body() body: any) {
|
|
196
|
+
if (id === "nonexistent") {
|
|
197
|
+
throw new NotFoundException("User not found");
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return { message: "User updated", id, data: body };
|
|
201
|
+
}
|
|
80
202
|
}
|
|
81
203
|
```
|
|
82
204
|
|
|
83
|
-
###
|
|
205
|
+
### 4. Bootstrap Your Application
|
|
84
206
|
|
|
85
207
|
```typescript
|
|
208
|
+
// index.ts
|
|
86
209
|
import { WynkFactory } from "wynkjs";
|
|
87
|
-
import { UserController } from "./
|
|
210
|
+
import { UserController } from "./user.controller";
|
|
88
211
|
|
|
89
212
|
const app = WynkFactory.create({
|
|
90
213
|
controllers: [UserController],
|
|
@@ -95,12 +218,32 @@ await app.listen(3000);
|
|
|
95
218
|
console.log("🚀 Server running on http://localhost:3000");
|
|
96
219
|
```
|
|
97
220
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
### 3. Run Your Server
|
|
221
|
+
### 5. Run Your Server
|
|
101
222
|
|
|
102
223
|
```bash
|
|
103
224
|
bun run index.ts
|
|
225
|
+
# or with --watch for hot reload
|
|
226
|
+
bun --watch index.ts
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### 6. Test Your API
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
# List users
|
|
233
|
+
curl http://localhost:3000/users
|
|
234
|
+
|
|
235
|
+
# Create user (with validation)
|
|
236
|
+
curl -X POST http://localhost:3000/users \
|
|
237
|
+
-H "Content-Type: application/json" \
|
|
238
|
+
-d '{"name":"John","email":"john@example.com","age":25}'
|
|
239
|
+
|
|
240
|
+
# Get user by ID
|
|
241
|
+
curl http://localhost:3000/users/123
|
|
242
|
+
|
|
243
|
+
# Update user
|
|
244
|
+
curl -X PATCH http://localhost:3000/users/123 \
|
|
245
|
+
-H "Content-Type: application/json" \
|
|
246
|
+
-d '{"email":"newemail@example.com"}'
|
|
104
247
|
```
|
|
105
248
|
|
|
106
249
|
That's it! 🎉
|
|
@@ -152,6 +295,159 @@ That's it! 🎉
|
|
|
152
295
|
|
|
153
296
|
---
|
|
154
297
|
|
|
298
|
+
## 🛠️ CLI Tools
|
|
299
|
+
|
|
300
|
+
WynkJS provides powerful CLI tools to speed up your development workflow:
|
|
301
|
+
|
|
302
|
+
### 📦 create-wynkjs - Project Scaffolding
|
|
303
|
+
|
|
304
|
+
Quickly scaffold a new WynkJS project with best practices:
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
# Create a new project
|
|
308
|
+
bunx create-wynkjs
|
|
309
|
+
# or
|
|
310
|
+
npx create-wynkjs
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**What you get:**
|
|
314
|
+
|
|
315
|
+
- ✅ **TypeScript** - Strict mode with decorators enabled (mandatory)
|
|
316
|
+
- ✅ **ESLint** - Code linting with TypeScript rules (optional)
|
|
317
|
+
- ✅ **Prettier** - Code formatting (optional)
|
|
318
|
+
- ✅ **Husky** - Git hooks for pre-commit checks (optional)
|
|
319
|
+
- ✅ **Hot Reload** - `bun --watch` for instant feedback
|
|
320
|
+
- ✅ **Working Example** - Complete CRUD controller, service, and DTOs
|
|
321
|
+
|
|
322
|
+
**Example:**
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
bunx create-wynkjs
|
|
326
|
+
# Choose project name: my-api
|
|
327
|
+
# Add ESLint? Yes
|
|
328
|
+
# Add Prettier? Yes
|
|
329
|
+
# Add Husky? No
|
|
330
|
+
|
|
331
|
+
cd my-api
|
|
332
|
+
bun run dev
|
|
333
|
+
# 🚀 Server running on http://localhost:3000
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### ⚡ wynkjs-cli - Code Generator
|
|
337
|
+
|
|
338
|
+
Generate modules, controllers, services, and DTOs instantly:
|
|
339
|
+
|
|
340
|
+
```bash
|
|
341
|
+
# Install globally (recommended)
|
|
342
|
+
bun add -g wynkjs-cli
|
|
343
|
+
|
|
344
|
+
# Or install in project
|
|
345
|
+
bun add -D wynkjs-cli
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
**Commands:**
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
# Generate complete CRUD module (controller + service + DTO)
|
|
352
|
+
wynkjs generate module product
|
|
353
|
+
# or short: wynkjs g m product
|
|
354
|
+
|
|
355
|
+
# Generate controller only (all HTTP methods)
|
|
356
|
+
wynkjs generate controller user
|
|
357
|
+
# or short: wynkjs g c user
|
|
358
|
+
|
|
359
|
+
# Generate service only (all CRUD methods)
|
|
360
|
+
wynkjs generate service order
|
|
361
|
+
# or short: wynkjs g s order
|
|
362
|
+
|
|
363
|
+
# Generate DTO only (Create, Update, ID DTOs)
|
|
364
|
+
wynkjs generate dto payment
|
|
365
|
+
# or short: wynkjs g d payment
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
**What it generates:**
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
wynkjs g m product
|
|
372
|
+
# Creates:
|
|
373
|
+
# src/modules/product/
|
|
374
|
+
# ├── product.controller.ts # Full CRUD controller
|
|
375
|
+
# ├── product.service.ts # All CRUD methods
|
|
376
|
+
# └── product.dto.ts # Validation schemas
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
**Auto-imports:** Controllers are automatically imported and added to `src/index.ts`!
|
|
380
|
+
|
|
381
|
+
**Generated Code Example:**
|
|
382
|
+
|
|
383
|
+
```typescript
|
|
384
|
+
// product.controller.ts - Ready to use!
|
|
385
|
+
@Injectable()
|
|
386
|
+
@Controller("/product")
|
|
387
|
+
export class ProductController {
|
|
388
|
+
constructor(private productService: ProductService) {}
|
|
389
|
+
|
|
390
|
+
@Get("/")
|
|
391
|
+
async findAll() {
|
|
392
|
+
/* ... */
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
@Post({ path: "/", body: CreateProductDTO })
|
|
396
|
+
async create(@Body() body: CreateProductType) {
|
|
397
|
+
/* ... */
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
@Get({ path: "/:id", params: ProductIdDto })
|
|
401
|
+
async findOne(@Param("id") id: string) {
|
|
402
|
+
/* ... */
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
@Put({ path: "/:id", params: ProductIdDto, body: UpdateProductDTO })
|
|
406
|
+
async update(@Param("id") id: string, @Body() body: UpdateProductType) {
|
|
407
|
+
/* ... */
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
@Delete({ path: "/:id", params: ProductIdDto })
|
|
411
|
+
async remove(@Param("id") id: string) {
|
|
412
|
+
/* ... */
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
**Full Workflow:**
|
|
418
|
+
|
|
419
|
+
```bash
|
|
420
|
+
# 1. Create new project
|
|
421
|
+
bunx create-wynkjs
|
|
422
|
+
cd my-api
|
|
423
|
+
|
|
424
|
+
# 2. Generate your first module
|
|
425
|
+
wynkjs g m product
|
|
426
|
+
|
|
427
|
+
# 3. Start developing
|
|
428
|
+
bun run dev
|
|
429
|
+
|
|
430
|
+
# 4. Your API is ready!
|
|
431
|
+
curl http://localhost:3000/product
|
|
432
|
+
# {"data":[]}
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
**Custom Configuration** (optional):
|
|
436
|
+
|
|
437
|
+
Create `wynkjs.config.json` in your project root:
|
|
438
|
+
|
|
439
|
+
```json
|
|
440
|
+
{
|
|
441
|
+
"srcDir": "src",
|
|
442
|
+
"controllersDir": "src/controllers",
|
|
443
|
+
"servicesDir": "src/services",
|
|
444
|
+
"dtoDir": "src/dto",
|
|
445
|
+
"modulesDir": "src/modules"
|
|
446
|
+
}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
155
451
|
## 🎯 Features & Examples
|
|
156
452
|
|
|
157
453
|
### 🔒 Authentication with Guards
|
|
@@ -211,38 +507,52 @@ export class AdminController {
|
|
|
211
507
|
|
|
212
508
|
### 💉 Dependency Injection
|
|
213
509
|
|
|
510
|
+
WynkJS includes powerful dependency injection with **zero setup required**:
|
|
511
|
+
|
|
214
512
|
```typescript
|
|
215
|
-
//
|
|
216
|
-
import { Injectable
|
|
513
|
+
// email.service.ts
|
|
514
|
+
import { Injectable } from "wynkjs";
|
|
217
515
|
|
|
218
516
|
@Injectable()
|
|
219
|
-
export class
|
|
220
|
-
async
|
|
221
|
-
|
|
517
|
+
export class EmailService {
|
|
518
|
+
async sendWelcomeEmail(email: string, userName: string): Promise<void> {
|
|
519
|
+
console.log(`📧 Sending welcome email to ${email}`);
|
|
520
|
+
// Your email sending logic
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
async sendPasswordResetEmail(
|
|
524
|
+
email: string,
|
|
525
|
+
resetToken: string
|
|
526
|
+
): Promise<void> {
|
|
527
|
+
console.log(`🔐 Sending password reset to ${email}`);
|
|
528
|
+
// Your reset email logic
|
|
222
529
|
}
|
|
223
530
|
}
|
|
224
531
|
|
|
532
|
+
// user.controller.ts
|
|
533
|
+
import { Controller, Post, Body, Injectable } from "wynkjs";
|
|
534
|
+
import { EmailService } from "./email.service";
|
|
535
|
+
|
|
225
536
|
@Injectable()
|
|
226
537
|
@Controller("/users")
|
|
227
538
|
export class UserController {
|
|
228
|
-
|
|
539
|
+
// ✨ EmailService is automatically injected!
|
|
540
|
+
constructor(private emailService: EmailService) {}
|
|
229
541
|
|
|
230
|
-
@
|
|
231
|
-
async
|
|
232
|
-
|
|
233
|
-
|
|
542
|
+
@Post("/")
|
|
543
|
+
async create(@Body() body: { name: string; email: string }) {
|
|
544
|
+
// Use the injected service
|
|
545
|
+
await this.emailService.sendWelcomeEmail(body.email, body.name);
|
|
546
|
+
return { message: "User created and email sent!" };
|
|
234
547
|
}
|
|
235
|
-
}
|
|
236
|
-
```
|
|
237
548
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
return [{ id: 1, name: "Alice" }];
|
|
549
|
+
@Post("/send-reset-email")
|
|
550
|
+
async sendPasswordReset(@Body() body: { email: string }) {
|
|
551
|
+
await this.emailService.sendPasswordResetEmail(
|
|
552
|
+
body.email,
|
|
553
|
+
"reset-token-123"
|
|
554
|
+
);
|
|
555
|
+
return { message: "Password reset email sent" };
|
|
246
556
|
}
|
|
247
557
|
}
|
|
248
558
|
```
|
|
@@ -254,15 +564,15 @@ export class UserService {
|
|
|
254
564
|
### 🗃️ Database Integration (Drizzle ORM)
|
|
255
565
|
|
|
256
566
|
```typescript
|
|
257
|
-
import { drizzle } from "drizzle-orm/
|
|
258
|
-
import {
|
|
567
|
+
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
568
|
+
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core";
|
|
259
569
|
import { InjectTable, registerTables } from "wynkjs";
|
|
260
570
|
|
|
261
571
|
// Define your table
|
|
262
|
-
const userTable =
|
|
263
|
-
id:
|
|
264
|
-
name:
|
|
265
|
-
email:
|
|
572
|
+
const userTable = sqliteTable("users", {
|
|
573
|
+
id: integer("id").primaryKey(),
|
|
574
|
+
name: text("name").notNull(),
|
|
575
|
+
email: text("email").notNull().unique(),
|
|
266
576
|
});
|
|
267
577
|
|
|
268
578
|
// Register tables
|
|
@@ -282,16 +592,82 @@ export class UserService {
|
|
|
282
592
|
|
|
283
593
|
### 📝 Request Validation
|
|
284
594
|
|
|
285
|
-
WynkJS provides automatic request validation with customizable error formats:
|
|
595
|
+
WynkJS provides automatic request validation with **full IntelliSense support** and customizable error formats:
|
|
286
596
|
|
|
287
597
|
```typescript
|
|
288
|
-
|
|
598
|
+
// user.dto.ts
|
|
599
|
+
import { DTO, CommonDTO } from "wynkjs";
|
|
600
|
+
|
|
601
|
+
// ✨ Full IntelliSense when typing DTO.String(), DTO.Number(), etc!
|
|
602
|
+
export const CreateUserDTO = DTO.Strict({
|
|
603
|
+
name: DTO.Optional(DTO.String({ minLength: 2, maxLength: 50 })),
|
|
604
|
+
email: CommonDTO.Email({
|
|
605
|
+
description: "User email address",
|
|
606
|
+
}),
|
|
607
|
+
mobile: DTO.Optional(
|
|
608
|
+
DTO.String({
|
|
609
|
+
pattern: "^[6-9]{1}[0-9]{9}$",
|
|
610
|
+
errorMessage: "Invalid mobile number",
|
|
611
|
+
})
|
|
612
|
+
),
|
|
613
|
+
age: DTO.Optional(DTO.Number({ minimum: 18 })),
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
export interface CreateUserType {
|
|
617
|
+
name?: string;
|
|
618
|
+
email?: string;
|
|
619
|
+
mobile?: string;
|
|
620
|
+
age?: number;
|
|
621
|
+
}
|
|
289
622
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
623
|
+
export const UserUpdateDTO = DTO.Strict({
|
|
624
|
+
email: DTO.Optional(DTO.String({ format: "email", minLength: 5 })),
|
|
625
|
+
age: DTO.Optional(DTO.Number({ minimum: 18 })),
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
export interface UserUpdateType {
|
|
629
|
+
email?: string;
|
|
630
|
+
age?: number;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// user.controller.ts
|
|
634
|
+
import { Controller, Post, Patch, Body, Param } from "wynkjs";
|
|
635
|
+
import { CreateUserDTO, UserUpdateDTO } from "./user.dto";
|
|
636
|
+
import type { CreateUserType, UserUpdateType } from "./user.dto";
|
|
637
|
+
|
|
638
|
+
@Controller("/users")
|
|
639
|
+
export class UserController {
|
|
640
|
+
@Post({
|
|
641
|
+
path: "/",
|
|
642
|
+
body: CreateUserDTO, // ✅ Automatic validation
|
|
643
|
+
})
|
|
644
|
+
async create(@Body() body: CreateUserType) {
|
|
645
|
+
// Body is validated automatically!
|
|
646
|
+
// Invalid requests get clear error messages
|
|
647
|
+
return { message: "User created", data: body };
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
@Patch({
|
|
651
|
+
path: "/:id",
|
|
652
|
+
body: UserUpdateDTO, // ✅ Different schema for updates
|
|
653
|
+
})
|
|
654
|
+
async update(@Param("id") id: string, @Body() body: UserUpdateType) {
|
|
655
|
+
return { message: "User updated", id, data: body };
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
**Customize validation error format:**
|
|
661
|
+
|
|
662
|
+
```typescript
|
|
663
|
+
import { WynkFactory, FormatErrorFormatter } from "wynkjs";
|
|
664
|
+
|
|
665
|
+
const app = WynkFactory.create({
|
|
666
|
+
controllers: [UserController],
|
|
667
|
+
// Choose your validation error format
|
|
668
|
+
validationErrorFormatter: new FormatErrorFormatter(), // NestJS-style (recommended)
|
|
669
|
+
// validationErrorFormatter: new SimpleErrorFormatter(), // Simple array
|
|
670
|
+
// validationErrorFormatter: new DetailedErrorFormatter(), // Detailed with paths
|
|
295
671
|
});
|
|
296
672
|
|
|
297
673
|
@Controller("/users")
|
|
@@ -318,7 +694,70 @@ const app = WynkFactory.create({
|
|
|
318
694
|
});
|
|
319
695
|
```
|
|
320
696
|
|
|
321
|
-
**See [VALIDATION_FORMATTERS.md](./docs/VALIDATION_FORMATTERS.md) for all available error formats**
|
|
697
|
+
**See [VALIDATION_FORMATTERS.md](./docs-wynkjs/VALIDATION_FORMATTERS.md) for all available error formats**
|
|
698
|
+
|
|
699
|
+
### 🚫 Exception Handling
|
|
700
|
+
|
|
701
|
+
```typescript
|
|
702
|
+
import { Controller, Get, Param, NotFoundException } from "wynkjs";
|
|
703
|
+
|
|
704
|
+
@Controller("/users")
|
|
705
|
+
export class UserController {
|
|
706
|
+
@Get("/:id")
|
|
707
|
+
async findOne(@Param("id") id: string) {
|
|
708
|
+
// Built-in exceptions
|
|
709
|
+
if (id === "nonexistent") {
|
|
710
|
+
throw new NotFoundException("User not found");
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
return { user: { id, name: "Alice" } };
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
**Built-in exceptions:**
|
|
719
|
+
|
|
720
|
+
- `BadRequestException` - 400
|
|
721
|
+
- `UnauthorizedException` - 401
|
|
722
|
+
- `ForbiddenException` - 403
|
|
723
|
+
- `NotFoundException` - 404
|
|
724
|
+
- `InternalServerErrorException` - 500
|
|
725
|
+
|
|
726
|
+
### 🔄 Multiple Params and Query Validation
|
|
727
|
+
|
|
728
|
+
```typescript
|
|
729
|
+
// user.dto.ts
|
|
730
|
+
export const MultiParamDto = DTO.Object({
|
|
731
|
+
id1: DTO.String({ minLength: 2, maxLength: 50 }),
|
|
732
|
+
id2: DTO.String({ minLength: 2, maxLength: 50 }),
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
export const UserQueryDto = DTO.Strict({
|
|
736
|
+
includePosts: DTO.Optional(DTO.Boolean({ default: false })),
|
|
737
|
+
includeComments: DTO.Optional(DTO.Boolean({ default: false })),
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
// user.controller.ts
|
|
741
|
+
@Post({
|
|
742
|
+
path: "/:id1/:id2",
|
|
743
|
+
body: CreateUserDTO,
|
|
744
|
+
params: MultiParamDto, // ✅ Validates route params
|
|
745
|
+
query: UserQueryDto, // ✅ Validates query params
|
|
746
|
+
})
|
|
747
|
+
async create(
|
|
748
|
+
@Body() body: CreateUserType,
|
|
749
|
+
@Param("id1") id1: string,
|
|
750
|
+
@Param("id2") id2: string,
|
|
751
|
+
@Query() query: UserQueryType
|
|
752
|
+
) {
|
|
753
|
+
return {
|
|
754
|
+
message: "User created",
|
|
755
|
+
data: body,
|
|
756
|
+
params: { id1, id2 },
|
|
757
|
+
query
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
```
|
|
322
761
|
|
|
323
762
|
### 🔄 Multiple Middleware
|
|
324
763
|
|
|
@@ -348,217 +787,475 @@ export class ApiController {
|
|
|
348
787
|
|
|
349
788
|
## 🏗️ Project Structure
|
|
350
789
|
|
|
790
|
+
Recommended project structure for WynkJS applications:
|
|
791
|
+
|
|
351
792
|
```
|
|
352
793
|
my-wynk-app/
|
|
353
794
|
├── src/
|
|
354
|
-
│ ├──
|
|
355
|
-
│ │ ├── user
|
|
356
|
-
│ │
|
|
357
|
-
│ ├──
|
|
358
|
-
│ │
|
|
359
|
-
│ │ └──
|
|
360
|
-
│ ├──
|
|
361
|
-
│ │
|
|
362
|
-
│ │
|
|
363
|
-
│ ├──
|
|
364
|
-
│ │ └──
|
|
795
|
+
│ ├── modules/
|
|
796
|
+
│ │ ├── user/
|
|
797
|
+
│ │ │ ├── user.controller.ts
|
|
798
|
+
│ │ │ ├── user.service.ts
|
|
799
|
+
│ │ │ └── user.dto.ts
|
|
800
|
+
│ │ └── product/
|
|
801
|
+
│ │ ├── product.controller.ts
|
|
802
|
+
│ │ ├── product.service.ts
|
|
803
|
+
│ │ └── product.dto.ts
|
|
804
|
+
│ ├── exceptions/
|
|
805
|
+
│ │ └── custom.exceptions.ts
|
|
806
|
+
│ ├── guards/
|
|
807
|
+
│ │ └── auth.guard.ts
|
|
808
|
+
│ ├── filters/
|
|
809
|
+
│ │ └── http-exception.filter.ts
|
|
365
810
|
│ └── index.ts
|
|
366
811
|
├── package.json
|
|
367
812
|
└── tsconfig.json
|
|
368
813
|
```
|
|
369
814
|
|
|
815
|
+
**Module-based Organization:**
|
|
816
|
+
|
|
817
|
+
- Each feature/domain lives in its own module folder
|
|
818
|
+
- Controllers, services, and DTOs are co-located
|
|
819
|
+
- Easy to navigate and maintain
|
|
820
|
+
- Generated automatically by `wynkjs-cli`
|
|
821
|
+
|
|
370
822
|
---
|
|
371
823
|
|
|
372
|
-
## 🎨 Complete Example
|
|
824
|
+
## 🎨 Complete Working Example
|
|
825
|
+
|
|
826
|
+
Here's a complete, production-ready example with all features:
|
|
373
827
|
|
|
374
828
|
```typescript
|
|
375
|
-
//
|
|
376
|
-
import "
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
829
|
+
// dto/user.dto.ts
|
|
830
|
+
import { DTO, CommonDTO } from "wynkjs";
|
|
831
|
+
|
|
832
|
+
export const CreateUserDTO = DTO.Strict({
|
|
833
|
+
name: DTO.Optional(DTO.String({ minLength: 2, maxLength: 50 })),
|
|
834
|
+
email: CommonDTO.Email({ description: "User email address" }),
|
|
835
|
+
mobile: DTO.Optional(
|
|
836
|
+
DTO.String({
|
|
837
|
+
pattern: "^[6-9]{1}[0-9]{9}$",
|
|
838
|
+
errorMessage: "Invalid mobile number",
|
|
839
|
+
})
|
|
840
|
+
),
|
|
841
|
+
age: DTO.Optional(DTO.Number({ minimum: 18 })),
|
|
842
|
+
});
|
|
380
843
|
|
|
381
|
-
const
|
|
382
|
-
|
|
383
|
-
globalGuards: [],
|
|
384
|
-
globalInterceptors: [],
|
|
844
|
+
export const UserIdDto = DTO.Object({
|
|
845
|
+
id: DTO.String({ minLength: 2, maxLength: 50 }),
|
|
385
846
|
});
|
|
386
847
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
848
|
+
export interface CreateUserType {
|
|
849
|
+
name?: string;
|
|
850
|
+
email: string;
|
|
851
|
+
mobile?: string;
|
|
852
|
+
age?: number;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
// services/email.service.ts
|
|
856
|
+
import { Injectable } from "wynkjs";
|
|
857
|
+
|
|
858
|
+
@Injectable()
|
|
859
|
+
export class EmailService {
|
|
860
|
+
async sendWelcomeEmail(email: string, userName: string): Promise<void> {
|
|
861
|
+
console.log(`📧 Sending welcome email to ${email}`);
|
|
862
|
+
// Your email sending logic (SendGrid, AWS SES, etc.)
|
|
863
|
+
}
|
|
864
|
+
}
|
|
390
865
|
|
|
391
|
-
```typescript
|
|
392
866
|
// controllers/user.controller.ts
|
|
393
867
|
import {
|
|
394
|
-
Injectable,
|
|
395
|
-
Inject,
|
|
396
868
|
Controller,
|
|
397
869
|
Get,
|
|
398
870
|
Post,
|
|
399
|
-
|
|
400
|
-
Delete,
|
|
871
|
+
Patch,
|
|
401
872
|
Body,
|
|
402
873
|
Param,
|
|
403
|
-
|
|
874
|
+
Injectable,
|
|
875
|
+
NotFoundException,
|
|
404
876
|
} from "wynkjs";
|
|
405
|
-
import {
|
|
406
|
-
import {
|
|
877
|
+
import { CreateUserDTO, UserIdDto } from "../dto/user.dto";
|
|
878
|
+
import type { CreateUserType } from "../dto/user.dto";
|
|
879
|
+
import { EmailService } from "../services/email.service";
|
|
407
880
|
|
|
408
881
|
@Injectable()
|
|
409
882
|
@Controller("/users")
|
|
410
|
-
@Use(jwtGuard)
|
|
411
883
|
export class UserController {
|
|
412
|
-
constructor(
|
|
884
|
+
constructor(private emailService: EmailService) {}
|
|
413
885
|
|
|
414
886
|
@Get("/")
|
|
415
887
|
async list() {
|
|
416
|
-
|
|
417
|
-
return { users };
|
|
888
|
+
return { users: ["Alice", "Bob", "Charlie"] };
|
|
418
889
|
}
|
|
419
890
|
|
|
420
|
-
@
|
|
421
|
-
async
|
|
422
|
-
|
|
423
|
-
|
|
891
|
+
@Post({ path: "/", body: CreateUserDTO })
|
|
892
|
+
async create(@Body() body: CreateUserType) {
|
|
893
|
+
// Send welcome email using injected service
|
|
894
|
+
if (body.email && body.name) {
|
|
895
|
+
await this.emailService.sendWelcomeEmail(body.email, body.name);
|
|
896
|
+
}
|
|
897
|
+
return { message: "User created", data: body };
|
|
424
898
|
}
|
|
425
899
|
|
|
426
|
-
@
|
|
427
|
-
async
|
|
428
|
-
|
|
429
|
-
|
|
900
|
+
@Get({ path: "/:id", params: UserIdDto })
|
|
901
|
+
async findOne(@Param("id") id: string) {
|
|
902
|
+
if (id === "nonexistent") {
|
|
903
|
+
throw new NotFoundException("User not found");
|
|
904
|
+
}
|
|
905
|
+
return { user: { id, name: "Alice" } };
|
|
430
906
|
}
|
|
431
907
|
|
|
432
|
-
@
|
|
908
|
+
@Patch({ path: "/:id", params: UserIdDto })
|
|
433
909
|
async update(@Param("id") id: string, @Body() body: any) {
|
|
434
|
-
|
|
435
|
-
return { message: "User updated", user };
|
|
910
|
+
return { message: "User updated", id, data: body };
|
|
436
911
|
}
|
|
912
|
+
}
|
|
437
913
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
914
|
+
// index.ts
|
|
915
|
+
import { WynkFactory } from "wynkjs";
|
|
916
|
+
import { UserController } from "./controllers/user.controller";
|
|
917
|
+
|
|
918
|
+
const app = WynkFactory.create({
|
|
919
|
+
controllers: [UserController],
|
|
920
|
+
});
|
|
921
|
+
|
|
922
|
+
await app.listen(3000);
|
|
923
|
+
console.log("🚀 Server running on http://localhost:3000");
|
|
924
|
+
```
|
|
925
|
+
|
|
926
|
+
**Test the API:**
|
|
927
|
+
|
|
928
|
+
```bash
|
|
929
|
+
# List all users
|
|
930
|
+
curl http://localhost:3000/users
|
|
931
|
+
|
|
932
|
+
# Create a new user (with validation and email)
|
|
933
|
+
curl -X POST http://localhost:3000/users \
|
|
934
|
+
-H "Content-Type: application/json" \
|
|
935
|
+
-d '{"name":"Alice","email":"alice@example.com","age":25}'
|
|
936
|
+
|
|
937
|
+
# Get user by ID
|
|
938
|
+
curl http://localhost:3000/users/123
|
|
939
|
+
|
|
940
|
+
# Update user
|
|
941
|
+
curl -X PATCH http://localhost:3000/users/123 \
|
|
942
|
+
-H "Content-Type: application/json" \
|
|
943
|
+
-d '{"email":"newemail@example.com"}'
|
|
944
|
+
|
|
945
|
+
# Test 404 exception
|
|
946
|
+
curl http://localhost:3000/users/nonexistent
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
---
|
|
950
|
+
|
|
951
|
+
## 📖 API Reference
|
|
952
|
+
|
|
953
|
+
### Core Decorators
|
|
954
|
+
|
|
955
|
+
#### `@Controller(basePath?: string)`
|
|
956
|
+
|
|
957
|
+
Define a controller class with optional base path.
|
|
958
|
+
|
|
959
|
+
```typescript
|
|
960
|
+
@Controller("/users")
|
|
961
|
+
export class UserController {
|
|
962
|
+
// All routes will be prefixed with /users
|
|
443
963
|
}
|
|
444
964
|
```
|
|
445
965
|
|
|
966
|
+
#### `@Get(path?: string)` / `@Post()` / `@Patch()` / `@Delete()`
|
|
967
|
+
|
|
968
|
+
HTTP method decorators with optional path and options.
|
|
969
|
+
|
|
446
970
|
```typescript
|
|
447
|
-
//
|
|
971
|
+
@Get("/") // GET /users
|
|
972
|
+
@Post({ path: "/", body: CreateUserDTO }) // POST with validation
|
|
973
|
+
@Patch({ path: "/:id", params: UserIdDto }) // PATCH with param validation
|
|
974
|
+
```
|
|
448
975
|
|
|
449
|
-
|
|
450
|
-
export class UserService {
|
|
451
|
-
private users = [
|
|
452
|
-
{ id: "1", name: "Alice", email: "alice@example.com" },
|
|
453
|
-
{ id: "2", name: "Bob", email: "bob@example.com" },
|
|
454
|
-
];
|
|
976
|
+
#### `@Body()` / `@Param(key?)` / `@Query(key?)`
|
|
455
977
|
|
|
456
|
-
|
|
457
|
-
return this.users;
|
|
458
|
-
}
|
|
978
|
+
Parameter extraction decorators.
|
|
459
979
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
980
|
+
```typescript
|
|
981
|
+
async create(
|
|
982
|
+
@Body() body: CreateUserType, // Full body
|
|
983
|
+
@Param("id") id: string, // Single param
|
|
984
|
+
@Query() query: UserQueryType // Full query object
|
|
985
|
+
) {}
|
|
986
|
+
```
|
|
463
987
|
|
|
464
|
-
|
|
465
|
-
const user = { id: Date.now().toString(), ...data };
|
|
466
|
-
this.users.push(user);
|
|
467
|
-
return user;
|
|
468
|
-
}
|
|
988
|
+
#### `@Injectable()`
|
|
469
989
|
|
|
470
|
-
|
|
471
|
-
const index = this.users.findIndex((u) => u.id === id);
|
|
472
|
-
if (index !== -1) {
|
|
473
|
-
this.users[index] = { ...this.users[index], ...data };
|
|
474
|
-
return this.users[index];
|
|
475
|
-
}
|
|
476
|
-
return null;
|
|
477
|
-
}
|
|
990
|
+
Mark a class as injectable for dependency injection.
|
|
478
991
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
}
|
|
484
|
-
}
|
|
992
|
+
```typescript
|
|
993
|
+
@Injectable()
|
|
994
|
+
export class EmailService {
|
|
995
|
+
// This service can be injected into controllers
|
|
485
996
|
}
|
|
486
997
|
```
|
|
487
998
|
|
|
488
|
-
|
|
999
|
+
### DTO Helpers
|
|
489
1000
|
|
|
490
|
-
|
|
1001
|
+
#### `DTO.Strict(properties, options?)`
|
|
491
1002
|
|
|
492
|
-
|
|
1003
|
+
Create object schema that strips additional properties.
|
|
493
1004
|
|
|
494
|
-
|
|
1005
|
+
```typescript
|
|
1006
|
+
const UserDTO = DTO.Strict({
|
|
1007
|
+
name: DTO.String(),
|
|
1008
|
+
email: DTO.String({ format: "email" }),
|
|
1009
|
+
});
|
|
1010
|
+
```
|
|
495
1011
|
|
|
496
|
-
|
|
1012
|
+
#### `CommonDTO` Patterns
|
|
497
1013
|
|
|
498
|
-
-
|
|
499
|
-
- `globalGuards?: Array<Guard>` - Global guards (optional)
|
|
500
|
-
- `globalInterceptors?: Array<Interceptor>` - Global interceptors (optional)
|
|
501
|
-
- `globalPipes?: Array<Pipe>` - Global pipes (optional)
|
|
502
|
-
- `globalFilters?: Array<Filter>` - Global exception filters (optional)
|
|
1014
|
+
Pre-built validation patterns for common use cases.
|
|
503
1015
|
|
|
504
|
-
|
|
1016
|
+
```typescript
|
|
1017
|
+
CommonDTO.Email(); // Email validation
|
|
1018
|
+
CommonDTO.Name(); // Name (2-50 chars)
|
|
1019
|
+
CommonDTO.Password(); // Password (min 6 chars)
|
|
1020
|
+
CommonDTO.UUID(); // UUID format
|
|
1021
|
+
CommonDTO.PhoneIN(); // Indian phone number
|
|
1022
|
+
```
|
|
505
1023
|
|
|
506
|
-
###
|
|
1024
|
+
### Exception Classes
|
|
507
1025
|
|
|
508
|
-
|
|
1026
|
+
```typescript
|
|
1027
|
+
throw new NotFoundException("User not found"); // 404
|
|
1028
|
+
throw new BadRequestException("Invalid input"); // 400
|
|
1029
|
+
throw new UnauthorizedException("Not authenticated"); // 401
|
|
1030
|
+
throw new ForbiddenException("Access denied"); // 403
|
|
1031
|
+
throw new InternalServerErrorException("Error"); // 500
|
|
1032
|
+
```
|
|
509
1033
|
|
|
510
1034
|
---
|
|
511
1035
|
|
|
512
|
-
##
|
|
1036
|
+
## �️ CLI Tool
|
|
513
1037
|
|
|
514
|
-
|
|
1038
|
+
### create-wynkjs
|
|
515
1039
|
|
|
516
|
-
|
|
517
|
-
| ---------- | ------------ | ------------- |
|
|
518
|
-
| **WynkJS** | **~250,000** | **~0.4ms** |
|
|
519
|
-
| Elysia | ~250,000 | ~0.4ms |
|
|
520
|
-
| Fastify | ~45,000 | ~2.2ms |
|
|
521
|
-
| Express | ~12,000 | ~8.3ms |
|
|
1040
|
+
Quickly scaffold a new WynkJS project with best practices:
|
|
522
1041
|
|
|
523
|
-
|
|
1042
|
+
```bash
|
|
1043
|
+
bunx create-wynkjs
|
|
1044
|
+
# or
|
|
1045
|
+
npx create-wynkjs
|
|
1046
|
+
```
|
|
524
1047
|
|
|
525
|
-
|
|
1048
|
+
**Features:**
|
|
526
1049
|
|
|
527
|
-
|
|
1050
|
+
- 🎯 Interactive project setup
|
|
1051
|
+
- ✅ TypeScript configuration (strict mode)
|
|
1052
|
+
- 🔍 ESLint with TypeScript rules
|
|
1053
|
+
- 💅 Prettier for code formatting
|
|
1054
|
+
- 🪝 Husky for Git hooks (optional)
|
|
1055
|
+
- 🔥 Hot reload with `bun --watch` (faster than nodemon)
|
|
1056
|
+
- 📝 Complete working example (CRUD API)
|
|
528
1057
|
|
|
529
|
-
|
|
1058
|
+
**Generated Project Structure:**
|
|
530
1059
|
|
|
531
|
-
|
|
1060
|
+
```
|
|
1061
|
+
my-wynkjs-app/
|
|
1062
|
+
├── src/
|
|
1063
|
+
│ ├── modules/
|
|
1064
|
+
│ │ └── user/
|
|
1065
|
+
│ │ ├── user.controller.ts
|
|
1066
|
+
│ │ ├── user.service.ts
|
|
1067
|
+
│ │ └── user.dto.ts
|
|
1068
|
+
│ └── index.ts
|
|
1069
|
+
├── .eslintrc.json
|
|
1070
|
+
├── .prettierrc
|
|
1071
|
+
├── tsconfig.json
|
|
1072
|
+
└── package.json
|
|
1073
|
+
```
|
|
532
1074
|
|
|
533
|
-
|
|
1075
|
+
**Available Scripts:**
|
|
534
1076
|
|
|
535
|
-
|
|
1077
|
+
- `bun run dev` - Development with hot reload
|
|
1078
|
+
- `bun run start` - Production server
|
|
1079
|
+
- `bun run build` - Build TypeScript
|
|
1080
|
+
- `bun run lint` - Run ESLint
|
|
1081
|
+
- `bun run format` - Format with Prettier
|
|
1082
|
+
|
|
1083
|
+
[Learn more about create-wynkjs](./packages/create-wynkjs/README.md)
|
|
536
1084
|
|
|
537
1085
|
---
|
|
538
1086
|
|
|
539
|
-
##
|
|
1087
|
+
## �🔗 Resources
|
|
540
1088
|
|
|
541
|
-
- [
|
|
542
|
-
- [
|
|
543
|
-
- [
|
|
544
|
-
- [
|
|
1089
|
+
- 📚 [Full Documentation](https://github.com/wynkjs/wynkjs-core)
|
|
1090
|
+
- 🚀 [CLI Tool (create-wynkjs)](./packages/create-wynkjs/README.md)
|
|
1091
|
+
- 🎨 [Validation Formatters](./docs-wynkjs/VALIDATION_FORMATTERS.md)
|
|
1092
|
+
- 📝 [Changelog](./CHANGELOG.md)
|
|
1093
|
+
- 🐛 [Report Issues](https://github.com/wynkjs/wynkjs-core/issues)
|
|
545
1094
|
|
|
546
1095
|
---
|
|
547
1096
|
|
|
548
|
-
##
|
|
1097
|
+
## 🤝 Contributing
|
|
1098
|
+
|
|
1099
|
+
We welcome contributions from the community! Whether you're fixing bugs, improving documentation, or proposing new features, your help is appreciated.
|
|
549
1100
|
|
|
550
|
-
|
|
1101
|
+
### 🐛 Reporting Issues
|
|
551
1102
|
|
|
552
|
-
|
|
553
|
-
- [tsyringe](https://github.com/microsoft/tsyringe) - Dependency injection
|
|
554
|
-
- [TypeScript](https://www.typescriptlang.org/) - Type safety
|
|
1103
|
+
If you find a bug or have a feature request:
|
|
555
1104
|
|
|
556
|
-
|
|
1105
|
+
1. **Check existing issues** to avoid duplicates
|
|
1106
|
+
2. **Create a new issue** with a clear title and description
|
|
1107
|
+
3. **Provide details**: Steps to reproduce, expected behavior, actual behavior
|
|
1108
|
+
4. **Include environment info**: Bun version, OS, WynkJS version
|
|
557
1109
|
|
|
558
|
-
|
|
1110
|
+
[Report an issue →](https://github.com/wynkjs/wynkjs-core/issues)
|
|
559
1111
|
|
|
560
|
-
|
|
1112
|
+
### 💡 Contributing Code
|
|
561
1113
|
|
|
562
|
-
|
|
1114
|
+
#### Getting Started
|
|
563
1115
|
|
|
564
|
-
|
|
1116
|
+
1. **Fork the repository**
|
|
1117
|
+
|
|
1118
|
+
```bash
|
|
1119
|
+
# Fork on GitHub, then clone your fork
|
|
1120
|
+
git clone https://github.com/YOUR_USERNAME/wynkjs-core.git
|
|
1121
|
+
cd wynkjs-core
|
|
1122
|
+
```
|
|
1123
|
+
|
|
1124
|
+
2. **Install dependencies**
|
|
1125
|
+
|
|
1126
|
+
```bash
|
|
1127
|
+
bun install
|
|
1128
|
+
```
|
|
1129
|
+
|
|
1130
|
+
3. **Build the packages**
|
|
1131
|
+
|
|
1132
|
+
```bash
|
|
1133
|
+
# Build main framework
|
|
1134
|
+
bun run build
|
|
1135
|
+
|
|
1136
|
+
# Build CLI tools
|
|
1137
|
+
cd packages/create-wynkjs && bun run build
|
|
1138
|
+
cd ../wynkjs-cli && bun run build
|
|
1139
|
+
```
|
|
1140
|
+
|
|
1141
|
+
4. **Create a branch**
|
|
1142
|
+
```bash
|
|
1143
|
+
git checkout -b feature/your-feature-name
|
|
1144
|
+
# or
|
|
1145
|
+
git checkout -b fix/bug-description
|
|
1146
|
+
```
|
|
1147
|
+
|
|
1148
|
+
#### Development Workflow
|
|
1149
|
+
|
|
1150
|
+
1. **Make your changes** in the appropriate package:
|
|
1151
|
+
|
|
1152
|
+
- `core/` - Core framework decorators and utilities
|
|
1153
|
+
- `packages/create-wynkjs/` - Project scaffolding CLI
|
|
1154
|
+
- `packages/wynkjs-cli/` - Code generator CLI
|
|
1155
|
+
|
|
1156
|
+
2. **Test your changes**
|
|
1157
|
+
|
|
1158
|
+
```bash
|
|
1159
|
+
# Test in the example project
|
|
1160
|
+
cd example
|
|
1161
|
+
bun run dev
|
|
1162
|
+
|
|
1163
|
+
# Test CLI generation
|
|
1164
|
+
cd /tmp && bunx /path/to/wynkjs-core/packages/create-wynkjs
|
|
1165
|
+
```
|
|
1166
|
+
|
|
1167
|
+
3. **Build all packages**
|
|
1168
|
+
|
|
1169
|
+
```bash
|
|
1170
|
+
# From project root
|
|
1171
|
+
bun run build
|
|
1172
|
+
cd packages/create-wynkjs && bun run build
|
|
1173
|
+
cd ../wynkjs-cli && bun run build
|
|
1174
|
+
```
|
|
1175
|
+
|
|
1176
|
+
4. **Commit your changes**
|
|
1177
|
+
|
|
1178
|
+
```bash
|
|
1179
|
+
git add .
|
|
1180
|
+
git commit -m "feat: add new feature"
|
|
1181
|
+
# or
|
|
1182
|
+
git commit -m "fix: resolve issue with decorators"
|
|
1183
|
+
```
|
|
1184
|
+
|
|
1185
|
+
**Commit Convention:**
|
|
1186
|
+
|
|
1187
|
+
- `feat:` - New feature
|
|
1188
|
+
- `fix:` - Bug fix
|
|
1189
|
+
- `docs:` - Documentation changes
|
|
1190
|
+
- `refactor:` - Code refactoring
|
|
1191
|
+
- `test:` - Adding tests
|
|
1192
|
+
- `chore:` - Maintenance tasks
|
|
1193
|
+
|
|
1194
|
+
5. **Push and create a Pull Request**
|
|
1195
|
+
|
|
1196
|
+
```bash
|
|
1197
|
+
git push origin feature/your-feature-name
|
|
1198
|
+
```
|
|
1199
|
+
|
|
1200
|
+
Then open a Pull Request on GitHub with:
|
|
1201
|
+
|
|
1202
|
+
- Clear description of changes
|
|
1203
|
+
- Link to related issues
|
|
1204
|
+
- Screenshots/examples if applicable
|
|
1205
|
+
|
|
1206
|
+
#### Code Style
|
|
1207
|
+
|
|
1208
|
+
- **TypeScript**: Strict mode enabled
|
|
1209
|
+
- **Formatting**: Use Prettier (run `bun run format` if available)
|
|
1210
|
+
- **Linting**: Follow ESLint rules
|
|
1211
|
+
- **Naming**:
|
|
1212
|
+
- PascalCase for classes and interfaces
|
|
1213
|
+
- camelCase for functions and variables
|
|
1214
|
+
- kebab-case for file names
|
|
1215
|
+
|
|
1216
|
+
#### Testing Guidelines
|
|
1217
|
+
|
|
1218
|
+
- Test your changes in the `example/` directory
|
|
1219
|
+
- Ensure existing examples still work
|
|
1220
|
+
- Add new examples for new features
|
|
1221
|
+
- Test CLI tools in a fresh directory
|
|
1222
|
+
|
|
1223
|
+
### 📝 Documentation
|
|
1224
|
+
|
|
1225
|
+
Documentation improvements are always welcome!
|
|
1226
|
+
|
|
1227
|
+
- **README updates**: Keep examples current and clear
|
|
1228
|
+
- **Code comments**: Add JSDoc comments for public APIs
|
|
1229
|
+
- **Guides**: Create helpful guides in `docs-wynkjs/`
|
|
1230
|
+
- **Examples**: Add real-world usage examples
|
|
1231
|
+
|
|
1232
|
+
### 🚀 Release Process (Maintainers)
|
|
1233
|
+
|
|
1234
|
+
1. Update `CHANGELOG.md` with changes
|
|
1235
|
+
2. Bump version in `package.json` files
|
|
1236
|
+
3. Build all packages
|
|
1237
|
+
4. Commit and tag the release
|
|
1238
|
+
5. Publish to npm:
|
|
1239
|
+
```bash
|
|
1240
|
+
npm publish --access public
|
|
1241
|
+
cd packages/create-wynkjs && npm publish --access public
|
|
1242
|
+
cd ../wynkjs-cli && npm publish --access public
|
|
1243
|
+
```
|
|
1244
|
+
|
|
1245
|
+
### 💬 Community
|
|
1246
|
+
|
|
1247
|
+
- **GitHub Discussions**: Ask questions and share ideas
|
|
1248
|
+
- **Discord**: (Coming soon) Join our community chat
|
|
1249
|
+
- **Twitter**: Follow [@wynkjs](https://twitter.com/wynkjs) for updates
|
|
1250
|
+
|
|
1251
|
+
### 📜 License
|
|
1252
|
+
|
|
1253
|
+
By contributing, you agree that your contributions will be licensed under the MIT License.
|
|
1254
|
+
|
|
1255
|
+
---
|
|
1256
|
+
|
|
1257
|
+
**Thank you for contributing to WynkJS! 🎉**
|
|
1258
|
+
|
|
1259
|
+
```
|
|
1260
|
+
|
|
1261
|
+
```
|