@riktajs/core 0.2.0 â 0.2.2
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 +89 -269
- package/dist/core/discovery.d.ts +14 -3
- package/dist/core/discovery.d.ts.map +1 -1
- package/dist/core/discovery.js +77 -36
- package/dist/core/discovery.js.map +1 -1
- package/dist/core/guards/can-activate.interface.d.ts +77 -0
- package/dist/core/guards/can-activate.interface.d.ts.map +1 -0
- package/dist/core/guards/can-activate.interface.js +3 -0
- package/dist/core/guards/can-activate.interface.js.map +1 -0
- package/dist/core/guards/execution-context.d.ts +72 -0
- package/dist/core/guards/execution-context.d.ts.map +1 -0
- package/dist/core/guards/execution-context.js +37 -0
- package/dist/core/guards/execution-context.js.map +1 -0
- package/dist/core/guards/index.d.ts +4 -0
- package/dist/core/guards/index.d.ts.map +1 -0
- package/dist/core/guards/index.js +10 -0
- package/dist/core/guards/index.js.map +1 -0
- package/dist/core/guards/use-guards.decorator.d.ts +82 -0
- package/dist/core/guards/use-guards.decorator.d.ts.map +1 -0
- package/dist/core/guards/use-guards.decorator.js +104 -0
- package/dist/core/guards/use-guards.decorator.js.map +1 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/router/router.d.ts +8 -0
- package/dist/core/router/router.d.ts.map +1 -1
- package/dist/core/router/router.js +44 -0
- package/dist/core/router/router.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,324 +1,144 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ð§ Rikta
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> **The Zero-Config TypeScript Framework for Modern Backends.**
|
|
4
|
+
|
|
5
|
+
Build scalable APIs with the power of Fastify and the elegance of decorators. No modules. No boilerplate. Just code.
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
[](https://www.npmjs.com/package/@riktajs/core)
|
|
8
|
+
[](LICENSE)
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
- ð **Full Autowiring** - No modules needed, everything is auto-discovered
|
|
9
|
-
- ð **Single DI decorator** - `@Autowired()` for everything
|
|
10
|
-
- ð **Hybrid Lifecycle** - Interface hooks + EventBus
|
|
11
|
-
- ðĄïļ **Exception Handling** - Built-in filters with standardized JSON responses
|
|
12
|
-
- â
**Zod Validation** - Native type-safe validation with automatic type inference
|
|
13
|
-
- ⥠**Fastify under the hood** - Maximum performance
|
|
14
|
-
- ð **Type-safe** - Full TypeScript support
|
|
15
|
-
- ðŠķ **Zero config** - Just decorate and run
|
|
10
|
+
---
|
|
16
11
|
|
|
17
|
-
##
|
|
12
|
+
## ðĪ Why Rikta?
|
|
18
13
|
|
|
19
|
-
|
|
20
|
-
npm install
|
|
21
|
-
```
|
|
14
|
+
Are you tired of **"Module Hell"** in NestJS? Do you miss the simplicity of Express but need the structure of a real framework?
|
|
22
15
|
|
|
23
|
-
|
|
16
|
+
**Rikta** is designed for developers who want to move fast without breaking things.
|
|
24
17
|
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
* ð **Zero-Config Autowiring:** No `imports: []`, `exports: []`, or `providers: []` arrays. Just decorate your class, and it works.
|
|
19
|
+
* ⥠**Fastify Powered:** Built on top of Fastify for maximum performance and low overhead.
|
|
20
|
+
* ðĄïļ **Type-Safe by Default:** Native Zod integration for validation that infers your TypeScript types automatically.
|
|
21
|
+
* ð **Hybrid Lifecycle:** Powerful hooks and an event bus for complex application flows.
|
|
27
22
|
|
|
28
|
-
|
|
29
|
-
class GreetingService {
|
|
30
|
-
getGreeting(): string {
|
|
31
|
-
return 'Hello from Rikta!';
|
|
32
|
-
}
|
|
33
|
-
}
|
|
23
|
+
Rikta is nordic for "guide". Let Rikta guide you to build better backends, faster.
|
|
34
24
|
|
|
35
|
-
|
|
36
|
-
export class AppController {
|
|
37
|
-
@Autowired()
|
|
38
|
-
private greetingService!: GreetingService;
|
|
25
|
+
---
|
|
39
26
|
|
|
40
|
-
|
|
41
|
-
getHello() {
|
|
42
|
-
return this.greetingService.getGreeting();
|
|
43
|
-
}
|
|
44
|
-
}
|
|
27
|
+
## ⥠Quick Start
|
|
45
28
|
|
|
46
|
-
|
|
47
|
-
const app = await Rikta.create({ port: 3000, autowired: [__dirname] });
|
|
48
|
-
await app.listen();
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## ð Documentation
|
|
52
|
-
|
|
53
|
-
| Section | Description |
|
|
54
|
-
|---------|-------------|
|
|
55
|
-
| [Core Architecture](./src/core/README.md) | Framework internals |
|
|
56
|
-
| [Dependency Injection](./src/core/container/README.md) | DI, tokens, autowiring |
|
|
57
|
-
| [Lifecycle](./src/core/lifecycle/README.md) | Hooks, events, priority |
|
|
58
|
-
| [Exception Handling](./src/core/exceptions/README.md) | Errors, filters, responses |
|
|
59
|
-
| [Decorators](./src/core/decorators/README.md) | All decorators reference |
|
|
60
|
-
| [Router](./src/core/router/README.md) | Routing and requests |
|
|
61
|
-
| [Example](./example/README.md) | Working code example |
|
|
62
|
-
|
|
63
|
-
## ð Running Example
|
|
29
|
+
### 1. Install
|
|
64
30
|
|
|
65
31
|
```bash
|
|
66
|
-
npm
|
|
32
|
+
npm install @riktajs/core
|
|
67
33
|
```
|
|
68
34
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
```typescript
|
|
72
|
-
const app = await Rikta.create({
|
|
73
|
-
port: 3000, // Server port
|
|
74
|
-
host: '0.0.0.0', // Server host
|
|
75
|
-
logger: true, // Fastify logging
|
|
76
|
-
prefix: '/api/v1', // Global prefix
|
|
77
|
-
|
|
78
|
-
// Auto-discovery paths (default: ['./**'] - scans all files)
|
|
79
|
-
autowired: [path.resolve('./src/controllers'), path.resolve('./src/services')],
|
|
80
|
-
|
|
81
|
-
// Optional: explicit controller list (skips auto-discovery)
|
|
82
|
-
controllers: [UserController, AppController],
|
|
83
|
-
|
|
84
|
-
// Exception handling configuration
|
|
85
|
-
exceptionFilter: {
|
|
86
|
-
includeStack: process.env.NODE_ENV !== 'production',
|
|
87
|
-
logErrors: true,
|
|
88
|
-
},
|
|
89
|
-
|
|
90
|
-
// Custom exception filters
|
|
91
|
-
exceptionFilters: [ValidationExceptionFilter],
|
|
92
|
-
});
|
|
93
|
-
```
|
|
35
|
+
### 2. Create your App
|
|
94
36
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
Rikta automatically scans and imports files to discover `@Controller` and `@Injectable` classes:
|
|
37
|
+
No complex setup. Just one file is enough to start.
|
|
98
38
|
|
|
99
39
|
```typescript
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
autowired: [path.resolve('./src/controllers')]
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
// Default: scans current directory recursively
|
|
106
|
-
await Rikta.create({ port: 3000, autowired: [path.resolve('.')] });
|
|
107
|
-
```
|
|
40
|
+
// main.ts
|
|
41
|
+
import { Rikta, Controller, Injectable, Get, Post, Body, Autowired, z } from '@riktajs/core';
|
|
108
42
|
|
|
109
|
-
|
|
43
|
+
// 1. Define a Service (Auto-discovered!)
|
|
44
|
+
@Injectable()
|
|
45
|
+
class UserService {
|
|
46
|
+
private users = [{ id: 1, name: 'Rikta User' }];
|
|
110
47
|
|
|
111
|
-
|
|
112
|
-
@Injectable({ priority: 100 }) // Higher = initialized first
|
|
113
|
-
class DatabaseService implements OnProviderInit, OnProviderDestroy {
|
|
114
|
-
async onProviderInit() {
|
|
115
|
-
await this.connect();
|
|
116
|
-
}
|
|
48
|
+
findAll() { return this.users; }
|
|
117
49
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
// Or use @On() decorator for events
|
|
124
|
-
@Injectable()
|
|
125
|
-
class MonitoringService {
|
|
126
|
-
@On('app:listen')
|
|
127
|
-
onServerStart({ address }) {
|
|
128
|
-
console.log(`Server at ${address}`);
|
|
50
|
+
create(name: string) {
|
|
51
|
+
const user = { id: this.users.length + 1, name };
|
|
52
|
+
this.users.push(user);
|
|
53
|
+
return user;
|
|
129
54
|
}
|
|
130
55
|
}
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
See [Lifecycle Documentation](./src/core/lifecycle/README.md) for full details.
|
|
134
56
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
NotFoundException,
|
|
140
|
-
BadRequestException,
|
|
141
|
-
HttpException
|
|
142
|
-
} from '@rikta/core';
|
|
57
|
+
// 2. Define a Schema (Type-safe!)
|
|
58
|
+
const CreateUserSchema = z.object({
|
|
59
|
+
name: z.string().min(3)
|
|
60
|
+
});
|
|
143
61
|
|
|
62
|
+
// 3. Define a Controller (Auto-discovered!)
|
|
144
63
|
@Controller('/users')
|
|
145
|
-
class UserController {
|
|
146
|
-
@
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return user;
|
|
64
|
+
export class UserController {
|
|
65
|
+
@Autowired()
|
|
66
|
+
private userService!: UserService; // Dependency Injection works like magic
|
|
67
|
+
|
|
68
|
+
@Get()
|
|
69
|
+
getUsers() {
|
|
70
|
+
return this.userService.findAll();
|
|
155
71
|
}
|
|
156
72
|
|
|
157
73
|
@Post()
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
message: 'Validation failed',
|
|
162
|
-
details: { email: 'Email is required' }
|
|
163
|
-
});
|
|
164
|
-
}
|
|
74
|
+
createUser(@Body(CreateUserSchema) body: z.infer<typeof CreateUserSchema>) {
|
|
75
|
+
// 'body' is fully typed here!
|
|
76
|
+
return this.userService.create(body.name);
|
|
165
77
|
}
|
|
166
78
|
}
|
|
167
|
-
```
|
|
168
79
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
{
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
80
|
+
// 4. Run it!
|
|
81
|
+
// autowired paths are resolved relative to your project, not node_modules!
|
|
82
|
+
const app = await Rikta.create({
|
|
83
|
+
port: 3000,
|
|
84
|
+
autowired: ['./src'] // Relative paths are resolved from YOUR project directory
|
|
85
|
+
});
|
|
86
|
+
await app.listen();
|
|
87
|
+
console.log('ð Server running on http://localhost:3000');
|
|
178
88
|
```
|
|
179
89
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
See [Exception Handling Documentation](./src/core/exceptions/README.md) for full details.
|
|
183
|
-
|
|
184
|
-
## â
Zod Validation
|
|
90
|
+
---
|
|
185
91
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
### Basic Usage
|
|
92
|
+
## ð Documentation
|
|
189
93
|
|
|
190
|
-
|
|
191
|
-
import { Controller, Post, Body, Query, z } from '@riktajs/core';
|
|
94
|
+
Everything you need to build production-ready APIs.
|
|
192
95
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
96
|
+
| Guide | Description |
|
|
97
|
+
|-------|-------------|
|
|
98
|
+
| [**Architecture**](./docs/guide/architecture.md) | How Rikta's auto-discovery works under the hood. |
|
|
99
|
+
| [**Dependency Injection**](./docs/guide/dependency-injection.md) | Using `@Autowired`, tokens, and scopes. |
|
|
100
|
+
| [**Routing**](./docs/guide/routing.md) | Controllers, methods, and parameter handling. |
|
|
101
|
+
| [**Validation**](./docs/guide/validation.md) | **New!** Type-safe validation with Zod. |
|
|
102
|
+
| [**Lifecycle**](./docs/guide/lifecycle.md) | Hooks (`OnProviderInit`) and the Event Bus. |
|
|
103
|
+
| [**Error Handling**](./docs/guide/error-handling.md) | Exception filters and standard JSON responses. |
|
|
199
104
|
|
|
200
|
-
|
|
201
|
-
type CreateUser = z.infer<typeof CreateUserSchema>;
|
|
105
|
+
---
|
|
202
106
|
|
|
203
|
-
|
|
204
|
-
class UserController {
|
|
205
|
-
@Post()
|
|
206
|
-
create(@Body(CreateUserSchema) data: CreateUser) {
|
|
207
|
-
// data is fully typed as { name: string; email: string; age?: number }
|
|
208
|
-
// Validation happens automatically before this method is called
|
|
209
|
-
return { success: true, user: data };
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
```
|
|
107
|
+
## âĻ Key Features
|
|
213
108
|
|
|
214
|
-
###
|
|
215
|
-
|
|
216
|
-
When validation fails, Rikta automatically returns a structured 400 response:
|
|
217
|
-
|
|
218
|
-
```json
|
|
219
|
-
{
|
|
220
|
-
"statusCode": 400,
|
|
221
|
-
"message": "Validation failed for body",
|
|
222
|
-
"error": "Validation Error",
|
|
223
|
-
"timestamp": "2024-01-15T10:30:00.000Z",
|
|
224
|
-
"path": "/users",
|
|
225
|
-
"details": {
|
|
226
|
-
"errors": [
|
|
227
|
-
{ "path": ["email"], "message": "Invalid email format", "code": "invalid_string" },
|
|
228
|
-
{ "path": ["name"], "message": "Name is required", "code": "too_small" }
|
|
229
|
-
],
|
|
230
|
-
"errorCount": 2
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
```
|
|
109
|
+
### ðŦ No Modules, Just Logic
|
|
110
|
+
Forget about `AppModule`, `UserModule`, `SharedModule`. Rikta scans your code and resolves dependencies automatically.
|
|
234
111
|
|
|
235
|
-
###
|
|
112
|
+
### â
Native Zod Validation
|
|
113
|
+
Don't duplicate your types. Define a Zod schema, and Rikta validates the request *and* gives you the TypeScript type.
|
|
236
114
|
|
|
237
115
|
```typescript
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
//
|
|
241
|
-
|
|
242
|
-
page: z.coerce.number().int().positive().default(1),
|
|
243
|
-
limit: z.coerce.number().int().max(100).default(10),
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
// Route params validation
|
|
247
|
-
const IdSchema = z.object({
|
|
248
|
-
id: z.string().uuid('Invalid UUID format'),
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
// Headers validation
|
|
252
|
-
const AuthSchema = z.object({
|
|
253
|
-
authorization: z.string().startsWith('Bearer '),
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
@Controller('/items')
|
|
257
|
-
class ItemController {
|
|
258
|
-
@Get()
|
|
259
|
-
list(@Query(PaginationSchema) query: z.infer<typeof PaginationSchema>) {
|
|
260
|
-
// query.page and query.limit are numbers (coerced from string)
|
|
261
|
-
return { page: query.page, limit: query.limit };
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
@Get('/:id')
|
|
265
|
-
findOne(
|
|
266
|
-
@Param(IdSchema) params: z.infer<typeof IdSchema>,
|
|
267
|
-
@Headers(AuthSchema) headers: z.infer<typeof AuthSchema>
|
|
268
|
-
) {
|
|
269
|
-
return { id: params.id };
|
|
270
|
-
}
|
|
116
|
+
@Post()
|
|
117
|
+
create(@Body(UserSchema) user: z.infer<typeof UserSchema>) {
|
|
118
|
+
// If we get here, 'user' is valid and typed.
|
|
119
|
+
// If not, Rikta returns a 400 Bad Request automatically.
|
|
271
120
|
}
|
|
272
121
|
```
|
|
273
122
|
|
|
274
|
-
###
|
|
275
|
-
|
|
276
|
-
Zod's transform feature works seamlessly:
|
|
123
|
+
### ð Powerful Dependency Injection
|
|
124
|
+
Support for Singleton (default) and Transient scopes, factory providers, and value tokens.
|
|
277
125
|
|
|
278
126
|
```typescript
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
create(@Body(DateSchema) data: z.infer<typeof DateSchema>) {
|
|
286
|
-
// data.date is a Date object
|
|
287
|
-
// data.tags is string[]
|
|
127
|
+
@Injectable()
|
|
128
|
+
class AuthService {
|
|
129
|
+
constructor(
|
|
130
|
+
@Autowired(DB_CONFIG) private config: Config,
|
|
131
|
+
@Autowired() private logger: LoggerService
|
|
132
|
+
) {}
|
|
288
133
|
}
|
|
289
134
|
```
|
|
290
135
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
Existing code without Zod schemas continues to work:
|
|
136
|
+
---
|
|
294
137
|
|
|
295
|
-
|
|
296
|
-
// These still work as before
|
|
297
|
-
@Get('/:id')
|
|
298
|
-
findOne(@Param('id') id: string) { ... }
|
|
299
|
-
|
|
300
|
-
@Post()
|
|
301
|
-
create(@Body() data: unknown) { ... }
|
|
138
|
+
## ðĪ Contributing
|
|
302
139
|
|
|
303
|
-
|
|
304
|
-
list(@Query('page') page: string) { ... }
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
## ð Project Structure
|
|
308
|
-
|
|
309
|
-
```
|
|
310
|
-
rikta/
|
|
311
|
-
âââ src/core/
|
|
312
|
-
â âââ container/ # Dependency Injection
|
|
313
|
-
â âââ decorators/ # All decorators
|
|
314
|
-
â âââ exceptions/ # Error handling & filters
|
|
315
|
-
â âââ lifecycle/ # Hooks & EventBus
|
|
316
|
-
â âââ router/ # Route handling
|
|
317
|
-
â âââ registry.ts # Auto-discovery registry
|
|
318
|
-
â âââ application.ts # Bootstrap
|
|
319
|
-
âââ example/ # Full-featured example
|
|
320
|
-
```
|
|
140
|
+
We love contributions! Please check our [Contributing Guide](CONTRIBUTING.md) (Coming Soon) and join our community.
|
|
321
141
|
|
|
322
|
-
##
|
|
142
|
+
## ð License
|
|
323
143
|
|
|
324
|
-
MIT
|
|
144
|
+
Rikta is [MIT licensed](LICENSE).
|
package/dist/core/discovery.d.ts
CHANGED
|
@@ -3,15 +3,20 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Only imports files that contain @Controller, @Injectable, or @Provider decorators.
|
|
5
5
|
*
|
|
6
|
-
* @param patterns - Glob patterns to search for files (default: ['./**'])
|
|
7
|
-
* @param cwd - Base directory for pattern matching
|
|
6
|
+
* @param patterns - Glob patterns or directory paths to search for files (default: ['./**'])
|
|
7
|
+
* @param cwd - Base directory for pattern matching. If not provided, it will be
|
|
8
|
+
* automatically resolved from the caller's location (useful when rikta-core
|
|
9
|
+
* is used as an external library from node_modules)
|
|
8
10
|
* @returns Promise resolving to list of imported files
|
|
9
11
|
*
|
|
10
12
|
* @example
|
|
11
13
|
* ```typescript
|
|
12
|
-
* // Scan specific directories
|
|
14
|
+
* // Scan specific directories (relative paths resolved from caller's location)
|
|
13
15
|
* await discoverModules(['./src/controllers', './src/services']);
|
|
14
16
|
*
|
|
17
|
+
* // Scan with absolute path
|
|
18
|
+
* await discoverModules(['/absolute/path/to/src']);
|
|
19
|
+
*
|
|
15
20
|
* // Scan everything (default)
|
|
16
21
|
* await discoverModules();
|
|
17
22
|
* ```
|
|
@@ -19,6 +24,12 @@
|
|
|
19
24
|
export declare function discoverModules(patterns?: string[], cwd?: string): Promise<string[]>;
|
|
20
25
|
/**
|
|
21
26
|
* Get the caller's directory (useful for relative pattern resolution)
|
|
27
|
+
*
|
|
28
|
+
* This function walks the stack trace to find the first file that is NOT
|
|
29
|
+
* part of rikta-core, making it work correctly when rikta-core is installed
|
|
30
|
+
* as an external library in node_modules.
|
|
31
|
+
*
|
|
32
|
+
* @returns The directory of the calling file, or process.cwd() if it cannot be determined
|
|
22
33
|
*/
|
|
23
34
|
export declare function getCallerDirectory(): string;
|
|
24
35
|
//# sourceMappingURL=discovery.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/core/discovery.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/core/discovery.ts"],"names":[],"mappings":"AAgFA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,eAAe,CACnC,QAAQ,GAAE,MAAM,EAAuB,EACvC,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,EAAE,CAAC,CAqEnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C"}
|
package/dist/core/discovery.js
CHANGED
|
@@ -52,6 +52,44 @@ const DEFAULT_IGNORE_PATTERNS = [
|
|
|
52
52
|
'**/*.spec.ts',
|
|
53
53
|
'**/*.d.ts',
|
|
54
54
|
];
|
|
55
|
+
/**
|
|
56
|
+
* Get the caller's directory from the stack trace
|
|
57
|
+
* This is used to resolve relative paths from the caller's context,
|
|
58
|
+
* not from where rikta-core is installed (e.g., node_modules)
|
|
59
|
+
*/
|
|
60
|
+
function getCallerDirectoryFromStack() {
|
|
61
|
+
const originalPrepareStackTrace = Error.prepareStackTrace;
|
|
62
|
+
try {
|
|
63
|
+
const err = new Error();
|
|
64
|
+
let callerFile;
|
|
65
|
+
Error.prepareStackTrace = (_, stack) => stack;
|
|
66
|
+
const stack = err.stack;
|
|
67
|
+
// Find the first file that's not part of rikta-core
|
|
68
|
+
for (const site of stack) {
|
|
69
|
+
const filename = site.getFileName();
|
|
70
|
+
if (filename &&
|
|
71
|
+
!filename.includes('discovery.ts') &&
|
|
72
|
+
!filename.includes('discovery.js') &&
|
|
73
|
+
!filename.includes('application.ts') &&
|
|
74
|
+
!filename.includes('application.js') &&
|
|
75
|
+
!filename.includes('rikta-core')) {
|
|
76
|
+
callerFile = filename;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (callerFile) {
|
|
81
|
+
// Handle file:// URLs (ESM) and regular paths
|
|
82
|
+
const filePath = callerFile.startsWith('file://')
|
|
83
|
+
? new URL(callerFile).pathname
|
|
84
|
+
: callerFile;
|
|
85
|
+
return path_1.default.dirname(filePath);
|
|
86
|
+
}
|
|
87
|
+
return process.cwd();
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
Error.prepareStackTrace = originalPrepareStackTrace;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
55
93
|
/**
|
|
56
94
|
* Regex patterns to detect Rikta decorators
|
|
57
95
|
*/
|
|
@@ -77,34 +115,56 @@ async function containsRiktaDecorators(filePath) {
|
|
|
77
115
|
*
|
|
78
116
|
* Only imports files that contain @Controller, @Injectable, or @Provider decorators.
|
|
79
117
|
*
|
|
80
|
-
* @param patterns - Glob patterns to search for files (default: ['./**'])
|
|
81
|
-
* @param cwd - Base directory for pattern matching
|
|
118
|
+
* @param patterns - Glob patterns or directory paths to search for files (default: ['./**'])
|
|
119
|
+
* @param cwd - Base directory for pattern matching. If not provided, it will be
|
|
120
|
+
* automatically resolved from the caller's location (useful when rikta-core
|
|
121
|
+
* is used as an external library from node_modules)
|
|
82
122
|
* @returns Promise resolving to list of imported files
|
|
83
123
|
*
|
|
84
124
|
* @example
|
|
85
125
|
* ```typescript
|
|
86
|
-
* // Scan specific directories
|
|
126
|
+
* // Scan specific directories (relative paths resolved from caller's location)
|
|
87
127
|
* await discoverModules(['./src/controllers', './src/services']);
|
|
88
128
|
*
|
|
129
|
+
* // Scan with absolute path
|
|
130
|
+
* await discoverModules(['/absolute/path/to/src']);
|
|
131
|
+
*
|
|
89
132
|
* // Scan everything (default)
|
|
90
133
|
* await discoverModules();
|
|
91
134
|
* ```
|
|
92
135
|
*/
|
|
93
|
-
async function discoverModules(patterns = ['./**/*.{ts,js}'], cwd
|
|
136
|
+
async function discoverModules(patterns = ['./**/*.{ts,js}'], cwd) {
|
|
137
|
+
// If no cwd provided, resolve from caller's directory
|
|
138
|
+
// This is crucial when rikta-core is installed in node_modules
|
|
139
|
+
const baseDir = cwd ?? getCallerDirectoryFromStack();
|
|
140
|
+
// Resolve the base directory to absolute if needed
|
|
141
|
+
const absoluteBaseDir = path_1.default.isAbsolute(baseDir)
|
|
142
|
+
? baseDir
|
|
143
|
+
: path_1.default.resolve(process.cwd(), baseDir);
|
|
94
144
|
// Normalize patterns to include file extensions if not present
|
|
95
145
|
const normalizedPatterns = patterns.map(pattern => {
|
|
146
|
+
// Resolve the pattern if it's an absolute path
|
|
147
|
+
// For absolute paths, we need to make them relative to cwd for fast-glob
|
|
148
|
+
let normalizedPattern = pattern;
|
|
149
|
+
if (path_1.default.isAbsolute(pattern)) {
|
|
150
|
+
// Convert absolute path to relative from baseDir
|
|
151
|
+
normalizedPattern = path_1.default.relative(absoluteBaseDir, pattern);
|
|
152
|
+
if (!normalizedPattern.startsWith('.')) {
|
|
153
|
+
normalizedPattern = './' + normalizedPattern;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
96
156
|
// If pattern already has extension, use as-is
|
|
97
|
-
if (/\.\w+$/.test(
|
|
98
|
-
return
|
|
157
|
+
if (/\.\w+$/.test(normalizedPattern) || normalizedPattern.endsWith('*')) {
|
|
158
|
+
return normalizedPattern;
|
|
99
159
|
}
|
|
100
160
|
// Add file extension pattern
|
|
101
|
-
return
|
|
102
|
-
? `${
|
|
103
|
-
: `${
|
|
161
|
+
return normalizedPattern.endsWith('/')
|
|
162
|
+
? `${normalizedPattern}**/*.{ts,js}`
|
|
163
|
+
: `${normalizedPattern}/**/*.{ts,js}`;
|
|
104
164
|
});
|
|
105
165
|
// Find all matching files
|
|
106
166
|
const files = await (0, fast_glob_1.default)(normalizedPatterns, {
|
|
107
|
-
cwd,
|
|
167
|
+
cwd: absoluteBaseDir,
|
|
108
168
|
absolute: true,
|
|
109
169
|
ignore: DEFAULT_IGNORE_PATTERNS,
|
|
110
170
|
onlyFiles: true,
|
|
@@ -136,33 +196,14 @@ async function discoverModules(patterns = ['./**/*.{ts,js}'], cwd = process.cwd(
|
|
|
136
196
|
}
|
|
137
197
|
/**
|
|
138
198
|
* Get the caller's directory (useful for relative pattern resolution)
|
|
199
|
+
*
|
|
200
|
+
* This function walks the stack trace to find the first file that is NOT
|
|
201
|
+
* part of rikta-core, making it work correctly when rikta-core is installed
|
|
202
|
+
* as an external library in node_modules.
|
|
203
|
+
*
|
|
204
|
+
* @returns The directory of the calling file, or process.cwd() if it cannot be determined
|
|
139
205
|
*/
|
|
140
206
|
function getCallerDirectory() {
|
|
141
|
-
|
|
142
|
-
try {
|
|
143
|
-
const err = new Error();
|
|
144
|
-
let callerFile;
|
|
145
|
-
Error.prepareStackTrace = (_, stack) => stack;
|
|
146
|
-
const stack = err.stack;
|
|
147
|
-
// Find the first file that's not this module
|
|
148
|
-
for (const site of stack) {
|
|
149
|
-
const filename = site.getFileName();
|
|
150
|
-
if (filename && !filename.includes('discovery.ts') && !filename.includes('application.ts')) {
|
|
151
|
-
callerFile = filename;
|
|
152
|
-
break;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
if (callerFile) {
|
|
156
|
-
// Handle file:// URLs (ESM) and regular paths
|
|
157
|
-
const filePath = callerFile.startsWith('file://')
|
|
158
|
-
? new URL(callerFile).pathname
|
|
159
|
-
: callerFile;
|
|
160
|
-
return path_1.default.dirname(filePath);
|
|
161
|
-
}
|
|
162
|
-
return process.cwd();
|
|
163
|
-
}
|
|
164
|
-
finally {
|
|
165
|
-
Error.prepareStackTrace = originalPrepareStackTrace;
|
|
166
|
-
}
|
|
207
|
+
return getCallerDirectoryFromStack();
|
|
167
208
|
}
|
|
168
209
|
//# sourceMappingURL=discovery.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../src/core/discovery.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../src/core/discovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { ExecutionContext } from './execution-context';
|
|
2
|
+
/**
|
|
3
|
+
* CanActivate Interface
|
|
4
|
+
*
|
|
5
|
+
* Guards implementing this interface determine whether a given
|
|
6
|
+
* request is allowed to be handled by the route handler.
|
|
7
|
+
*
|
|
8
|
+
* Guards are executed BEFORE the route handler. If any guard
|
|
9
|
+
* returns `false`, the request is rejected with a 403 Forbidden.
|
|
10
|
+
*
|
|
11
|
+
* @example Basic authentication guard
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { Injectable } from '@riktajs/core';
|
|
14
|
+
* import type { CanActivate, ExecutionContext } from '@riktajs/core';
|
|
15
|
+
*
|
|
16
|
+
* @Injectable()
|
|
17
|
+
* export class AuthGuard implements CanActivate {
|
|
18
|
+
* canActivate(context: ExecutionContext): boolean {
|
|
19
|
+
* const request = context.getRequest();
|
|
20
|
+
* return !!request.headers.authorization;
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @example Async guard with service injection
|
|
26
|
+
* ```typescript
|
|
27
|
+
* @Injectable()
|
|
28
|
+
* export class JwtGuard implements CanActivate {
|
|
29
|
+
* constructor(private authService: AuthService) {}
|
|
30
|
+
*
|
|
31
|
+
* async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
32
|
+
* const request = context.getRequest();
|
|
33
|
+
* const token = request.headers.authorization?.replace('Bearer ', '');
|
|
34
|
+
*
|
|
35
|
+
* if (!token) return false;
|
|
36
|
+
*
|
|
37
|
+
* try {
|
|
38
|
+
* const user = await this.authService.verifyToken(token);
|
|
39
|
+
* request.user = user;
|
|
40
|
+
* return true;
|
|
41
|
+
* } catch {
|
|
42
|
+
* return false;
|
|
43
|
+
* }
|
|
44
|
+
* }
|
|
45
|
+
* }
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* @example Role-based guard
|
|
49
|
+
* ```typescript
|
|
50
|
+
* @Injectable()
|
|
51
|
+
* export class RolesGuard implements CanActivate {
|
|
52
|
+
* canActivate(context: ExecutionContext): boolean {
|
|
53
|
+
* const request = context.getRequest();
|
|
54
|
+
* const user = request.user;
|
|
55
|
+
* const requiredRoles = context.getMetadata<string[]>('roles');
|
|
56
|
+
*
|
|
57
|
+
* if (!requiredRoles || requiredRoles.length === 0) {
|
|
58
|
+
* return true;
|
|
59
|
+
* }
|
|
60
|
+
*
|
|
61
|
+
* return requiredRoles.some(role => user?.roles?.includes(role));
|
|
62
|
+
* }
|
|
63
|
+
* }
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export interface CanActivate {
|
|
67
|
+
/**
|
|
68
|
+
* Determines whether the request can proceed to the handler.
|
|
69
|
+
*
|
|
70
|
+
* @param context - The execution context containing request/reply
|
|
71
|
+
* @returns `true` to allow access, `false` to deny (throws 403)
|
|
72
|
+
*
|
|
73
|
+
* Can be synchronous or return a Promise for async operations.
|
|
74
|
+
*/
|
|
75
|
+
canActivate(context: ExecutionContext): boolean | Promise<boolean>;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=can-activate.interface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"can-activate.interface.d.ts","sourceRoot":"","sources":["../../../src/core/guards/can-activate.interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;;;;OAOG;IACH,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACpE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"can-activate.interface.js","sourceRoot":"","sources":["../../../src/core/guards/can-activate.interface.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { FastifyRequest, FastifyReply } from 'fastify';
|
|
2
|
+
import type { Constructor } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* ExecutionContext Interface
|
|
5
|
+
*
|
|
6
|
+
* Provides access to the current request context, including
|
|
7
|
+
* request/reply objects and handler metadata. Used by guards
|
|
8
|
+
* to make authorization decisions.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* @Injectable()
|
|
13
|
+
* class AuthGuard implements CanActivate {
|
|
14
|
+
* async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
15
|
+
* const request = context.getRequest();
|
|
16
|
+
* const authHeader = request.headers.authorization;
|
|
17
|
+
* return !!authHeader;
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export interface ExecutionContext {
|
|
23
|
+
/**
|
|
24
|
+
* Get the Fastify request object
|
|
25
|
+
*
|
|
26
|
+
* @returns The current request instance
|
|
27
|
+
*/
|
|
28
|
+
getRequest<T = FastifyRequest>(): T;
|
|
29
|
+
/**
|
|
30
|
+
* Get the Fastify reply object
|
|
31
|
+
*
|
|
32
|
+
* @returns The current reply instance
|
|
33
|
+
*/
|
|
34
|
+
getReply<T = FastifyReply>(): T;
|
|
35
|
+
/**
|
|
36
|
+
* Get the controller class that handles the route
|
|
37
|
+
*
|
|
38
|
+
* @returns The controller constructor
|
|
39
|
+
*/
|
|
40
|
+
getClass(): Constructor;
|
|
41
|
+
/**
|
|
42
|
+
* Get the handler method name
|
|
43
|
+
*
|
|
44
|
+
* @returns The method name being invoked
|
|
45
|
+
*/
|
|
46
|
+
getHandler(): string | symbol;
|
|
47
|
+
/**
|
|
48
|
+
* Get handler metadata by key
|
|
49
|
+
*
|
|
50
|
+
* @param key - Metadata key to retrieve
|
|
51
|
+
* @returns The metadata value if found
|
|
52
|
+
*/
|
|
53
|
+
getMetadata<T = unknown>(key: string | symbol): T | undefined;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Default ExecutionContext implementation
|
|
57
|
+
*
|
|
58
|
+
* @internal
|
|
59
|
+
*/
|
|
60
|
+
export declare class ExecutionContextImpl implements ExecutionContext {
|
|
61
|
+
private readonly request;
|
|
62
|
+
private readonly reply;
|
|
63
|
+
private readonly controllerClass;
|
|
64
|
+
private readonly handlerName;
|
|
65
|
+
constructor(request: FastifyRequest, reply: FastifyReply, controllerClass: Constructor, handlerName: string | symbol);
|
|
66
|
+
getRequest<T = FastifyRequest>(): T;
|
|
67
|
+
getReply<T = FastifyReply>(): T;
|
|
68
|
+
getClass(): Constructor;
|
|
69
|
+
getHandler(): string | symbol;
|
|
70
|
+
getMetadata<T = unknown>(key: string | symbol): T | undefined;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=execution-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execution-context.d.ts","sourceRoot":"","sources":["../../../src/core/guards/execution-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,UAAU,CAAC,CAAC,GAAG,cAAc,KAAK,CAAC,CAAC;IAEpC;;;;OAIG;IACH,QAAQ,CAAC,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC;IAEhC;;;;OAIG;IACH,QAAQ,IAAI,WAAW,CAAC;IAExB;;;;OAIG;IACH,UAAU,IAAI,MAAM,GAAG,MAAM,CAAC;IAE9B;;;;;OAKG;IACH,WAAW,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;CAC/D;AAED;;;;GAIG;AACH,qBAAa,oBAAqB,YAAW,gBAAgB;IAEzD,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,WAAW;gBAHX,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,YAAY,EACnB,eAAe,EAAE,WAAW,EAC5B,WAAW,EAAE,MAAM,GAAG,MAAM;IAG/C,UAAU,CAAC,CAAC,GAAG,cAAc,KAAK,CAAC;IAInC,QAAQ,CAAC,CAAC,GAAG,YAAY,KAAK,CAAC;IAI/B,QAAQ,IAAI,WAAW;IAIvB,UAAU,IAAI,MAAM,GAAG,MAAM;IAI7B,WAAW,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,CAAC,GAAG,SAAS;CAG9D"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ExecutionContextImpl = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Default ExecutionContext implementation
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
class ExecutionContextImpl {
|
|
10
|
+
request;
|
|
11
|
+
reply;
|
|
12
|
+
controllerClass;
|
|
13
|
+
handlerName;
|
|
14
|
+
constructor(request, reply, controllerClass, handlerName) {
|
|
15
|
+
this.request = request;
|
|
16
|
+
this.reply = reply;
|
|
17
|
+
this.controllerClass = controllerClass;
|
|
18
|
+
this.handlerName = handlerName;
|
|
19
|
+
}
|
|
20
|
+
getRequest() {
|
|
21
|
+
return this.request;
|
|
22
|
+
}
|
|
23
|
+
getReply() {
|
|
24
|
+
return this.reply;
|
|
25
|
+
}
|
|
26
|
+
getClass() {
|
|
27
|
+
return this.controllerClass;
|
|
28
|
+
}
|
|
29
|
+
getHandler() {
|
|
30
|
+
return this.handlerName;
|
|
31
|
+
}
|
|
32
|
+
getMetadata(key) {
|
|
33
|
+
return Reflect.getMetadata(key, this.controllerClass, this.handlerName);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.ExecutionContextImpl = ExecutionContextImpl;
|
|
37
|
+
//# sourceMappingURL=execution-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execution-context.js","sourceRoot":"","sources":["../../../src/core/guards/execution-context.ts"],"names":[],"mappings":";;;AA4DA;;;;GAIG;AACH,MAAa,oBAAoB;IAEZ;IACA;IACA;IACA;IAJnB,YACmB,OAAuB,EACvB,KAAmB,EACnB,eAA4B,EAC5B,WAA4B;QAH5B,YAAO,GAAP,OAAO,CAAgB;QACvB,UAAK,GAAL,KAAK,CAAc;QACnB,oBAAe,GAAf,eAAe,CAAa;QAC5B,gBAAW,GAAX,WAAW,CAAiB;IAC5C,CAAC;IAEJ,UAAU;QACR,OAAO,IAAI,CAAC,OAAY,CAAC;IAC3B,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,KAAU,CAAC;IACzB,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,eAAe,CAAC;IAC9B,CAAC;IAED,UAAU;QACR,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED,WAAW,CAAc,GAAoB;QAC3C,OAAO,OAAO,CAAC,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,IAAI,CAAC,WAAqB,CAAkB,CAAC;IACrG,CAAC;CACF;AA3BD,oDA2BC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/guards/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAG7E,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getGuardsMetadata = exports.UseGuards = exports.ExecutionContextImpl = void 0;
|
|
4
|
+
var execution_context_1 = require("./execution-context");
|
|
5
|
+
Object.defineProperty(exports, "ExecutionContextImpl", { enumerable: true, get: function () { return execution_context_1.ExecutionContextImpl; } });
|
|
6
|
+
// UseGuards decorator
|
|
7
|
+
var use_guards_decorator_1 = require("./use-guards.decorator");
|
|
8
|
+
Object.defineProperty(exports, "UseGuards", { enumerable: true, get: function () { return use_guards_decorator_1.UseGuards; } });
|
|
9
|
+
Object.defineProperty(exports, "getGuardsMetadata", { enumerable: true, get: function () { return use_guards_decorator_1.getGuardsMetadata; } });
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/guards/index.ts"],"names":[],"mappings":";;;AAEA,yDAA6E;AAAlD,yHAAA,oBAAoB,OAAA;AAE/C,sBAAsB;AACtB,+DAAsE;AAA7D,iHAAA,SAAS,OAAA;AAAE,yHAAA,iBAAiB,OAAA"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import type { Constructor } from '../types';
|
|
3
|
+
import type { CanActivate } from './can-activate.interface';
|
|
4
|
+
/**
|
|
5
|
+
* Type for guard constructors
|
|
6
|
+
*/
|
|
7
|
+
export type GuardClass = Constructor<CanActivate>;
|
|
8
|
+
/**
|
|
9
|
+
* @UseGuards() Decorator
|
|
10
|
+
*
|
|
11
|
+
* Applies one or more guards to a controller or route handler.
|
|
12
|
+
* Guards are executed in order before the handler runs.
|
|
13
|
+
*
|
|
14
|
+
* Guards are resolved from the DI container, so they can have
|
|
15
|
+
* injected dependencies. Make sure guards are decorated with @Injectable().
|
|
16
|
+
*
|
|
17
|
+
* @param guards - Guard classes to apply
|
|
18
|
+
*
|
|
19
|
+
* @example Apply guard to entire controller
|
|
20
|
+
* ```typescript
|
|
21
|
+
* @Controller('/admin')
|
|
22
|
+
* @UseGuards(AuthGuard)
|
|
23
|
+
* class AdminController {
|
|
24
|
+
* @Get('/dashboard')
|
|
25
|
+
* getDashboard() {
|
|
26
|
+
* return { message: 'Admin dashboard' };
|
|
27
|
+
* }
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @example Apply guard to specific route
|
|
32
|
+
* ```typescript
|
|
33
|
+
* @Controller('/users')
|
|
34
|
+
* class UserController {
|
|
35
|
+
* @Get()
|
|
36
|
+
* findAll() {
|
|
37
|
+
* return []; // Public route
|
|
38
|
+
* }
|
|
39
|
+
*
|
|
40
|
+
* @Post()
|
|
41
|
+
* @UseGuards(AuthGuard)
|
|
42
|
+
* create() {
|
|
43
|
+
* return {}; // Protected route
|
|
44
|
+
* }
|
|
45
|
+
* }
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* @example Multiple guards (AND logic - all must pass)
|
|
49
|
+
* ```typescript
|
|
50
|
+
* @Controller('/admin')
|
|
51
|
+
* @UseGuards(AuthGuard, RolesGuard)
|
|
52
|
+
* class AdminController {
|
|
53
|
+
* // User must be authenticated AND have correct role
|
|
54
|
+
* }
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* @example Combined controller and method guards
|
|
58
|
+
* ```typescript
|
|
59
|
+
* @Controller('/api')
|
|
60
|
+
* @UseGuards(AuthGuard) // Applied to all routes
|
|
61
|
+
* class ApiController {
|
|
62
|
+
* @Get('/public')
|
|
63
|
+
* // Only AuthGuard runs
|
|
64
|
+
* getPublic() {}
|
|
65
|
+
*
|
|
66
|
+
* @Post('/admin')
|
|
67
|
+
* @UseGuards(AdminGuard) // Additional guard
|
|
68
|
+
* // Both AuthGuard AND AdminGuard run
|
|
69
|
+
* adminAction() {}
|
|
70
|
+
* }
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export declare function UseGuards(...guards: GuardClass[]): ClassDecorator & MethodDecorator;
|
|
74
|
+
/**
|
|
75
|
+
* Get guards metadata from a controller class and/or method
|
|
76
|
+
*
|
|
77
|
+
* @param target - Controller class
|
|
78
|
+
* @param propertyKey - Method name (optional)
|
|
79
|
+
* @returns Combined array of guard classes (controller + method)
|
|
80
|
+
*/
|
|
81
|
+
export declare function getGuardsMetadata(target: Constructor, propertyKey?: string | symbol): GuardClass[];
|
|
82
|
+
//# sourceMappingURL=use-guards.decorator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-guards.decorator.d.ts","sourceRoot":"","sources":["../../../src/core/guards/use-guards.decorator.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AACH,wBAAgB,SAAS,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,GAAG,cAAc,GAAG,eAAe,CA2BnF;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,WAAW,EACnB,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,GAC5B,UAAU,EAAE,CAed"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UseGuards = UseGuards;
|
|
4
|
+
exports.getGuardsMetadata = getGuardsMetadata;
|
|
5
|
+
require("reflect-metadata");
|
|
6
|
+
const constants_1 = require("../constants");
|
|
7
|
+
/**
|
|
8
|
+
* @UseGuards() Decorator
|
|
9
|
+
*
|
|
10
|
+
* Applies one or more guards to a controller or route handler.
|
|
11
|
+
* Guards are executed in order before the handler runs.
|
|
12
|
+
*
|
|
13
|
+
* Guards are resolved from the DI container, so they can have
|
|
14
|
+
* injected dependencies. Make sure guards are decorated with @Injectable().
|
|
15
|
+
*
|
|
16
|
+
* @param guards - Guard classes to apply
|
|
17
|
+
*
|
|
18
|
+
* @example Apply guard to entire controller
|
|
19
|
+
* ```typescript
|
|
20
|
+
* @Controller('/admin')
|
|
21
|
+
* @UseGuards(AuthGuard)
|
|
22
|
+
* class AdminController {
|
|
23
|
+
* @Get('/dashboard')
|
|
24
|
+
* getDashboard() {
|
|
25
|
+
* return { message: 'Admin dashboard' };
|
|
26
|
+
* }
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @example Apply guard to specific route
|
|
31
|
+
* ```typescript
|
|
32
|
+
* @Controller('/users')
|
|
33
|
+
* class UserController {
|
|
34
|
+
* @Get()
|
|
35
|
+
* findAll() {
|
|
36
|
+
* return []; // Public route
|
|
37
|
+
* }
|
|
38
|
+
*
|
|
39
|
+
* @Post()
|
|
40
|
+
* @UseGuards(AuthGuard)
|
|
41
|
+
* create() {
|
|
42
|
+
* return {}; // Protected route
|
|
43
|
+
* }
|
|
44
|
+
* }
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* @example Multiple guards (AND logic - all must pass)
|
|
48
|
+
* ```typescript
|
|
49
|
+
* @Controller('/admin')
|
|
50
|
+
* @UseGuards(AuthGuard, RolesGuard)
|
|
51
|
+
* class AdminController {
|
|
52
|
+
* // User must be authenticated AND have correct role
|
|
53
|
+
* }
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* @example Combined controller and method guards
|
|
57
|
+
* ```typescript
|
|
58
|
+
* @Controller('/api')
|
|
59
|
+
* @UseGuards(AuthGuard) // Applied to all routes
|
|
60
|
+
* class ApiController {
|
|
61
|
+
* @Get('/public')
|
|
62
|
+
* // Only AuthGuard runs
|
|
63
|
+
* getPublic() {}
|
|
64
|
+
*
|
|
65
|
+
* @Post('/admin')
|
|
66
|
+
* @UseGuards(AdminGuard) // Additional guard
|
|
67
|
+
* // Both AuthGuard AND AdminGuard run
|
|
68
|
+
* adminAction() {}
|
|
69
|
+
* }
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
function UseGuards(...guards) {
|
|
73
|
+
return (target, propertyKey) => {
|
|
74
|
+
if (propertyKey !== undefined) {
|
|
75
|
+
// Method decorator - store on the method
|
|
76
|
+
const existingGuards = Reflect.getMetadata(constants_1.GUARDS_METADATA, target.constructor, propertyKey) ?? [];
|
|
77
|
+
Reflect.defineMetadata(constants_1.GUARDS_METADATA, [...existingGuards, ...guards], target.constructor, propertyKey);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
// Class decorator - store on the class
|
|
81
|
+
const existingGuards = Reflect.getMetadata(constants_1.GUARDS_METADATA, target) ?? [];
|
|
82
|
+
Reflect.defineMetadata(constants_1.GUARDS_METADATA, [...existingGuards, ...guards], target);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Get guards metadata from a controller class and/or method
|
|
88
|
+
*
|
|
89
|
+
* @param target - Controller class
|
|
90
|
+
* @param propertyKey - Method name (optional)
|
|
91
|
+
* @returns Combined array of guard classes (controller + method)
|
|
92
|
+
*/
|
|
93
|
+
function getGuardsMetadata(target, propertyKey) {
|
|
94
|
+
// Get class-level guards
|
|
95
|
+
const classGuards = Reflect.getMetadata(constants_1.GUARDS_METADATA, target) ?? [];
|
|
96
|
+
if (!propertyKey) {
|
|
97
|
+
return classGuards;
|
|
98
|
+
}
|
|
99
|
+
// Get method-level guards
|
|
100
|
+
const methodGuards = Reflect.getMetadata(constants_1.GUARDS_METADATA, target, propertyKey) ?? [];
|
|
101
|
+
// Combine: class guards run first, then method guards
|
|
102
|
+
return [...classGuards, ...methodGuards];
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=use-guards.decorator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-guards.decorator.js","sourceRoot":"","sources":["../../../src/core/guards/use-guards.decorator.ts"],"names":[],"mappings":";;AA2EA,8BA2BC;AASD,8CAkBC;AAjID,4BAA0B;AAC1B,4CAA+C;AAS/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AACH,SAAgB,SAAS,CAAC,GAAG,MAAoB;IAC/C,OAAO,CACL,MAAc,EACd,WAA6B,EAEvB,EAAE;QACR,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;YAC9B,yCAAyC;YACzC,MAAM,cAAc,GAClB,OAAO,CAAC,WAAW,CAAC,2BAAe,EAAE,MAAM,CAAC,WAAW,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC;YAC9E,OAAO,CAAC,cAAc,CACpB,2BAAe,EACf,CAAC,GAAG,cAAc,EAAE,GAAG,MAAM,CAAC,EAC9B,MAAM,CAAC,WAAW,EAClB,WAAW,CACZ,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,uCAAuC;YACvC,MAAM,cAAc,GAClB,OAAO,CAAC,WAAW,CAAC,2BAAe,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;YACrD,OAAO,CAAC,cAAc,CACpB,2BAAe,EACf,CAAC,GAAG,cAAc,EAAE,GAAG,MAAM,CAAC,EAC9B,MAAM,CACP,CAAC;QACJ,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,iBAAiB,CAC/B,MAAmB,EACnB,WAA6B;IAE7B,yBAAyB;IACzB,MAAM,WAAW,GACf,OAAO,CAAC,WAAW,CAAC,2BAAe,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;IAErD,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,0BAA0B;IAC1B,MAAM,YAAY,GAChB,OAAO,CAAC,WAAW,CAAC,2BAAe,EAAE,MAAM,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC;IAElE,sDAAsD;IACtD,OAAO,CAAC,GAAG,WAAW,EAAE,GAAG,YAAY,CAAC,CAAC;AAC3C,CAAC"}
|
package/dist/core/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export * from './router/router';
|
|
|
8
8
|
export * from './application';
|
|
9
9
|
export * from './decorators';
|
|
10
10
|
export * from './exceptions';
|
|
11
|
+
export * from './guards';
|
|
11
12
|
export { z } from 'zod';
|
|
12
13
|
export type { ZodType, ZodSchema, ZodError, ZodIssue, infer as ZodInfer } from 'zod';
|
|
13
14
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/core/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AACA,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AACA,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AAKzB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,IAAI,QAAQ,EAAE,MAAM,KAAK,CAAC"}
|
package/dist/core/index.js
CHANGED
|
@@ -26,6 +26,7 @@ __exportStar(require("./router/router"), exports);
|
|
|
26
26
|
__exportStar(require("./application"), exports);
|
|
27
27
|
__exportStar(require("./decorators"), exports);
|
|
28
28
|
__exportStar(require("./exceptions"), exports);
|
|
29
|
+
__exportStar(require("./guards"), exports);
|
|
29
30
|
// Re-export Zod for convenience
|
|
30
31
|
// This allows users to import everything from '@riktajs/core':
|
|
31
32
|
// import { z, Body, Controller } from '@riktajs/core';
|
package/dist/core/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,eAAe;AACf,0CAAwB;AACxB,8CAA4B;AAC5B,8CAA4B;AAC5B,6CAA2B;AAC3B,8CAA4B;AAC5B,8CAA4B;AAC5B,kDAAgC;AAChC,gDAA8B;AAC9B,+CAA6B;AAC7B,+CAA6B;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,eAAe;AACf,0CAAwB;AACxB,8CAA4B;AAC5B,8CAA4B;AAC5B,6CAA2B;AAC3B,8CAA4B;AAC5B,8CAA4B;AAC5B,kDAAgC;AAChC,gDAA8B;AAC9B,+CAA6B;AAC7B,+CAA6B;AAC7B,2CAAyB;AAEzB,gCAAgC;AAChC,+DAA+D;AAC/D,uDAAuD;AACvD,2BAAwB;AAAf,wFAAA,CAAC,OAAA"}
|
|
@@ -35,5 +35,13 @@ export declare class Router {
|
|
|
35
35
|
* Resolve a single parameter with optional Zod validation
|
|
36
36
|
*/
|
|
37
37
|
private resolveParam;
|
|
38
|
+
/**
|
|
39
|
+
* Execute guards for a route
|
|
40
|
+
* Guards are executed in order (controller-level first, then method-level)
|
|
41
|
+
* All guards must pass (AND logic)
|
|
42
|
+
*
|
|
43
|
+
* @throws ForbiddenException if any guard returns false
|
|
44
|
+
*/
|
|
45
|
+
private executeGuards;
|
|
38
46
|
}
|
|
39
47
|
//# sourceMappingURL=router.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../../src/core/router/router.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAC;AACxE,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAQnD,OAAO,EAAE,WAAW,EAAiC,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../../src/core/router/router.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAC;AACxE,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAQnD,OAAO,EAAE,WAAW,EAAiC,MAAM,UAAU,CAAC;AAQtE;;;;;;;GAOG;AACH,qBAAa,MAAM;IAEf,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,YAAY;gBAFZ,MAAM,EAAE,eAAe,EACvB,SAAS,EAAE,SAAS,EACpB,YAAY,GAAE,MAAW;IAG5C;;OAEG;IACH,kBAAkB,CAAC,eAAe,EAAE,WAAW,GAAG,IAAI;IAuBtD;;OAEG;IACH,OAAO,CAAC,aAAa;IAuErB;;OAEG;IACH,OAAO,CAAC,SAAS;IASjB;;OAEG;IACH,OAAO,CAAC,aAAa;IAmBrB;;OAEG;IACH,OAAO,CAAC,YAAY;IA8DpB;;;;;;OAMG;YACW,aAAa;CAgD5B"}
|
|
@@ -4,6 +4,9 @@ exports.Router = void 0;
|
|
|
4
4
|
require("reflect-metadata");
|
|
5
5
|
const constants_1 = require("../constants");
|
|
6
6
|
const validation_exception_1 = require("../exceptions/validation.exception");
|
|
7
|
+
const exceptions_1 = require("../exceptions/exceptions");
|
|
8
|
+
const execution_context_1 = require("../guards/execution-context");
|
|
9
|
+
const use_guards_decorator_1 = require("../guards/use-guards.decorator");
|
|
7
10
|
/**
|
|
8
11
|
* Router class
|
|
9
12
|
*
|
|
@@ -55,9 +58,15 @@ class Router {
|
|
|
55
58
|
const paramsMeta = Reflect.getMetadata(constants_1.PARAM_METADATA, controllerClass, route.handlerName) ?? [];
|
|
56
59
|
// Get HTTP status code if set
|
|
57
60
|
const statusCode = Reflect.getMetadata(constants_1.HTTP_CODE_METADATA, controllerClass, route.handlerName);
|
|
61
|
+
// Get guards for this route (controller-level + method-level)
|
|
62
|
+
const guards = (0, use_guards_decorator_1.getGuardsMetadata)(controllerClass, route.handlerName);
|
|
58
63
|
// Create the route handler
|
|
59
64
|
const routeHandler = async (request, reply) => {
|
|
60
65
|
try {
|
|
66
|
+
// Execute guards before handler
|
|
67
|
+
if (guards.length > 0) {
|
|
68
|
+
await this.executeGuards(guards, request, reply, controllerClass, route.handlerName);
|
|
69
|
+
}
|
|
61
70
|
// Build route context
|
|
62
71
|
const context = {
|
|
63
72
|
request,
|
|
@@ -164,6 +173,41 @@ class Router {
|
|
|
164
173
|
// No schema provided, return raw value (backward compatible)
|
|
165
174
|
return rawValue;
|
|
166
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* Execute guards for a route
|
|
178
|
+
* Guards are executed in order (controller-level first, then method-level)
|
|
179
|
+
* All guards must pass (AND logic)
|
|
180
|
+
*
|
|
181
|
+
* @throws ForbiddenException if any guard returns false
|
|
182
|
+
*/
|
|
183
|
+
async executeGuards(guards, request, reply, controllerClass, handlerName) {
|
|
184
|
+
// Create execution context
|
|
185
|
+
const context = new execution_context_1.ExecutionContextImpl(request, reply, controllerClass, handlerName);
|
|
186
|
+
// Execute each guard sequentially
|
|
187
|
+
for (const GuardClass of guards) {
|
|
188
|
+
// Resolve guard instance from DI container
|
|
189
|
+
let guardInstance;
|
|
190
|
+
try {
|
|
191
|
+
guardInstance = this.container.resolve(GuardClass);
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
throw new Error(`Failed to resolve guard ${GuardClass.name}. ` +
|
|
195
|
+
`Make sure it is decorated with @Injectable(). ` +
|
|
196
|
+
`Original error: ${error instanceof Error ? error.message : String(error)}`);
|
|
197
|
+
}
|
|
198
|
+
// Verify the guard has canActivate method
|
|
199
|
+
if (typeof guardInstance.canActivate !== 'function') {
|
|
200
|
+
throw new Error(`${GuardClass.name} does not implement CanActivate interface. ` +
|
|
201
|
+
`The guard must have a canActivate(context: ExecutionContext) method.`);
|
|
202
|
+
}
|
|
203
|
+
// Execute the guard
|
|
204
|
+
const result = await guardInstance.canActivate(context);
|
|
205
|
+
// If guard returns false, deny access
|
|
206
|
+
if (result !== true) {
|
|
207
|
+
throw new exceptions_1.ForbiddenException(`Access denied by ${GuardClass.name}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
167
211
|
}
|
|
168
212
|
exports.Router = Router;
|
|
169
213
|
//# sourceMappingURL=router.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.js","sourceRoot":"","sources":["../../../src/core/router/router.ts"],"names":[],"mappings":";;;AAAA,4BAA0B;AAG1B,4CAMsB;AAGtB,6EAAyE;
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../../../src/core/router/router.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED