@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 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
@@ -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
+ }