@ooneex/routing 0.0.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/LICENSE +21 -0
- package/README.md +460 -0
- package/dist/index.d.ts +226 -0
- package/dist/index.js +94 -0
- package/dist/index.js.map +13 -0
- package/dist/ooneex-routing-0.0.1.tgz +0 -0
- package/package.json +46 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Ooneex
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
# @ooneex/routing
|
|
2
|
+
|
|
3
|
+
Type-safe routing system for Ooneex applications with powerful utilities for route configuration and type generation.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @ooneex/routing
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- 🎯 Type-safe route definitions with path parameter extraction
|
|
14
|
+
- 🔒 Compile-time route validation
|
|
15
|
+
- 🚀 Decorator-based route registration
|
|
16
|
+
- 🔄 Automatic type inference from ArkType schemas
|
|
17
|
+
- 🛠️ Utilities for type string generation and path validation
|
|
18
|
+
- 🌐 Support for HTTP methods and WebSocket routes
|
|
19
|
+
- 📝 Built-in support for params, queries, payload, and response validation
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { Route, type ContextType } from "@ooneex/routing";
|
|
25
|
+
import { Assert } from "@ooneex/validation";
|
|
26
|
+
|
|
27
|
+
@Route.get("/users/:id", {
|
|
28
|
+
name: "api.users.show",
|
|
29
|
+
description: "Get a user by ID",
|
|
30
|
+
params: {
|
|
31
|
+
id: Assert("string"),
|
|
32
|
+
},
|
|
33
|
+
response: Assert({
|
|
34
|
+
id: "string",
|
|
35
|
+
name: "string",
|
|
36
|
+
email: "string",
|
|
37
|
+
}),
|
|
38
|
+
})
|
|
39
|
+
export class GetUserController {
|
|
40
|
+
public async index(context: ContextType) {
|
|
41
|
+
const { id } = context.params;
|
|
42
|
+
|
|
43
|
+
// Your logic here
|
|
44
|
+
return context.response.json({
|
|
45
|
+
id,
|
|
46
|
+
name: "John Doe",
|
|
47
|
+
email: "john@example.com",
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Route Configuration
|
|
54
|
+
|
|
55
|
+
### Route Decorators
|
|
56
|
+
|
|
57
|
+
The package provides decorators for all HTTP methods and WebSocket connections:
|
|
58
|
+
|
|
59
|
+
- `@Route.get(path, config)`
|
|
60
|
+
- `@Route.post(path, config)`
|
|
61
|
+
- `@Route.put(path, config)`
|
|
62
|
+
- `@Route.patch(path, config)`
|
|
63
|
+
- `@Route.delete(path, config)`
|
|
64
|
+
- `@Route.options(path, config)`
|
|
65
|
+
- `@Route.head(path, config)`
|
|
66
|
+
- `@Route.socket(path, config)` - for WebSocket routes
|
|
67
|
+
|
|
68
|
+
### Route Configuration Options
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
type RouteConfig = {
|
|
72
|
+
name: RouteNameType; // e.g., "api.users.list"
|
|
73
|
+
description: string; // Route description
|
|
74
|
+
params?: Record<string, AssertType | IAssert>; // Path parameters
|
|
75
|
+
queries?: AssertType | IAssert; // Query string validation
|
|
76
|
+
payload?: AssertType | IAssert; // Request body validation
|
|
77
|
+
response?: AssertType | IAssert; // Response validation
|
|
78
|
+
env?: Environment[]; // Allowed environments
|
|
79
|
+
ip?: string[]; // IP whitelist
|
|
80
|
+
host?: string[]; // Host whitelist
|
|
81
|
+
roles?: ERole[]; // Required roles
|
|
82
|
+
};
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Path Parameters
|
|
86
|
+
|
|
87
|
+
Routes support dynamic path parameters that are automatically extracted and validated:
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
@Route.delete("/users/:userId/posts/:postId", {
|
|
91
|
+
name: "api.users.posts.delete",
|
|
92
|
+
description: "Delete a user's post",
|
|
93
|
+
params: {
|
|
94
|
+
userId: Assert("string"),
|
|
95
|
+
postId: Assert("string"),
|
|
96
|
+
},
|
|
97
|
+
response: Assert({
|
|
98
|
+
success: "boolean",
|
|
99
|
+
message: "string",
|
|
100
|
+
}),
|
|
101
|
+
})
|
|
102
|
+
export class DeleteUserPostController {
|
|
103
|
+
public async index(context: ContextType) {
|
|
104
|
+
const { userId, postId } = context.params;
|
|
105
|
+
// Delete logic here
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Utilities
|
|
111
|
+
|
|
112
|
+
### `routeConfigToTypeString(config)`
|
|
113
|
+
|
|
114
|
+
Convert a route configuration to a TypeScript type string representation. This is useful for code generation, documentation, and type visualization.
|
|
115
|
+
|
|
116
|
+
#### Parameters
|
|
117
|
+
|
|
118
|
+
- `config`: Object containing route configuration with `params`, `queries`, `payload`, and/or `response` fields
|
|
119
|
+
|
|
120
|
+
#### Returns
|
|
121
|
+
|
|
122
|
+
A formatted string representing the TypeScript type definition.
|
|
123
|
+
|
|
124
|
+
#### Example
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
import { routeConfigToTypeString } from "@ooneex/routing";
|
|
128
|
+
import { type } from "arktype";
|
|
129
|
+
|
|
130
|
+
const config = {
|
|
131
|
+
params: {
|
|
132
|
+
id: type("string"),
|
|
133
|
+
emailId: type("string"),
|
|
134
|
+
},
|
|
135
|
+
payload: type({
|
|
136
|
+
name: "string",
|
|
137
|
+
email: "string",
|
|
138
|
+
}),
|
|
139
|
+
queries: type({
|
|
140
|
+
limit: "number",
|
|
141
|
+
offset: "number",
|
|
142
|
+
}),
|
|
143
|
+
response: type({
|
|
144
|
+
success: "boolean",
|
|
145
|
+
message: "string",
|
|
146
|
+
data: {
|
|
147
|
+
id: "string",
|
|
148
|
+
name: "string",
|
|
149
|
+
},
|
|
150
|
+
}),
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const typeString = routeConfigToTypeString(config);
|
|
154
|
+
console.log(typeString);
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Output:**
|
|
158
|
+
```typescript
|
|
159
|
+
{
|
|
160
|
+
response: { success: boolean; message: string; data: { id: string; name: string } };
|
|
161
|
+
params: { id: string; emailId: string };
|
|
162
|
+
payload: { name: string; email: string };
|
|
163
|
+
queries: { limit: number; offset: number };
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
#### Use Cases
|
|
168
|
+
|
|
169
|
+
**1. Type Definition Generation**
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
const typeName = "DeleteUserRouteConfig";
|
|
173
|
+
const typeDefinition = `export type ${typeName} = ${routeConfigToTypeString(config)}`;
|
|
174
|
+
|
|
175
|
+
// Generates:
|
|
176
|
+
// export type DeleteUserRouteConfig = {
|
|
177
|
+
// response: { success: boolean; message: string };
|
|
178
|
+
// params: { id: string; emailId: string };
|
|
179
|
+
// payload: { name: string };
|
|
180
|
+
// queries: { limit: number };
|
|
181
|
+
// }
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**2. API Client Generation**
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
// Generate typed API client methods
|
|
188
|
+
const routeConfig = {
|
|
189
|
+
params: { userId: type("string") },
|
|
190
|
+
response: type({ id: "string", name: "string" }),
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const clientMethod = `
|
|
194
|
+
public async getUser(userId: string): Promise<{ id: string; name: string }> {
|
|
195
|
+
return this.fetch("/users/" + userId);
|
|
196
|
+
}
|
|
197
|
+
`;
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**3. Documentation Generation**
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
// Generate API documentation with type information
|
|
204
|
+
const docString = `
|
|
205
|
+
## GET /users/:id
|
|
206
|
+
|
|
207
|
+
**Type Definition:**
|
|
208
|
+
\`\`\`typescript
|
|
209
|
+
${routeConfigToTypeString(userRouteConfig)}
|
|
210
|
+
\`\`\`
|
|
211
|
+
`;
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### `isValidRoutePath(path)`
|
|
215
|
+
|
|
216
|
+
Validates a route path at runtime.
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
import { isValidRoutePath } from "@ooneex/routing";
|
|
220
|
+
|
|
221
|
+
isValidRoutePath("/users/:id"); // true
|
|
222
|
+
isValidRoutePath("/users/:id/posts/:postId"); // true
|
|
223
|
+
isValidRoutePath("users"); // false (must start with /)
|
|
224
|
+
isValidRoutePath("/users//posts"); // false (no double slashes)
|
|
225
|
+
isValidRoutePath("/users/:"); // false (parameter needs name)
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### `extractParameterNames(path)`
|
|
229
|
+
|
|
230
|
+
Extract parameter names from a route path.
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
import { extractParameterNames } from "@ooneex/routing";
|
|
234
|
+
|
|
235
|
+
extractParameterNames("/users/:id");
|
|
236
|
+
// ["id"]
|
|
237
|
+
|
|
238
|
+
extractParameterNames("/users/:userId/posts/:postId");
|
|
239
|
+
// ["userId", "postId"]
|
|
240
|
+
|
|
241
|
+
extractParameterNames("/static/path");
|
|
242
|
+
// []
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Type System
|
|
246
|
+
|
|
247
|
+
### Route Path Types
|
|
248
|
+
|
|
249
|
+
The package provides compile-time validation for route paths:
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
import type { RoutePathType, ExtractParameters } from "@ooneex/routing";
|
|
253
|
+
|
|
254
|
+
// Valid paths
|
|
255
|
+
type ValidPath = RoutePathType<"/users/:id">;
|
|
256
|
+
type MultiParam = RoutePathType<"/users/:userId/posts/:postId">;
|
|
257
|
+
|
|
258
|
+
// Extract parameters
|
|
259
|
+
type UserParams = ExtractParameters<"/users/:id">;
|
|
260
|
+
// Result: "id"
|
|
261
|
+
|
|
262
|
+
type PostParams = ExtractParameters<"/users/:userId/posts/:postId">;
|
|
263
|
+
// Result: "userId" | "postId"
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Route Name Types
|
|
267
|
+
|
|
268
|
+
Route names follow a strict pattern: `namespace.resource.action`
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
import type { RouteNameType } from "@ooneex/routing";
|
|
272
|
+
|
|
273
|
+
// Valid route names
|
|
274
|
+
type ApiUserList = "api.users.list";
|
|
275
|
+
type ApiUserShow = "api.users.show";
|
|
276
|
+
type WebPostCreate = "client.posts.create";
|
|
277
|
+
type AdminSettingsUpdate = "admin.settings.update";
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## Router API
|
|
281
|
+
|
|
282
|
+
### Adding Routes
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
import { router } from "@ooneex/routing";
|
|
286
|
+
|
|
287
|
+
router.addRoute({
|
|
288
|
+
name: "api.users.list",
|
|
289
|
+
path: "/users",
|
|
290
|
+
method: "GET",
|
|
291
|
+
controller: UserListController,
|
|
292
|
+
description: "List all users",
|
|
293
|
+
isSocket: false,
|
|
294
|
+
});
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Finding Routes
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
// Find by path
|
|
301
|
+
const routes = router.findRouteByPath("/users/:id");
|
|
302
|
+
|
|
303
|
+
// Find by name
|
|
304
|
+
const route = router.findRouteByName("api.users.show");
|
|
305
|
+
|
|
306
|
+
// Get all routes
|
|
307
|
+
const allRoutes = router.getRoutes();
|
|
308
|
+
|
|
309
|
+
// Get only HTTP routes
|
|
310
|
+
const httpRoutes = router.getHttpRoutes();
|
|
311
|
+
|
|
312
|
+
// Get only WebSocket routes
|
|
313
|
+
const socketRoutes = router.getSocketRoutes();
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Generating URLs
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
// Generate URL from route name
|
|
320
|
+
const url = router.generate("api.users.show", { id: "123" });
|
|
321
|
+
// Result: "/users/123"
|
|
322
|
+
|
|
323
|
+
const complexUrl = router.generate("api.users.posts.show", {
|
|
324
|
+
userId: "123",
|
|
325
|
+
postId: "456",
|
|
326
|
+
});
|
|
327
|
+
// Result: "/users/123/posts/456"
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
## Advanced Examples
|
|
331
|
+
|
|
332
|
+
### Complex Route with All Options
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
import { Route } from "@ooneex/routing";
|
|
336
|
+
import { Assert } from "@ooneex/validation";
|
|
337
|
+
import { Environment } from "@ooneex/app-env";
|
|
338
|
+
import { ERole } from "@ooneex/role";
|
|
339
|
+
|
|
340
|
+
@Route.post("/admin/users/:userId/permissions", {
|
|
341
|
+
name: "admin.users.permissions.update",
|
|
342
|
+
description: "Update user permissions (admin only)",
|
|
343
|
+
params: {
|
|
344
|
+
userId: Assert("string"),
|
|
345
|
+
},
|
|
346
|
+
payload: Assert({
|
|
347
|
+
permissions: "string[]",
|
|
348
|
+
expiresAt: "string.date.parse",
|
|
349
|
+
}),
|
|
350
|
+
queries: Assert({
|
|
351
|
+
"notify?": "boolean",
|
|
352
|
+
}),
|
|
353
|
+
response: Assert({
|
|
354
|
+
success: "boolean",
|
|
355
|
+
message: "string",
|
|
356
|
+
permissions: "string[]",
|
|
357
|
+
}),
|
|
358
|
+
env: [Environment.PRODUCTION, Environment.STAGING],
|
|
359
|
+
roles: [ERole.ADMIN, ERole.SUPER_ADMIN],
|
|
360
|
+
})
|
|
361
|
+
export class UpdateUserPermissionsController {
|
|
362
|
+
public async index(context: ContextType) {
|
|
363
|
+
// Implementation
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### WebSocket Route
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
@Route.socket("/chat/:roomId", {
|
|
372
|
+
name: "client.chat.room",
|
|
373
|
+
description: "WebSocket chat room connection",
|
|
374
|
+
params: {
|
|
375
|
+
roomId: Assert("string"),
|
|
376
|
+
},
|
|
377
|
+
queries: Assert({
|
|
378
|
+
token: "string",
|
|
379
|
+
}),
|
|
380
|
+
})
|
|
381
|
+
export class ChatRoomSocketController {
|
|
382
|
+
public async index(context: ContextType) {
|
|
383
|
+
// WebSocket handling
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Optional Properties
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
@Route.patch("/users/:id", {
|
|
392
|
+
name: "api.users.update",
|
|
393
|
+
description: "Update user profile",
|
|
394
|
+
params: {
|
|
395
|
+
id: Assert("string"),
|
|
396
|
+
},
|
|
397
|
+
payload: Assert({
|
|
398
|
+
"name?": "string",
|
|
399
|
+
"email?": "string.email",
|
|
400
|
+
"age?": "number >= 18",
|
|
401
|
+
"bio?": "string",
|
|
402
|
+
}),
|
|
403
|
+
response: Assert({
|
|
404
|
+
success: "boolean",
|
|
405
|
+
updated: "boolean",
|
|
406
|
+
}),
|
|
407
|
+
})
|
|
408
|
+
export class UpdateUserController {
|
|
409
|
+
public async index(context: ContextType) {
|
|
410
|
+
// Update logic
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
## Testing
|
|
416
|
+
|
|
417
|
+
```typescript
|
|
418
|
+
import { describe, expect, test } from "bun:test";
|
|
419
|
+
import { routeConfigToTypeString, extractParameterNames } from "@ooneex/routing";
|
|
420
|
+
import { type } from "arktype";
|
|
421
|
+
|
|
422
|
+
describe("Route utilities", () => {
|
|
423
|
+
test("converts route config to type string", () => {
|
|
424
|
+
const config = {
|
|
425
|
+
params: { id: type("string") },
|
|
426
|
+
response: type({ success: "boolean" }),
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
const result = routeConfigToTypeString(config);
|
|
430
|
+
|
|
431
|
+
expect(result).toContain("params:");
|
|
432
|
+
expect(result).toContain("id: string");
|
|
433
|
+
expect(result).toContain("response:");
|
|
434
|
+
expect(result).toContain("success: boolean");
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
test("extracts parameter names", () => {
|
|
438
|
+
const params = extractParameterNames("/users/:id/posts/:postId");
|
|
439
|
+
expect(params).toEqual(["id", "postId"]);
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
## Best Practices
|
|
445
|
+
|
|
446
|
+
1. **Use descriptive route names** following the `namespace.resource.action` pattern
|
|
447
|
+
2. **Validate all inputs** using ArkType assertions for params, queries, and payload
|
|
448
|
+
3. **Document routes** with clear descriptions
|
|
449
|
+
4. **Type your responses** to ensure API consistency
|
|
450
|
+
5. **Use environment restrictions** for sensitive endpoints
|
|
451
|
+
6. **Leverage path parameter extraction** for type-safe access to URL parameters
|
|
452
|
+
7. **Generate types** using `routeConfigToTypeString` for API clients and documentation
|
|
453
|
+
|
|
454
|
+
## Contributing
|
|
455
|
+
|
|
456
|
+
Contributions are welcome! Please follow the project's coding standards and ensure all tests pass.
|
|
457
|
+
|
|
458
|
+
## License
|
|
459
|
+
|
|
460
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { AssertType as AssertType2 } from "@ooneex/validation";
|
|
2
|
+
import { Environment } from "@ooneex/app-env";
|
|
3
|
+
import { ControllerClassType } from "@ooneex/controller";
|
|
4
|
+
import { ERole } from "@ooneex/role";
|
|
5
|
+
import { HttpMethodType } from "@ooneex/types";
|
|
6
|
+
import { AssertType, IAssert } from "@ooneex/validation";
|
|
7
|
+
type RouteNamespace = "api" | "client" | "admin" | "public" | "auth" | "webhook" | "internal" | "external" | "system" | "health" | "metrics" | "docs";
|
|
8
|
+
/**
|
|
9
|
+
* Common route actions
|
|
10
|
+
*/
|
|
11
|
+
type RouteAction = "list" | "show" | "read" | "create" | "update" | "delete" | "store" | "edit" | "index" | "search" | "filter" | "sort" | "export" | "import" | "upload" | "download" | "duplicate" | "clone" | "archive" | "restore" | "activate" | "deactivate" | "enable" | "disable" | "publish" | "unpublish" | "approve" | "reject" | "cancel" | "confirm" | "validate" | "verify" | "reset" | "refresh" | "sync" | "backup" | "migrate" | "seed" | "truncate" | "count" | "exists" | "find" | "aggregate" | "bulk" | "batch" | "preview" | "template" | "history" | "audit" | "log" | "track" | "monitor" | "health" | "status" | "ping" | "test" | "debug" | "info" | "stats" | "report" | "analytics" | "metrics" | "summary" | "detail" | "config" | "settings" | "preferences" | "profile" | "avatar" | "password" | "login" | "logout" | "register" | "unregister" | "subscribe" | "unsubscribe" | "follow" | "unfollow" | "like" | "unlike" | "share" | "comment" | "reply" | "rate" | "review" | "bookmark" | "favorite" | "tag" | "untag" | "assign" | "unassign" | "invite" | "revoke" | "grant" | "deny" | "lock" | "unlock" | "move" | "copy" | "rename" | "reorder" | "merge" | "split" | "convert" | "transform" | "process" | "queue" | "retry" | "skip" | "pause" | "resume" | "stop" | "start" | "restart" | "reload" | "clear" | "flush" | "purge" | "cleanup" | "optimize" | "compress" | "decompress" | "manage" | "administer" | "supervise" | "oversee" | "govern" | "control" | "execute" | "perform" | "run" | "operate" | "handle" | "maintain" | "service" | "support" | "assist" | "help" | "guide" | "instruct" | "teach" | "train" | "educate" | "inform" | "notify" | "alert" | "warn" | "remind" | "schedule" | "plan" | "organize" | "arrange" | "coordinate" | "integrate" | "connect" | "link" | "bind" | "attach" | "detach" | "separate" | "isolate" | "quarantine" | "protect" | "secure" | "encrypt" | "decrypt" | "encode" | "decode" | "format" | "parse" | "serialize" | "deserialize" | "marshal" | "unmarshal" | "package" | "unpack" | "bundle" | "unbundle" | "group" | "ungroup" | "categorize" | "classify" | "sort" | "order" | "rank" | "prioritize" | "weight" | "score" | "evaluate" | "assess" | "measure" | "calculate" | "compute" | "analyze" | "examine" | "inspect" | "check" | "scan" | "detect" | "discover" | "explore" | "browse" | "navigate" | "travel" | "visit" | "access" | "enter" | "exit" | "leave" | "join" | "connect" | "disconnect" | "reconnect" | "establish" | "initialize" | "setup" | "configure" | "customize" | "personalize" | "adapt" | "adjust" | "modify" | "change" | "alter" | "revise" | "amend" | "correct" | "fix" | "repair" | "restore" | "recover" | "retrieve" | "fetch" | "get" | "obtain" | "acquire" | "receive" | "accept" | "take" | "capture" | "record" | "save" | "store" | "preserve" | "keep" | "retain" | "hold" | "maintain" | "sustain" | "continue" | "proceed" | "advance" | "progress" | "develop" | "evolve" | "grow" | "expand" | "extend" | "stretch" | "scale" | "resize" | "adjust" | "tune" | "calibrate" | "balance" | "stabilize" | "normalize" | "standardize" | "regulate" | "moderate" | "mediate" | "negotiate" | "compromise" | "resolve" | "solve" | "address" | "tackle" | "approach" | "handle" | "deal" | "cope" | "manage" | "oversee" | "supervise" | "direct" | "lead" | "command" | "instruct" | "order" | "request" | "ask" | "query" | "question" | "inquire" | "investigate" | "research" | "study" | "learn" | "understand" | "comprehend" | "grasp" | "realize" | "recognize" | "identify" | "determine" | "decide" | "choose" | "select" | "pick" | "opt" | "prefer" | "favor" | "recommend" | "suggest" | "propose" | "offer" | "provide" | "supply" | "deliver" | "send" | "transmit" | "transfer" | "forward" | "relay" | "redirect" | "route" | "dispatch" | "distribute" | "allocate" | "assign" | "designate" | "appoint" | "nominate" | "elect" | "vote" | "poll" | "survey" | "interview" | "question" | "examine" | "test" | "trial" | "experiment" | "try" | "attempt" | "endeavor" | "strive" | "effort" | "work" | "labor" | "toil" | "struggle" | "fight" | "battle" | "compete" | "contest" | "challenge" | "oppose" | "resist" | "defend" | "protect" | "guard" | "shield" | "cover" | "hide" | "conceal" | "mask" | "disguise" | "camouflage" | "cloak" | "wrap" | "enclose" | "contain" | "include" | "encompass" | "comprise" | "consist" | "compose" | "constitute" | "form" | "shape" | "mold" | "craft" | "build" | "construct" | "assemble" | "compile" | "gather" | "collect" | "accumulate" | "amass" | "pile" | "stack" | "heap" | "load" | "fill" | "populate" | "occupy" | "inhabit" | "reside" | "dwell" | "live" | "exist" | "become" | "turn" | "transform" | "metamorphose" | "evolve" | "mutate" | "adapt" | "adjust" | "accommodate" | "conform" | "comply" | "obey" | "follow" | "adhere" | "stick" | "attach" | "fasten" | "secure" | "tie" | "bind" | "connect" | "join" | "unite" | "merge" | "combine" | "mix" | "blend" | "fuse" | "integrate" | "incorporate" | "embed" | "insert" | "inject" | "introduce" | "import" | "bring" | "carry" | "transport" | "convey" | "deliver" | "distribute" | "spread" | "scatter" | "disperse" | "disseminate" | "broadcast" | "publish" | "announce" | "declare" | "proclaim" | "state" | "express" | "voice" | "speak" | "say" | "tell" | "communicate" | "convey" | "relay" | "transmit" | "send" | "deliver" | "transfer" | "move" | "shift" | "relocate" | "migrate" | "travel" | "journey" | "voyage" | "trip" | "visit" | "tour" | "explore" | "discover" | "find" | "locate" | "position" | "place" | "put" | "set" | "lay" | "rest" | "sit" | "stand" | "rise" | "lift" | "raise" | "elevate" | "boost" | "enhance" | "improve" | "upgrade" | "update" | "refresh" | "renew" | "revive" | "restore" | "repair" | "fix" | "mend" | "heal" | "cure" | "treat" | "remedy" | "solve" | "resolve" | "settle" | "conclude" | "finish" | "complete" | "end" | "terminate" | "stop" | "halt" | "pause" | "break" | "interrupt" | "suspend" | "delay" | "postpone" | "defer" | "wait" | "hold" | "keep" | "retain" | "preserve" | "maintain" | "sustain" | "support" | "uphold" | "defend" | "protect" | "safeguard" | "secure" | "ensure" | "guarantee" | "warrant" | "promise" | "pledge" | "commit" | "dedicate" | "devote" | "consecrate" | "sacrifice" | "offer" | "give" | "donate" | "contribute" | "provide" | "supply" | "furnish" | "equip" | "outfit" | "prepare" | "ready" | "arrange" | "organize" | "plan" | "design" | "create" | "make" | "produce" | "manufacture" | "fabricate" | "construct" | "build" | "erect" | "establish" | "found" | "institute" | "inaugurate" | "launch" | "start" | "begin" | "commence" | "initiate" | "trigger" | "activate" | "enable" | "turn-on" | "switch-on" | "power-on" | "boot" | "startup" | "load" | "run" | "execute" | "perform" | "operate" | "function" | "work" | "act" | "behave" | "conduct" | "direct" | "guide" | "lead" | "steer" | "navigate" | "pilot" | "drive" | "control" | "command" | "rule" | "govern" | "regulate" | "manage" | "administer" | "handle" | "deal" | "process" | "treat" | "handle" | "manipulate" | "operate" | "use" | "utilize" | "employ" | "apply" | "implement" | "deploy" | "install" | "setup" | "configure" | "adjust" | "tune" | "optimize" | "improve" | "enhance" | "refine" | "polish" | "perfect" | "complete" | "finalize" | "conclude" | "close" | "shut" | "seal" | "lock" | "secure" | "protect" | "defend" | "guard" | "watch" | "monitor" | "observe" | "supervise" | "oversee" | "manage";
|
|
12
|
+
/**
|
|
13
|
+
* Valid route name segment - alphanumeric with optional hyphens/underscores
|
|
14
|
+
*/
|
|
15
|
+
type RouteNameSegment = string;
|
|
16
|
+
/**
|
|
17
|
+
* Route name type that enforces 'api.users.list' format
|
|
18
|
+
* Must be three segments separated by dots: namespace.resource.action
|
|
19
|
+
* Example: 'api.users.list', 'web.posts.show', 'admin.settings.update'
|
|
20
|
+
*/
|
|
21
|
+
type RouteNameType = `${RouteNamespace}.${RouteNameSegment}.${RouteAction}`;
|
|
22
|
+
type RouteConfigType = {
|
|
23
|
+
name: RouteNameType;
|
|
24
|
+
path: `/${string}`;
|
|
25
|
+
method: HttpMethodType;
|
|
26
|
+
params?: Record<string, AssertType | IAssert>;
|
|
27
|
+
queries?: AssertType | IAssert;
|
|
28
|
+
payload?: AssertType | IAssert;
|
|
29
|
+
response?: AssertType | IAssert;
|
|
30
|
+
controller: ControllerClassType;
|
|
31
|
+
description: string;
|
|
32
|
+
env?: Environment[];
|
|
33
|
+
ip?: string[];
|
|
34
|
+
host?: string[];
|
|
35
|
+
roles?: ERole[];
|
|
36
|
+
isSocket: boolean;
|
|
37
|
+
generate?: {
|
|
38
|
+
doc?: boolean;
|
|
39
|
+
fetcher?: boolean;
|
|
40
|
+
query?: boolean;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
interface IRouter {
|
|
44
|
+
addRoute: (route: RouteConfigType) => this;
|
|
45
|
+
findRouteByPath: (path: string) => RouteConfigType[] | null;
|
|
46
|
+
findRouteByName: (name: RouteNameType) => RouteConfigType | null;
|
|
47
|
+
getRoutes: () => Map<string, RouteConfigType[]>;
|
|
48
|
+
getSocketRoutes: () => Map<string, RouteConfigType[]>;
|
|
49
|
+
getHttpRoutes: () => Map<string, RouteConfigType[]>;
|
|
50
|
+
generate: <P extends Record<string, string | number> = Record<string, string | number>>(name: RouteNameType, params?: P) => string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Check if a string segment is a route parameter (starts with :)
|
|
54
|
+
*/
|
|
55
|
+
type IsParameter<T extends string> = T extends `:${string}` ? true : false;
|
|
56
|
+
/**
|
|
57
|
+
* Extract all parameter names from a route path
|
|
58
|
+
* Examples:
|
|
59
|
+
* - "/users/:id" -> "id"
|
|
60
|
+
* - "/users/:id/bills/:billId" -> "id" | "billId"
|
|
61
|
+
* - "/static/path" -> never
|
|
62
|
+
*/
|
|
63
|
+
type ExtractParameters<T extends string> = T extends `${infer _Start}/:${infer Param}/${infer Rest}` ? Param | ExtractParameters<`/${Rest}`> : T extends `${infer _Start}/:${infer Param}` ? Param : never;
|
|
64
|
+
/**
|
|
65
|
+
* Helper type to check for malformed parameters in a single segment
|
|
66
|
+
*/
|
|
67
|
+
type HasMalformedParameter<T extends string> = T extends `:${string}:${string}` ? true : false;
|
|
68
|
+
/**
|
|
69
|
+
* Helper type to validate each path segment recursively
|
|
70
|
+
*/
|
|
71
|
+
type ValidateSegments<T extends string> = T extends `${infer Segment}/${infer Rest}` ? HasMalformedParameter<Segment> extends true ? never : ValidateSegments<Rest> : HasMalformedParameter<T> extends true ? never : T;
|
|
72
|
+
/**
|
|
73
|
+
* Validate that a route path follows correct patterns
|
|
74
|
+
* - Must start with /
|
|
75
|
+
* - No double slashes (//)
|
|
76
|
+
* - No malformed parameters (like :id:name within same segment)
|
|
77
|
+
* - Parameters must be in format /:paramName
|
|
78
|
+
* - Allow multiple parameters like /users/:id/emails/:emailId
|
|
79
|
+
*/
|
|
80
|
+
type ValidateRoutePath<T extends string> = T extends `/${infer Path}` ? T extends `${string}//${string}` ? never : T extends `${string}/:${string}/:` ? never : T extends `${string}/:` ? never : T extends `${string}:${string}/` ? never : ValidateSegments<Path> extends never ? never : T : never;
|
|
81
|
+
/**
|
|
82
|
+
* Main route path type that ensures valid path structure
|
|
83
|
+
*/
|
|
84
|
+
type RoutePathType<T extends string = string> = ValidateRoutePath<T>;
|
|
85
|
+
/**
|
|
86
|
+
* Extract route parameters as a typed record
|
|
87
|
+
* Examples:
|
|
88
|
+
* - RouteParameters<"/users/:id"> -> { id: string }
|
|
89
|
+
* - RouteParameters<"/users/:id/bills/:billId"> -> { id: string; billId: string }
|
|
90
|
+
* - RouteParameters<"/static"> -> Record<string, never>
|
|
91
|
+
*/
|
|
92
|
+
type RouteParameters<T extends string> = ExtractParameters<T> extends never ? Record<string, never> : Record<ExtractParameters<T>, string>;
|
|
93
|
+
/**
|
|
94
|
+
* Check if a route path has parameters
|
|
95
|
+
*/
|
|
96
|
+
type HasParameters<T extends string> = ExtractParameters<T> extends never ? false : true;
|
|
97
|
+
/**
|
|
98
|
+
* Get parameter count for a route path
|
|
99
|
+
*/
|
|
100
|
+
type CountParameters<
|
|
101
|
+
T extends string,
|
|
102
|
+
Count extends readonly unknown[] = readonly []
|
|
103
|
+
> = ExtractParameters<T> extends never ? Count["length"] : T extends `${infer _Start}/:${infer _Param}/${infer Rest}` ? CountParameters<`/${Rest}`, readonly [...Count, unknown]> : T extends `${infer _Start}/:${infer _Param}` ? [...Count, unknown]["length"] : Count["length"];
|
|
104
|
+
type ParameterCount<T extends string> = CountParameters<T>;
|
|
105
|
+
/**
|
|
106
|
+
* Utility type to ensure route path is valid at compile time
|
|
107
|
+
* Usage: const path: ValidRoutePath = "/users/:id/bills/:billId";
|
|
108
|
+
*/
|
|
109
|
+
type ValidRoutePath = RoutePathType<string>;
|
|
110
|
+
type TypedRouteConfig<T extends string> = Omit<RouteConfigType, "method" | "path" | "isSocket" | "controller" | "params"> & {
|
|
111
|
+
params?: ExtractParameters<T> extends never ? never : Record<ExtractParameters<T>, AssertType2>;
|
|
112
|
+
};
|
|
113
|
+
type InferredRouteDecorator = (target: new (...args: unknown[]) => unknown) => void;
|
|
114
|
+
type RouteDecoratorFunction = <T extends string>(path: RoutePathType<T>, config: TypedRouteConfig<T>) => InferredRouteDecorator;
|
|
115
|
+
declare const Route: {
|
|
116
|
+
get: RouteDecoratorFunction;
|
|
117
|
+
post: RouteDecoratorFunction;
|
|
118
|
+
put: RouteDecoratorFunction;
|
|
119
|
+
delete: RouteDecoratorFunction;
|
|
120
|
+
patch: RouteDecoratorFunction;
|
|
121
|
+
options: RouteDecoratorFunction;
|
|
122
|
+
head: RouteDecoratorFunction;
|
|
123
|
+
socket: RouteDecoratorFunction;
|
|
124
|
+
};
|
|
125
|
+
declare class Router implements IRouter {
|
|
126
|
+
private routes;
|
|
127
|
+
addRoute(route: RouteConfigType): this;
|
|
128
|
+
findRouteByPath(path: string): RouteConfigType[] | null;
|
|
129
|
+
findRouteByName(name: string): RouteConfigType | null;
|
|
130
|
+
getRoutes(): Map<string, RouteConfigType[]>;
|
|
131
|
+
getSocketRoutes(): Map<string, RouteConfigType[]>;
|
|
132
|
+
getHttpRoutes(): Map<string, RouteConfigType[]>;
|
|
133
|
+
generate<P extends Record<string, string | number> = Record<string, string | number>>(name: string, params?: P): string;
|
|
134
|
+
}
|
|
135
|
+
import { Exception } from "@ooneex/exception";
|
|
136
|
+
declare class RouterException extends Exception {
|
|
137
|
+
constructor(message: string, data?: Record<string, unknown>);
|
|
138
|
+
}
|
|
139
|
+
declare const isValidRoutePath: (path: string) => path is ValidRoutePath;
|
|
140
|
+
/**
|
|
141
|
+
* Extract parameter names from a route path at runtime
|
|
142
|
+
*/
|
|
143
|
+
declare const extractParameterNames: (path: string) => string[];
|
|
144
|
+
/**
|
|
145
|
+
* Convert RouteConfigType to TypeScript type string representation
|
|
146
|
+
*
|
|
147
|
+
* @param config - Route configuration object
|
|
148
|
+
* @returns TypeScript type definition as a string
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```ts
|
|
152
|
+
* const config = {
|
|
153
|
+
* params: {
|
|
154
|
+
* id: Assert("string"),
|
|
155
|
+
* emailId: Assert("string"),
|
|
156
|
+
* },
|
|
157
|
+
* payload: Assert({ name: "string" }),
|
|
158
|
+
* queries: Assert({ limit: "number" }),
|
|
159
|
+
* response: Assert({ success: "boolean", message: "string" }),
|
|
160
|
+
* };
|
|
161
|
+
*
|
|
162
|
+
* const typeString = routeConfigToTypeString(config);
|
|
163
|
+
* // Returns:
|
|
164
|
+
* // {
|
|
165
|
+
* // response: { success: boolean; message: string };
|
|
166
|
+
* // params: { id: string; emailId: string };
|
|
167
|
+
* // payload: { name: string };
|
|
168
|
+
* // queries: { limit: number };
|
|
169
|
+
* // }
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
declare const routeConfigToTypeString: (config: Pick<RouteConfigType, "params" | "queries" | "payload" | "response">) => string;
|
|
173
|
+
/**
|
|
174
|
+
* Convert RouteConfigType to JSON documentation format
|
|
175
|
+
*
|
|
176
|
+
* @param config - Route configuration object
|
|
177
|
+
* @returns JSON documentation object with route metadata and schemas
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```ts
|
|
181
|
+
* const config = {
|
|
182
|
+
* name: "api.users.delete",
|
|
183
|
+
* path: "/users/:id/emails/:emailId",
|
|
184
|
+
* method: "DELETE",
|
|
185
|
+
* description: "Delete a user by ID",
|
|
186
|
+
* params: {
|
|
187
|
+
* id: Assert("string"),
|
|
188
|
+
* emailId: Assert("string"),
|
|
189
|
+
* },
|
|
190
|
+
* payload: Assert({ name: "string" }),
|
|
191
|
+
* queries: Assert({ limit: "number" }),
|
|
192
|
+
* response: Assert({ success: "boolean", message: "string" }),
|
|
193
|
+
* env: [Environment.LOCAL],
|
|
194
|
+
* roles: [ERole.ADMIN],
|
|
195
|
+
* isSocket: false,
|
|
196
|
+
* };
|
|
197
|
+
*
|
|
198
|
+
* const jsonDoc = routeConfigToJsonDoc(config);
|
|
199
|
+
* // Returns:
|
|
200
|
+
* // {
|
|
201
|
+
* // name: "api.users.delete",
|
|
202
|
+
* // path: "/users/:id/emails/:emailId",
|
|
203
|
+
* // method: "DELETE",
|
|
204
|
+
* // description: "Delete a user by ID",
|
|
205
|
+
* // isSocket: false,
|
|
206
|
+
* // parameters: ["id", "emailId"],
|
|
207
|
+
* // schemas: {
|
|
208
|
+
* // params: { type: "object", properties: { id: { type: "string" }, emailId: { type: "string" } } },
|
|
209
|
+
* // queries: { type: "object", properties: { limit: { type: "number" } } },
|
|
210
|
+
* // payload: { type: "object", properties: { name: { type: "string" } } },
|
|
211
|
+
* // response: { type: "object", properties: { success: { type: "boolean" }, message: { type: "string" } } }
|
|
212
|
+
* // },
|
|
213
|
+
* // security: {
|
|
214
|
+
* // environments: ["LOCAL"],
|
|
215
|
+
* // roles: ["ADMIN"],
|
|
216
|
+
* // allowedIPs: [],
|
|
217
|
+
* // allowedHosts: []
|
|
218
|
+
* // }
|
|
219
|
+
* // }
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
declare const routeConfigToJsonDoc: (config: RouteConfigType) => Record<string, unknown>;
|
|
223
|
+
declare const routeConfigToFetcherString: (config: RouteConfigType) => string;
|
|
224
|
+
declare const routeConfigToSocketString: (config: RouteConfigType) => string;
|
|
225
|
+
declare const routeConfigToUseQueryString: (config: RouteConfigType, baseURL?: string) => string;
|
|
226
|
+
export { routeConfigToUseQueryString, routeConfigToTypeString, routeConfigToSocketString, routeConfigToJsonDoc, routeConfigToFetcherString, isValidRoutePath, extractParameterNames, ValidateRoutePath, ValidRoutePath, RouterException, Router, RoutePathType, RouteParameters, RouteNamespace, RouteNameType, RouteNameSegment, RouteConfigType, RouteAction, Route, ParameterCount, IsParameter, IRouter, HasParameters, ExtractParameters, CountParameters };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import{container as h,EContainerScope as n}from"@ooneex/container";import{Exception as p}from"@ooneex/exception";import{HttpStatus as r}from"@ooneex/http-status";class Z extends p{constructor(R,w={}){super(R,{status:r.Code.InternalServerError,data:w});this.name="RouterException"}}class U{routes=new Map;addRoute(R){let w=R.name;for(let M of this.routes[Symbol.iterator]())if(M[1].find((H)=>H.name===w))throw new Z(`Route with name '${w}' already exists`,R);let $=this.routes.get(R.path)??[];if(R.isSocket&&$.find((M)=>M.isSocket))throw new Z(`Socket route with path '${R.path}' already exists`,R);if(!R.isSocket&&$.find((M)=>!M.isSocket&&M.method===R.method))throw new Z(`Route with path '${R.path}' and method '${R.method}' already exists`,R);return $.push(R),this.routes.set(R.path,$),h.add(R.controller,n.Singleton),this}findRouteByPath(R){return this.routes.get(R)??null}findRouteByName(R){for(let w of this.routes[Symbol.iterator]()){let $=w[1].find((M)=>M.name===R);if($)return $}return null}getRoutes(){return this.routes}getSocketRoutes(){let R=new Map;for(let[w,$]of this.routes){let M=$.filter((x)=>x.isSocket);if(M.length>0)R.set(w,M)}return R}getHttpRoutes(){let R=new Map;for(let[w,$]of this.routes){let M=$.filter((x)=>!x.isSocket);if(M.length>0)R.set(w,M)}return R}generate(R,w){let $=this.findRouteByName(R);if(!$)throw new Z(`Route with name '${R}' not found`);let M=$.path,x=M.match(/:[a-zA-Z0-9_]+/g)||[];if(x.length>0){if(!w||typeof w!=="object"||w===null)throw new Z(`Route '${R}' requires parameters, but none were provided`);for(let H of x){let v=H.substring(1);if(!(v in w))throw new Z(`Missing required parameter '${v}' for route '${R}'`);M=M.replace(H,String(w[v]))}}return M}}var q=new U;var Y=(R)=>{return(w,$)=>{return(M)=>{let x={...$,path:w,method:R,isSocket:!1,controller:M};q.addRoute(x)}}},f=()=>{return(R,w)=>{return($)=>{let M={...w,path:R,method:"GET",isSocket:!0,controller:$};q.addRoute(M)}}},uR={get:Y("GET"),post:Y("POST"),put:Y("PUT"),delete:Y("DELETE"),patch:Y("PATCH"),options:Y("OPTIONS"),head:Y("HEAD"),socket:f()};import{toPascalCase as Q}from"@ooneex/utils";var zR=(R)=>{if(!R.startsWith("/"))return!1;if(R.includes("//"))return!1;if(R.includes("::"))return!1;if(R.endsWith(":"))return!1;if(R.includes("/:")){let w=R.split("/");for(let $ of w){if($.startsWith(":")&&$.length===1)return!1;if($.includes(":")&&!$.startsWith(":"))return!1}}return!0},g=(R)=>{let w=R.match(/:([^/]+)/g);return w?w.map(($)=>$.slice(1)):[]},_=(R)=>{if(!R||typeof R!=="object")return"unknown";let w=R;if(w.type)switch(w.type){case"string":return"string";case"number":case"integer":return"number";case"boolean":return"boolean";case"null":return"null";case"array":if(w.items)return`${_(w.items)}[]`;return"unknown[]";case"object":if(w.properties&&typeof w.properties==="object"){let $=[],M=Array.isArray(w.required)?w.required:[];for(let[x,H]of Object.entries(w.properties)){let v=M.includes(x),u=_(H);$.push(`${x}${v?"":"?"}: ${u}`)}return`{ ${$.join("; ")} }`}return"Record<string, unknown>"}if(w.anyOf||w.oneOf){let $=w.anyOf||w.oneOf;if(Array.isArray($))return $.map((M)=>_(M)).join(" | ")}if(w.allOf&&Array.isArray(w.allOf))return w.allOf.map(($)=>_($)).join(" & ");return"unknown"},b=(R)=>{if(!R.response&&!R.params&&!R.payload&&!R.queries)return"never";let w=[];if(R.response)try{let M=("getConstraint"in R.response?R.response.getConstraint():R.response).toJsonSchema(),x=_(M);if(x==="unknown"||x==="{ }"||x==="Record<string, unknown>")x="never";w.push(`response: ${x}`)}catch{w.push("response: never")}if(R.params){let $=[];for(let[M,x]of Object.entries(R.params))try{let v=("getConstraint"in x?x.getConstraint():x).toJsonSchema(),u=_(v);if(u==="unknown"||u==="{ }"||u==="Record<string, unknown>")u="never";$.push(`${M}: ${u}`)}catch{$.push(`${M}: never`)}if($.length>0){let M=`{ ${$.join("; ")} }`;w.push(`params: ${M}`)}else w.push("params: never")}if(R.payload)try{let M=("getConstraint"in R.payload?R.payload.getConstraint():R.payload).toJsonSchema(),x=_(M);if(x==="unknown"||x==="{ }"||x==="Record<string, unknown>")x="never";w.push(`payload: ${x}`)}catch{w.push("payload: never")}if(R.queries)try{let M=("getConstraint"in R.queries?R.queries.getConstraint():R.queries).toJsonSchema(),x=_(M);if(x==="unknown"||x==="{ }"||x==="Record<string, unknown>")x="never";w.push(`queries: ${x}`)}catch{w.push("queries: never")}return`{
|
|
2
|
+
${w.join(`;
|
|
3
|
+
`)};
|
|
4
|
+
}`},L=(R)=>{try{return(R&&typeof R==="object"&&"getConstraint"in R?R.getConstraint():R).toJsonSchema()}catch{return{type:"unknown"}}},BR=(R)=>{let w={name:R.name,path:R.path,method:R.method,description:R.description,controller:R.controller.name,isSocket:R.isSocket,parameters:g(R.path)},$={};if(R.params){let x={type:"object",properties:{}};for(let[H,v]of Object.entries(R.params)){let u=L(v);delete u.$schema,u.required=!0,x.properties[H]=u}$.params=x}if(R.queries){let x=L(R.queries);if(delete x.$schema,x.type==="object"&&x.properties){let H=x.required||[],v=x.properties;for(let u of Object.keys(v)){let I=v[u];I.required=H.includes(u)}delete x.required}$.queries=x}if(R.payload){let x=L(R.payload);if(delete x.$schema,x.type==="object"&&x.properties){let H=x.required||[],v=x.properties;for(let u of Object.keys(v)){let I=v[u];I.required=H.includes(u)}delete x.required}$.payload=x}if(R.response){let x=L(R.response);if(delete x.$schema,x.type==="object"&&x.properties){let H=x.required||[],v=x.properties;for(let u of Object.keys(v)){let I=v[u];I.required=H.includes(u)}delete x.required}$.response=x}if(Object.keys($).length>0)w.schemas=$;let M={};if(R.env&&R.env.length>0)M.environments=R.env;if(R.roles&&R.roles.length>0)M.roles=R.roles;if(R.ip&&R.ip.length>0)M.allowedIPs=R.ip;if(R.host&&R.host.length>0)M.allowedHosts=R.host;if(Object.keys(M).length>0)w.security=M;return w},O=(R)=>{let w=R.split(".");return w[w.length-1]||""},W=(R,w=!0)=>{let $=w?"config.params":"params";return R.replace(/:(\w+)/g,(M,x)=>`\${${$}.${x}}`)},IR=(R)=>{let w=O(R.name),$=`${w.charAt(0).toUpperCase()+w.slice(1)}RouteConfigType`,M=`${Q(R.name)}Fetcher`,x=R.method.toLowerCase(),H=b(R),v=`export type ${$} = ${H}`,u=[],I=R.params&&Object.keys(R.params).length>0,E=R.payload!==void 0,G=R.queries!==void 0;if(I)u.push(`params: ${$}["params"]`);if(E)u.push(`payload: ${$}["payload"]`);if(G)u.push(`queries: ${$}["queries"]`);let V=u.length>0?`config: {
|
|
5
|
+
${u.join(`;
|
|
6
|
+
`)};
|
|
7
|
+
}`:"",z=W(R.path),B=`\`${z}\``;if(G)B=`\`${z}?\${new URLSearchParams(config.queries as Record<string, string>).toString()}\``;let C=["post","put","patch"],X="";if(C.includes(x)&&E)X=`return await fetcher.${x}<${$}["response"]>(
|
|
8
|
+
${B},
|
|
9
|
+
config.payload,
|
|
10
|
+
);`;else X=`return await fetcher.${x}<${$}["response"]>(
|
|
11
|
+
${B},
|
|
12
|
+
);`;let A=`export class ${M} {
|
|
13
|
+
constructor(private baseURL: string) {}
|
|
14
|
+
|
|
15
|
+
public async ${w}(${V}): Promise<ResponseDataType<${$}["response"]>> {
|
|
16
|
+
const fetcher = new Fetcher(this.baseURL);
|
|
17
|
+
|
|
18
|
+
${X}
|
|
19
|
+
}
|
|
20
|
+
}`;return`${`import type { ResponseDataType } from "@ooneex/http-response";
|
|
21
|
+
import { Fetcher } from "@ooneex/fetcher";`}
|
|
22
|
+
|
|
23
|
+
${v}
|
|
24
|
+
|
|
25
|
+
${A}`},ZR=(R)=>{let w=O(R.name),$=`${w.charAt(0).toUpperCase()+w.slice(1)}RouteConfigType`,M=`${Q(R.name)}Socket`,x=b(R),H=`export type ${$} = ${x}`,v=R.params&&Object.keys(R.params).length>0,u=R.payload&&Object.keys(R.payload).length>0,I=R.queries&&Object.keys(R.queries).length>0,E=v?`params: ${$}["params"]`:"",V=`\`\${this.baseURL}${W(R.path,!1)}\``,z=[];if(u)z.push(`payload: ${$}["payload"]`);if(I)z.push(`queries: ${$}["queries"]`);z.push("language?: LocaleInfoType");let B=z.length>0?`{ ${z.join("; ")} }`:"Record<string, unknown>",C=`export class ${M} {
|
|
26
|
+
constructor(private baseURL: string) {}
|
|
27
|
+
|
|
28
|
+
public ${w}(${E}): ISocket {
|
|
29
|
+
const url = ${V};
|
|
30
|
+
const socket = new Socket<${B}, ${$}["response"]>(url);
|
|
31
|
+
|
|
32
|
+
socket.onMessage((response) => {
|
|
33
|
+
// TODO: Handle socket message event
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
socket.onOpen((event) => {
|
|
37
|
+
// TODO: Handle socket open event
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
socket.onClose((event) => {
|
|
41
|
+
// TODO: Handle socket close event
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
socket.onError((event, response) => {
|
|
45
|
+
// TODO: Handle socket error event
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
return socket;
|
|
49
|
+
}
|
|
50
|
+
}`;return`${`import type { LocaleInfoType } from "@ooneex/translation";
|
|
51
|
+
import { type ISocket, Socket } from "@ooneex/socket/client"`}
|
|
52
|
+
|
|
53
|
+
${H}
|
|
54
|
+
|
|
55
|
+
${C}`},_R=(R,w="")=>{let $=O(R.name),M=`${$.charAt(0).toUpperCase()+$.slice(1)}RouteConfigType`,x=`use${Q(R.name)}`,H=R.method.toUpperCase(),v=b(R),u=`export type ${M} = ${v}`,E=["GET","HEAD","OPTIONS"].includes(H),G=R.params&&Object.keys(R.params).length>0,V=R.payload!==void 0,z=R.queries!==void 0;if(E){let F=[];if(G)F.push(`params: ${M}["params"]`);if(z)F.push(`queries?: ${M}["queries"]`);let S=F.length>0?`config: {
|
|
56
|
+
${F.join(`;
|
|
57
|
+
`)};
|
|
58
|
+
}`:"",J=`[${R.name.split(".").map((K)=>`'${K}'`).join(", ")}`;if(G&&R.params){let K=Object.keys(R.params);for(let m of K)J+=`, config.params.${m}`}if(z)J+=", config.queries";J+="]";let j=W(R.path),D=`\`${j}\``;if(z)D=`\`${j}?\${new URLSearchParams(config.queries as Record<string, string>).toString()}\``;let P=H.toLowerCase(),t=`const fetcher = new Fetcher('${w}');
|
|
59
|
+
const url = ${D};
|
|
60
|
+
|
|
61
|
+
return await fetcher.${P}<${M}["response"]>(url);`,y=`export const ${x} = (${S}) => {
|
|
62
|
+
return useQuery({
|
|
63
|
+
queryKey: ${J},
|
|
64
|
+
queryFn: async () => {
|
|
65
|
+
${t}
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
};`;return`${`import { useQuery } from '@tanstack/react-query';
|
|
69
|
+
import { Fetcher } from '@ooneex/fetcher';`}
|
|
70
|
+
|
|
71
|
+
${u}
|
|
72
|
+
|
|
73
|
+
${y}`}let B=[];if(G)B.push(`params: ${M}["params"]`);if(V)B.push(`payload: ${M}["payload"]`);if(z)B.push(`queries?: ${M}["queries"]`);let C=B.length>0?`config: {
|
|
74
|
+
${B.join(`;
|
|
75
|
+
`)};
|
|
76
|
+
}`:"config?: Record<string, never>",X=W(R.path),A=`\`${X}\``;if(z)A=`\`${X}\${config.queries ? \`?\${new URLSearchParams(config.queries as Record<string, string>).toString()}\` : ''}\``;let l=["POST","PUT","PATCH"].includes(H)&&V,d=H.toLowerCase(),k=`const fetcher = new Fetcher('${w}');
|
|
77
|
+
const url = ${A};
|
|
78
|
+
|
|
79
|
+
`;if(l)k+=`return await fetcher.${d}<${M}["response"]>(url, config.payload);`;else k+=`return await fetcher.${d}<${M}["response"]>(url);`;let T=`export const ${x} = () => {
|
|
80
|
+
const mutation = useMutation({
|
|
81
|
+
mutationFn: async (${C}) => {
|
|
82
|
+
${k}
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return mutation;
|
|
87
|
+
};`;return`${`import { useMutation } from '@tanstack/react-query';
|
|
88
|
+
import { Fetcher } from '@ooneex/fetcher';`}
|
|
89
|
+
|
|
90
|
+
${u}
|
|
91
|
+
|
|
92
|
+
${T}`};export{_R as routeConfigToUseQueryString,b as routeConfigToTypeString,ZR as routeConfigToSocketString,BR as routeConfigToJsonDoc,IR as routeConfigToFetcherString,zR as isValidRoutePath,g as extractParameterNames,Z as RouterException,U as Router,uR as Route};
|
|
93
|
+
|
|
94
|
+
//# debugId=F38C937393AEFC1764756E2164756E21
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["src/Router.ts", "src/RouterException.ts", "src/decorators.ts", "src/utils.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { container, EContainerScope } from \"@ooneex/container\";\nimport { RouterException } from \"./RouterException\";\nimport type { IRouter, RouteConfigType } from \"./types\";\n\nexport class Router implements IRouter {\n private routes: Map<string, RouteConfigType[]> = new Map();\n\n public addRoute(route: RouteConfigType): this {\n const name = route.name;\n\n for (const item of this.routes[Symbol.iterator]()) {\n const existingRoute = item[1].find((r) => r.name === name);\n\n if (existingRoute) {\n throw new RouterException(`Route with name '${name}' already exists`, route);\n }\n }\n\n const routes = this.routes.get(route.path) ?? [];\n\n if (route.isSocket && routes.find((r) => r.isSocket)) {\n throw new RouterException(`Socket route with path '${route.path}' already exists`, route);\n }\n\n if (!route.isSocket && routes.find((r) => !r.isSocket && r.method === route.method)) {\n throw new RouterException(`Route with path '${route.path}' and method '${route.method}' already exists`, route);\n }\n\n routes.push(route);\n this.routes.set(route.path, routes);\n container.add(route.controller, EContainerScope.Singleton);\n\n return this;\n }\n\n public findRouteByPath(path: string): RouteConfigType[] | null {\n return this.routes.get(path) ?? null;\n }\n\n public findRouteByName(name: string): RouteConfigType | null {\n for (const item of this.routes[Symbol.iterator]()) {\n const existingRoute = item[1].find((r) => r.name === name);\n\n if (existingRoute) {\n return existingRoute;\n }\n }\n\n return null;\n }\n\n public getRoutes(): Map<string, RouteConfigType[]> {\n return this.routes;\n }\n\n public getSocketRoutes(): Map<string, RouteConfigType[]> {\n const socketRoutes = new Map<string, RouteConfigType[]>();\n\n for (const [path, routes] of this.routes) {\n const filteredRoutes = routes.filter((route): route is RouteConfigType => route.isSocket);\n if (filteredRoutes.length > 0) {\n socketRoutes.set(path, filteredRoutes);\n }\n }\n\n return socketRoutes;\n }\n\n public getHttpRoutes(): Map<string, RouteConfigType[]> {\n const httpRoutes = new Map<string, RouteConfigType[]>();\n\n for (const [path, routes] of this.routes) {\n const filteredRoutes = routes.filter((route): route is RouteConfigType => !route.isSocket);\n if (filteredRoutes.length > 0) {\n httpRoutes.set(path, filteredRoutes);\n }\n }\n\n return httpRoutes;\n }\n\n public generate<P extends Record<string, string | number> = Record<string, string | number>>(\n name: string,\n params?: P,\n ): string {\n const route = this.findRouteByName(name);\n\n if (!route) {\n throw new RouterException(`Route with name '${name}' not found`);\n }\n\n let path: string = route.path;\n const paramMatches = path.match(/:[a-zA-Z0-9_]+/g) || [];\n\n if (paramMatches.length > 0) {\n if (!params || typeof params !== \"object\" || params === null) {\n throw new RouterException(`Route '${name}' requires parameters, but none were provided`);\n }\n\n for (const match of paramMatches) {\n const paramName = match.substring(1);\n if (!(paramName in params)) {\n throw new RouterException(`Missing required parameter '${paramName}' for route '${name}'`);\n }\n\n path = path.replace(match, String(params[paramName]));\n }\n }\n\n return path;\n }\n}\n\nexport const router: Router = new Router();\n",
|
|
6
|
+
"import { Exception } from \"@ooneex/exception\";\nimport { HttpStatus } from \"@ooneex/http-status\";\n\nexport class RouterException extends Exception {\n constructor(message: string, data: Record<string, unknown> = {}) {\n super(message, {\n status: HttpStatus.Code.InternalServerError,\n data,\n });\n this.name = \"RouterException\";\n }\n}\n",
|
|
7
|
+
"import type { ControllerClassType } from \"@ooneex/controller\";\nimport type { HttpMethodType } from \"@ooneex/types\";\nimport type { AssertType } from \"@ooneex/validation\";\nimport { router } from \"./Router\";\nimport type { ExtractParameters, RouteConfigType, RoutePathType } from \"./types\";\n\ntype TypedRouteConfig<T extends string> = Omit<\n RouteConfigType,\n \"method\" | \"path\" | \"isSocket\" | \"controller\" | \"params\"\n> & {\n params?: ExtractParameters<T> extends never ? never : Record<ExtractParameters<T>, AssertType>;\n};\n\ntype InferredRouteDecorator = (target: new (...args: unknown[]) => unknown) => void;\n\ntype RouteDecoratorFunction = <T extends string>(\n path: RoutePathType<T>,\n config: TypedRouteConfig<T>,\n) => InferredRouteDecorator;\n\nconst createRouteDecorator = (method: HttpMethodType) => {\n return <T extends string>(path: RoutePathType<T>, config: TypedRouteConfig<T>): InferredRouteDecorator => {\n return (target: new (...args: unknown[]) => unknown): void => {\n const route: RouteConfigType = {\n ...config,\n path,\n method,\n isSocket: false,\n controller: target as ControllerClassType,\n };\n\n router.addRoute(route);\n };\n };\n};\n\nconst createSocketDecorator = () => {\n return <T extends string>(path: RoutePathType<T>, config: TypedRouteConfig<T>): InferredRouteDecorator => {\n return (target: new (...args: unknown[]) => unknown): void => {\n const route: RouteConfigType = {\n ...config,\n path,\n method: \"GET\",\n isSocket: true,\n controller: target as ControllerClassType,\n };\n\n router.addRoute(route);\n };\n };\n};\n\nexport const Route = {\n get: createRouteDecorator(\"GET\") as RouteDecoratorFunction,\n post: createRouteDecorator(\"POST\") as RouteDecoratorFunction,\n put: createRouteDecorator(\"PUT\") as RouteDecoratorFunction,\n delete: createRouteDecorator(\"DELETE\") as RouteDecoratorFunction,\n patch: createRouteDecorator(\"PATCH\") as RouteDecoratorFunction,\n options: createRouteDecorator(\"OPTIONS\") as RouteDecoratorFunction,\n head: createRouteDecorator(\"HEAD\") as RouteDecoratorFunction,\n socket: createSocketDecorator() as RouteDecoratorFunction,\n};\n\n// @Route.delete(\"/users/:id/emails/:emailId/state/:state\", {\n// name: \"api.users.delete\",\n// description: \"Delete a user by ID\",\n// params: {\n// state: Assert(\"string\"),\n// id: Assert(\"string\"),\n// emailId: Assert(\"string\"),\n// },\n// payload: Assert({\n// name: \"string\",\n// }),\n// queries: Assert({\n// limit: \"number\",\n// }),\n// response: Assert({\n// success: \"boolean\",\n// message: \"string\",\n// }),\n// env: [Environment.LOCAL],\n// })\n// export class DeleteUserController {\n// public async index(context: ContextType<TypedRouteConfigType>) {\n// const { id } = context.params;\n//\n// return context.response.json({\n// success: true,\n// message: `User with ID ${id} has been deleted`,\n// });\n// }\n// }\n",
|
|
8
|
+
"import { toPascalCase } from \"@ooneex/utils\";\nimport type { RouteConfigType, ValidRoutePath } from \"./types\";\n\n// Type guards and validation helpers\nexport const isValidRoutePath = (path: string): path is ValidRoutePath => {\n // Runtime validation\n if (!path.startsWith(\"/\")) return false;\n if (path.includes(\"//\")) return false;\n if (path.includes(\"::\")) return false;\n if (path.endsWith(\":\")) return false;\n if (path.includes(\"/:\")) {\n // Check for malformed parameters\n const segments = path.split(\"/\");\n for (const segment of segments) {\n if (segment.startsWith(\":\") && segment.length === 1) return false;\n if (segment.includes(\":\") && !segment.startsWith(\":\")) return false;\n }\n }\n return true;\n};\n\n/**\n * Extract parameter names from a route path at runtime\n */\nexport const extractParameterNames = (path: string): string[] => {\n const matches = path.match(/:([^/]+)/g);\n return matches ? matches.map((match) => match.slice(1)) : [];\n};\n\n/**\n * Convert a JSON Schema type to TypeScript type string\n */\nconst jsonSchemaToTypeString = (schema: unknown): string => {\n if (!schema || typeof schema !== \"object\") return \"unknown\";\n\n const schemaObj = schema as Record<string, unknown>;\n\n // Handle type property\n if (schemaObj.type) {\n switch (schemaObj.type) {\n case \"string\":\n return \"string\";\n case \"number\":\n case \"integer\":\n return \"number\";\n case \"boolean\":\n return \"boolean\";\n case \"null\":\n return \"null\";\n case \"array\":\n if (schemaObj.items) {\n return `${jsonSchemaToTypeString(schemaObj.items)}[]`;\n }\n return \"unknown[]\";\n case \"object\":\n if (schemaObj.properties && typeof schemaObj.properties === \"object\") {\n const props: string[] = [];\n const required = Array.isArray(schemaObj.required) ? schemaObj.required : [];\n\n for (const [key, value] of Object.entries(schemaObj.properties)) {\n const isRequired = required.includes(key);\n const propType = jsonSchemaToTypeString(value);\n props.push(`${key}${isRequired ? \"\" : \"?\"}: ${propType}`);\n }\n\n return `{ ${props.join(\"; \")} }`;\n }\n return \"Record<string, unknown>\";\n }\n }\n\n // Handle anyOf/oneOf (union types)\n if (schemaObj.anyOf || schemaObj.oneOf) {\n const schemas = schemaObj.anyOf || schemaObj.oneOf;\n if (Array.isArray(schemas)) {\n return schemas.map((s: unknown) => jsonSchemaToTypeString(s)).join(\" | \");\n }\n }\n\n // Handle allOf (intersection types)\n if (schemaObj.allOf && Array.isArray(schemaObj.allOf)) {\n return schemaObj.allOf.map((s: unknown) => jsonSchemaToTypeString(s)).join(\" & \");\n }\n\n return \"unknown\";\n};\n\n/**\n * Convert RouteConfigType to TypeScript type string representation\n *\n * @param config - Route configuration object\n * @returns TypeScript type definition as a string\n *\n * @example\n * ```ts\n * const config = {\n * params: {\n * id: Assert(\"string\"),\n * emailId: Assert(\"string\"),\n * },\n * payload: Assert({ name: \"string\" }),\n * queries: Assert({ limit: \"number\" }),\n * response: Assert({ success: \"boolean\", message: \"string\" }),\n * };\n *\n * const typeString = routeConfigToTypeString(config);\n * // Returns:\n * // {\n * // response: { success: boolean; message: string };\n * // params: { id: string; emailId: string };\n * // payload: { name: string };\n * // queries: { limit: number };\n * // }\n * ```\n */\nexport const routeConfigToTypeString = (\n config: Pick<RouteConfigType, \"params\" | \"queries\" | \"payload\" | \"response\">,\n): string => {\n if (!config.response && !config.params && !config.payload && !config.queries) {\n return \"never\";\n }\n\n const typeProperties: string[] = [];\n\n if (config.response) {\n try {\n const constraint = \"getConstraint\" in config.response ? config.response.getConstraint() : config.response;\n const schema = constraint.toJsonSchema();\n let typeStr = jsonSchemaToTypeString(schema);\n if (typeStr === \"unknown\" || typeStr === \"{ }\" || typeStr === \"Record<string, unknown>\") {\n typeStr = \"never\";\n }\n typeProperties.push(`response: ${typeStr}`);\n } catch {\n typeProperties.push(\"response: never\");\n }\n }\n\n if (config.params) {\n const paramProps: string[] = [];\n\n for (const [key, assert] of Object.entries(config.params)) {\n try {\n const constraint = \"getConstraint\" in assert ? assert.getConstraint() : assert;\n const schema = constraint.toJsonSchema();\n let typeStr = jsonSchemaToTypeString(schema);\n if (typeStr === \"unknown\" || typeStr === \"{ }\" || typeStr === \"Record<string, unknown>\") {\n typeStr = \"never\";\n }\n paramProps.push(`${key}: ${typeStr}`);\n } catch {\n paramProps.push(`${key}: never`);\n }\n }\n\n if (paramProps.length > 0) {\n const paramsType = `{ ${paramProps.join(\"; \")} }`;\n typeProperties.push(`params: ${paramsType}`);\n } else {\n typeProperties.push(\"params: never\");\n }\n }\n\n if (config.payload) {\n try {\n const constraint = \"getConstraint\" in config.payload ? config.payload.getConstraint() : config.payload;\n const schema = constraint.toJsonSchema();\n let typeStr = jsonSchemaToTypeString(schema);\n if (typeStr === \"unknown\" || typeStr === \"{ }\" || typeStr === \"Record<string, unknown>\") {\n typeStr = \"never\";\n }\n typeProperties.push(`payload: ${typeStr}`);\n } catch {\n typeProperties.push(\"payload: never\");\n }\n }\n\n if (config.queries) {\n try {\n const constraint = \"getConstraint\" in config.queries ? config.queries.getConstraint() : config.queries;\n const schema = constraint.toJsonSchema();\n let typeStr = jsonSchemaToTypeString(schema);\n if (typeStr === \"unknown\" || typeStr === \"{ }\" || typeStr === \"Record<string, unknown>\") {\n typeStr = \"never\";\n }\n typeProperties.push(`queries: ${typeStr}`);\n } catch {\n typeProperties.push(\"queries: never\");\n }\n }\n\n return `{\\n ${typeProperties.join(\";\\n \")};\\n}`;\n};\n\n/**\n * Helper function to convert AssertType/IAssert to JSON Schema\n */\nconst assertToJsonSchema = (assert: unknown): Record<string, unknown> => {\n try {\n const constraint =\n assert && typeof assert === \"object\" && \"getConstraint\" in assert\n ? (assert as { getConstraint: () => { toJsonSchema: () => Record<string, unknown> } }).getConstraint()\n : (assert as { toJsonSchema: () => Record<string, unknown> });\n return constraint.toJsonSchema();\n } catch {\n return { type: \"unknown\" };\n }\n};\n\n/**\n * Convert RouteConfigType to JSON documentation format\n *\n * @param config - Route configuration object\n * @returns JSON documentation object with route metadata and schemas\n *\n * @example\n * ```ts\n * const config = {\n * name: \"api.users.delete\",\n * path: \"/users/:id/emails/:emailId\",\n * method: \"DELETE\",\n * description: \"Delete a user by ID\",\n * params: {\n * id: Assert(\"string\"),\n * emailId: Assert(\"string\"),\n * },\n * payload: Assert({ name: \"string\" }),\n * queries: Assert({ limit: \"number\" }),\n * response: Assert({ success: \"boolean\", message: \"string\" }),\n * env: [Environment.LOCAL],\n * roles: [ERole.ADMIN],\n * isSocket: false,\n * };\n *\n * const jsonDoc = routeConfigToJsonDoc(config);\n * // Returns:\n * // {\n * // name: \"api.users.delete\",\n * // path: \"/users/:id/emails/:emailId\",\n * // method: \"DELETE\",\n * // description: \"Delete a user by ID\",\n * // isSocket: false,\n * // parameters: [\"id\", \"emailId\"],\n * // schemas: {\n * // params: { type: \"object\", properties: { id: { type: \"string\" }, emailId: { type: \"string\" } } },\n * // queries: { type: \"object\", properties: { limit: { type: \"number\" } } },\n * // payload: { type: \"object\", properties: { name: { type: \"string\" } } },\n * // response: { type: \"object\", properties: { success: { type: \"boolean\" }, message: { type: \"string\" } } }\n * // },\n * // security: {\n * // environments: [\"LOCAL\"],\n * // roles: [\"ADMIN\"],\n * // allowedIPs: [],\n * // allowedHosts: []\n * // }\n * // }\n * ```\n */\nexport const routeConfigToJsonDoc = (config: RouteConfigType): Record<string, unknown> => {\n const doc: Record<string, unknown> = {\n name: config.name,\n path: config.path,\n method: config.method,\n description: config.description,\n controller: config.controller.name,\n isSocket: config.isSocket,\n parameters: extractParameterNames(config.path),\n };\n\n const schemas: Record<string, Record<string, unknown>> = {};\n\n if (config.params) {\n const paramsSchema: Record<string, unknown> = {\n type: \"object\",\n properties: {},\n };\n\n for (const [key, assert] of Object.entries(config.params)) {\n const schema = assertToJsonSchema(assert);\n // Remove $schema from the schema object\n delete schema.$schema;\n // Add required field to each property\n schema.required = true;\n (paramsSchema.properties as Record<string, unknown>)[key] = schema;\n }\n\n schemas.params = paramsSchema;\n }\n\n if (config.queries) {\n const schema = assertToJsonSchema(config.queries);\n delete schema.$schema;\n if (schema.type === \"object\" && schema.properties) {\n const requiredFields = (schema.required as string[]) || [];\n const properties = schema.properties as Record<string, unknown>;\n for (const key of Object.keys(properties)) {\n const propSchema = properties[key] as Record<string, unknown>;\n propSchema.required = requiredFields.includes(key);\n }\n delete schema.required;\n }\n schemas.queries = schema;\n }\n\n if (config.payload) {\n const schema = assertToJsonSchema(config.payload);\n delete schema.$schema;\n if (schema.type === \"object\" && schema.properties) {\n const requiredFields = (schema.required as string[]) || [];\n const properties = schema.properties as Record<string, unknown>;\n for (const key of Object.keys(properties)) {\n const propSchema = properties[key] as Record<string, unknown>;\n propSchema.required = requiredFields.includes(key);\n }\n delete schema.required;\n }\n schemas.payload = schema;\n }\n\n if (config.response) {\n const schema = assertToJsonSchema(config.response);\n delete schema.$schema;\n if (schema.type === \"object\" && schema.properties) {\n const requiredFields = (schema.required as string[]) || [];\n const properties = schema.properties as Record<string, unknown>;\n for (const key of Object.keys(properties)) {\n const propSchema = properties[key] as Record<string, unknown>;\n propSchema.required = requiredFields.includes(key);\n }\n delete schema.required;\n }\n schemas.response = schema;\n }\n\n if (Object.keys(schemas).length > 0) {\n doc.schemas = schemas;\n }\n\n const security: Record<string, unknown> = {};\n\n if (config.env && config.env.length > 0) {\n security.environments = config.env;\n }\n\n if (config.roles && config.roles.length > 0) {\n security.roles = config.roles;\n }\n\n if (config.ip && config.ip.length > 0) {\n security.allowedIPs = config.ip;\n }\n\n if (config.host && config.host.length > 0) {\n security.allowedHosts = config.host;\n }\n\n if (Object.keys(security).length > 0) {\n doc.security = security;\n }\n\n return doc;\n};\n\n/**\n * Get the action part from route name\n * Example: \"api.users.delete\" -> \"delete\"\n */\nconst getRouteAction = (name: string): string => {\n const parts = name.split(\".\");\n return parts[parts.length - 1] || \"\";\n};\n\n/**\n * Build URL path by replacing route parameters with template literals\n * Example: \"/users/:id/emails/:emailId\" -> \"/users/${config.params.id}/emails/${config.params.emailId}\"\n */\nconst buildPathWithParams = (path: string, useConfigPrefix = true): string => {\n const prefix = useConfigPrefix ? \"config.params\" : \"params\";\n return path.replace(/:(\\w+)/g, (_match, param) => `\\${${prefix}.${param}}`);\n};\n\nexport const routeConfigToFetcherString = (config: RouteConfigType): string => {\n const action = getRouteAction(config.name);\n const typeName = `${action.charAt(0).toUpperCase() + action.slice(1)}RouteConfigType`;\n const className = `${toPascalCase(config.name)}Fetcher`;\n const method = config.method.toLowerCase();\n\n // Generate type definition\n const typeString = routeConfigToTypeString(config);\n\n const typeDefinition = `export type ${typeName} = ${typeString}`;\n\n // Build config parameter type\n const configProps: string[] = [];\n const hasParams = config.params && Object.keys(config.params).length > 0;\n const hasPayload = config.payload !== undefined;\n const hasQueries = config.queries !== undefined;\n\n if (hasParams) {\n configProps.push(`params: ${typeName}[\"params\"]`);\n }\n if (hasPayload) {\n configProps.push(`payload: ${typeName}[\"payload\"]`);\n }\n if (hasQueries) {\n configProps.push(`queries: ${typeName}[\"queries\"]`);\n }\n\n const configType = configProps.length > 0 ? `config: {\\n ${configProps.join(\";\\n \")};\\n }` : \"\";\n\n // Build URL with parameters\n const urlPath = buildPathWithParams(config.path);\n let urlExpression = `\\`${urlPath}\\``;\n\n // Add query string if queries exist\n if (hasQueries) {\n urlExpression = `\\`${urlPath}?\\${new URLSearchParams(config.queries as Record<string, string>).toString()}\\``;\n }\n\n // Build method call based on HTTP method\n const methodsWithPayload = [\"post\", \"put\", \"patch\"];\n let methodCall = \"\";\n\n if (methodsWithPayload.includes(method) && hasPayload) {\n methodCall = `return await fetcher.${method}<${typeName}[\"response\"]>(\\n ${urlExpression},\\n config.payload,\\n );`;\n } else {\n methodCall = `return await fetcher.${method}<${typeName}[\"response\"]>(\\n ${urlExpression},\\n );`;\n }\n\n // Generate class\n const classDefinition = `export class ${className} {\n constructor(private baseURL: string) {}\n\n public async ${action}(${configType}): Promise<ResponseDataType<${typeName}[\"response\"]>> {\n const fetcher = new Fetcher(this.baseURL);\n\n ${methodCall}\n }\n}`;\n\n const imports = `import type { ResponseDataType } from \"@ooneex/http-response\";\nimport { Fetcher } from \"@ooneex/fetcher\";`;\n\n return `${imports}\\n\\n${typeDefinition}\\n\\n${classDefinition}`;\n};\n\nexport const routeConfigToSocketString = (config: RouteConfigType): string => {\n const action = getRouteAction(config.name);\n const typeName = `${action.charAt(0).toUpperCase() + action.slice(1)}RouteConfigType`;\n const className = `${toPascalCase(config.name)}Socket`;\n\n // Generate type definition\n const typeString = routeConfigToTypeString(config);\n\n const typeDefinition = `export type ${typeName} = ${typeString}`;\n\n // Build config parameter type\n const hasParams = config.params && Object.keys(config.params).length > 0;\n const hasPayload = config.payload && Object.keys(config.payload).length > 0;\n const hasQueries = config.queries && Object.keys(config.queries).length > 0;\n\n // Method signature only includes params\n const configType = hasParams ? `params: ${typeName}[\"params\"]` : \"\";\n\n // Build URL with parameters\n const urlPath = buildPathWithParams(config.path, false);\n const urlExpression = `\\`\\${this.baseURL}${urlPath}\\``;\n\n // Build SendData type for Socket\n const sendDataTypeProps: string[] = [];\n if (hasPayload) {\n sendDataTypeProps.push(`payload: ${typeName}[\"payload\"]`);\n }\n if (hasQueries) {\n sendDataTypeProps.push(`queries: ${typeName}[\"queries\"]`);\n }\n sendDataTypeProps.push(\"language?: LocaleInfoType\");\n\n const sendDataType = sendDataTypeProps.length > 0 ? `{ ${sendDataTypeProps.join(\"; \")} }` : \"Record<string, unknown>\";\n\n // Generate class\n const classDefinition = `export class ${className} {\n constructor(private baseURL: string) {}\n\n public ${action}(${configType}): ISocket {\n const url = ${urlExpression};\n const socket = new Socket<${sendDataType}, ${typeName}[\"response\"]>(url);\n\n socket.onMessage((response) => {\n // TODO: Handle socket message event\n });\n\n socket.onOpen((event) => {\n // TODO: Handle socket open event\n });\n\n socket.onClose((event) => {\n // TODO: Handle socket close event\n });\n\n socket.onError((event, response) => {\n // TODO: Handle socket error event\n });\n\n return socket;\n }\n}`;\n\n const imports = `import type { LocaleInfoType } from \"@ooneex/translation\";\nimport { type ISocket, Socket } from \"@ooneex/socket/client\"`;\n\n return `${imports}\\n\\n${typeDefinition}\\n\\n${classDefinition}`;\n};\n\nexport const routeConfigToUseQueryString = (config: RouteConfigType, baseURL = \"\"): string => {\n const action = getRouteAction(config.name);\n const typeName = `${action.charAt(0).toUpperCase() + action.slice(1)}RouteConfigType`;\n const hookName = `use${toPascalCase(config.name)}`;\n const method = config.method.toUpperCase();\n\n // Generate type definition\n const typeString = routeConfigToTypeString(config);\n const typeDefinition = `export type ${typeName} = ${typeString}`;\n\n // Determine if this is a query or mutation based on HTTP method\n const queryMethods = [\"GET\", \"HEAD\", \"OPTIONS\"];\n const isQuery = queryMethods.includes(method);\n\n const hasParams = config.params && Object.keys(config.params).length > 0;\n const hasPayload = config.payload !== undefined;\n const hasQueries = config.queries !== undefined;\n\n if (isQuery) {\n // Generate useQuery hook\n const configProps: string[] = [];\n\n if (hasParams) {\n configProps.push(`params: ${typeName}[\"params\"]`);\n }\n if (hasQueries) {\n configProps.push(`queries?: ${typeName}[\"queries\"]`);\n }\n\n const configParam = configProps.length > 0 ? `config: {\\n ${configProps.join(\";\\n \")};\\n}` : \"\";\n\n // Build query key\n const queryKeyParts = config.name.split(\".\");\n const queryKeyBase = queryKeyParts.map((part) => `'${part}'`).join(\", \");\n let queryKey = `[${queryKeyBase}`;\n\n if (hasParams && config.params) {\n const paramKeys = Object.keys(config.params);\n for (const paramKey of paramKeys) {\n queryKey += `, config.params.${paramKey}`;\n }\n }\n\n if (hasQueries) {\n queryKey += \", config.queries\";\n }\n\n queryKey += \"]\";\n\n // Build URL\n // Build URL with parameters\n const urlPath = buildPathWithParams(config.path);\n let urlExpression = `\\`${urlPath}\\``;\n\n // Add query string if queries exist\n if (hasQueries) {\n urlExpression = `\\`${urlPath}?\\${new URLSearchParams(config.queries as Record<string, string>).toString()}\\``;\n }\n\n // Build fetcher call\n const fetcherMethod = method.toLowerCase();\n const fetchCall = `const fetcher = new Fetcher('${baseURL}');\n const url = ${urlExpression};\n\n return await fetcher.${fetcherMethod}<${typeName}[\"response\"]>(url);`;\n\n const hookDefinition = `export const ${hookName} = (${configParam}) => {\n return useQuery({\n queryKey: ${queryKey},\n queryFn: async () => {\n ${fetchCall}\n },\n });\n};`;\n\n const imports = `import { useQuery } from '@tanstack/react-query';\nimport { Fetcher } from '@ooneex/fetcher';`;\n\n return `${imports}\\n\\n${typeDefinition}\\n\\n${hookDefinition}`;\n }\n\n // Generate useMutation hook\n const configProps: string[] = [];\n\n if (hasParams) {\n configProps.push(`params: ${typeName}[\"params\"]`);\n }\n if (hasPayload) {\n configProps.push(`payload: ${typeName}[\"payload\"]`);\n }\n if (hasQueries) {\n configProps.push(`queries?: ${typeName}[\"queries\"]`);\n }\n\n const mutationConfigType =\n configProps.length > 0 ? `config: {\\n ${configProps.join(\";\\n \")};\\n }` : \"config?: Record<string, never>\";\n\n // Build URL\n const urlPath = buildPathWithParams(config.path);\n let urlExpression = `\\`${urlPath}\\``;\n\n // Add query string if queries exist\n if (hasQueries) {\n urlExpression = `\\`${urlPath}\\${config.queries ? \\`?\\${new URLSearchParams(config.queries as Record<string, string>).toString()}\\` : ''}\\``;\n }\n\n // Build fetcher call\n const methodsWithPayload = [\"POST\", \"PUT\", \"PATCH\"];\n const hasFetchBody = methodsWithPayload.includes(method) && hasPayload;\n const fetcherMethod = method.toLowerCase();\n\n let fetchCall = `const fetcher = new Fetcher('${baseURL}');\n const url = ${urlExpression};\n\n `;\n\n if (hasFetchBody) {\n fetchCall += `return await fetcher.${fetcherMethod}<${typeName}[\"response\"]>(url, config.payload);`;\n } else {\n fetchCall += `return await fetcher.${fetcherMethod}<${typeName}[\"response\"]>(url);`;\n }\n\n const hookDefinition = `export const ${hookName} = () => {\n const mutation = useMutation({\n mutationFn: async (${mutationConfigType}) => {\n ${fetchCall}\n },\n });\n\n return mutation;\n};`;\n\n const imports = `import { useMutation } from '@tanstack/react-query';\nimport { Fetcher } from '@ooneex/fetcher';`;\n\n return `${imports}\\n\\n${typeDefinition}\\n\\n${hookDefinition}`;\n};\n"
|
|
9
|
+
],
|
|
10
|
+
"mappings": "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",
|
|
11
|
+
"debugId": "F38C937393AEFC1764756E2164756E21",
|
|
12
|
+
"names": []
|
|
13
|
+
}
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ooneex/routing",
|
|
3
|
+
"description": "",
|
|
4
|
+
"version": "0.0.2",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist",
|
|
8
|
+
"LICENSE",
|
|
9
|
+
"README.md",
|
|
10
|
+
"package.json"
|
|
11
|
+
],
|
|
12
|
+
"module": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"import": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"default": "./dist/index.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"./package.json": "./package.json"
|
|
22
|
+
},
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"scripts": {
|
|
25
|
+
"test": "bun test tests",
|
|
26
|
+
"build": "bunup",
|
|
27
|
+
"lint": "tsgo --noEmit && bunx biome lint",
|
|
28
|
+
"publish:prod": "bun publish --tolerate-republish --access public",
|
|
29
|
+
"publish:pack": "bun pm pack --destination ./dist",
|
|
30
|
+
"publish:dry": "bun publish --dry-run"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@ooneex/utils": "0.0.8",
|
|
34
|
+
"@ooneex/container": "0.0.1",
|
|
35
|
+
"@ooneex/exception": "0.0.1",
|
|
36
|
+
"@ooneex/http-status": "0.0.1"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@ooneex/app-env": "0.0.1",
|
|
40
|
+
"@ooneex/controller": "0.0.1",
|
|
41
|
+
"@ooneex/role": "0.0.1",
|
|
42
|
+
"@ooneex/types": "0.0.1",
|
|
43
|
+
"@ooneex/validation": "0.0.1"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {}
|
|
46
|
+
}
|