@arikajs/dispatcher 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ArikaJs
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,224 @@
1
+ ## Arika Dispatcher
2
+
3
+ `@arikajs/dispatcher` is the **execution engine** of the ArikaJS ecosystem.
4
+
5
+ Before the response is sent back to the client, this package handles the **journey from route to result**:
6
+
7
+ - Route handler resolution (Closures or Controllers)
8
+ - Dependency Injection for controllers
9
+ - Method invocation with parameter injection
10
+ - Middleware pipeline execution
11
+ - Response normalization
12
+
13
+ If the following works cleanly, the dispatcher is considered correct:
14
+
15
+ ```ts
16
+ const dispatcher = new Dispatcher(container);
17
+ const response = await dispatcher.dispatch(matchedRoute, request, responseInstance);
18
+ ```
19
+
20
+ Arika Dispatcher is to ArikaJS what `illuminate/routing`’s dispatcher is to Laravel: it takes a route and makes things happen.
21
+
22
+ ---
23
+
24
+ ### Status
25
+
26
+ - **Stage**: Experimental / v0.x
27
+ - **Scope (v0.x)**:
28
+ - Controller & Closure resolution
29
+ - Method parameter injection (Request + Route Params)
30
+ - Basic middleware pipeline
31
+ - Response normalization (String, Object, Response)
32
+ - **Out of scope (for this package)**:
33
+ - Route matching (see `@arikajs/router`)
34
+ - Request parsing (see `@arikajs/http`)
35
+ - Template rendering
36
+
37
+ The goal of this package is to be the **reliable executor** of application logic.
38
+
39
+ ---
40
+
41
+ ## Features
42
+
43
+ - **Handler Resolution**
44
+ - Support for basic closures: `(req) => 'Hello'`
45
+ - Support for class-based controllers: `[UserController, 'show']`
46
+ - Automatic instantiation of controllers via Service Container
47
+
48
+ - **Method Invocation**
49
+ - Resolves method dependencies
50
+ - Injects `Request` object automatically
51
+ - Maps route parameters (e.g., `{id}`) to method arguments
52
+
53
+ - **Middleware Pipeline**
54
+ - Executes route-specific middleware
55
+ - Onion-style execution flow (nested `next()` calls)
56
+ - Asynchronous middleware support
57
+ - Container-based middleware resolution
58
+
59
+ - **Response Normalization**
60
+ - Automatically converts return values to HTTP responses
61
+ - Objects/Arrays → JSON response
62
+ - Strings → Plain text response
63
+ - `null`/`undefined` → 204 No Content
64
+
65
+ ---
66
+
67
+ ## Installation
68
+
69
+ ```bash
70
+ npm install @arikajs/dispatcher
71
+ # or
72
+ yarn add @arikajs/dispatcher
73
+ # or
74
+ pnpm add @arikajs/dispatcher
75
+ ```
76
+
77
+ This package is written in TypeScript and ships with type definitions.
78
+
79
+ ---
80
+
81
+ ## Quick Start
82
+
83
+ ### 1. Simple Dispatching
84
+
85
+ ```ts
86
+ import { Dispatcher } from '@arikajs/dispatcher';
87
+
88
+ const dispatcher = new Dispatcher();
89
+ const response = await dispatcher.dispatch(matchedRoute, request, response);
90
+ ```
91
+
92
+ ### 2. Using Controllers with DI
93
+
94
+ ```ts
95
+ import { Dispatcher } from '@arikajs/dispatcher';
96
+ import { Container } from '@arikajs/foundation';
97
+
98
+ const container = new Container();
99
+ const dispatcher = new Dispatcher(container);
100
+
101
+ // Dispatch to [UserController, 'index']
102
+ const response = await dispatcher.dispatch(matchedRoute, request, response);
103
+ ```
104
+
105
+ ---
106
+
107
+ ## Dispatcher
108
+
109
+ The `Dispatcher` class is the central coordinator that manages the lifecycle of a request dispatch.
110
+
111
+ Core responsibilities:
112
+
113
+ - Resolves the handler (Closure or Controller)
114
+ - Manages the **middleware pipeline**
115
+ - Coordinates the **invocation** of the handler
116
+ - Ensures the return value is **normalized** into a Response
117
+
118
+ Minimal API:
119
+
120
+ ```ts
121
+ class Dispatcher {
122
+ constructor(container?: Container);
123
+
124
+ setContainer(container: Container): this;
125
+ dispatch(matchedRoute: MatchedRoute, request: Request, response: Response): Promise<Response>;
126
+ }
127
+ ```
128
+
129
+ Typical usage:
130
+
131
+ ```ts
132
+ import { Dispatcher } from '@arikajs/dispatcher';
133
+
134
+ const dispatcher = new Dispatcher(app.container);
135
+ const response = await dispatcher.dispatch(matched, request, response);
136
+ ```
137
+
138
+ ---
139
+
140
+ ## Middleware Pipeline
141
+
142
+ The `MiddlewarePipeline` executes middleware in an "onion" pattern, where each layer can perform logic before and after the next layer.
143
+
144
+ Minimal API:
145
+
146
+ ```ts
147
+ pipeline.use(middleware);
148
+ pipeline.handle(request, destination);
149
+ ```
150
+
151
+ ### Usage
152
+
153
+ ```ts
154
+ const pipeline = new MiddlewarePipeline(container);
155
+
156
+ pipeline.use(async (req, next) => {
157
+ const res = await next(req);
158
+ res.header('X-Processed-By', 'Arika');
159
+ return res;
160
+ });
161
+ ```
162
+
163
+ ---
164
+
165
+ ## Response Resolver
166
+
167
+ The `ResponseResolver` ensures that your controller methods can stay clean by returning plain data.
168
+
169
+ Conversion Rules:
170
+
171
+ - **Objects**: Automatically converted to JSON via `response.json()`
172
+ - **Strings/Buffers**: Sent as the body via `response.send()`
173
+ - **null/undefined**: Sets status 204 (No Content)
174
+ - **Response**: Returned as-is
175
+
176
+ ---
177
+
178
+ ## Project Structure (recommended)
179
+
180
+ Inside the `arika-dispatcher` repository:
181
+
182
+ - `src/`
183
+ - `Dispatcher.ts` – Main coordinator
184
+ - `ControllerResolver.ts` – DI-aware handler resolution
185
+ - `MethodInvoker.ts` – Dynamic method execution
186
+ - `ResponseResolver.ts` – Logic for normalizing return values
187
+ - `MiddlewarePipeline.ts` – The execution stack for middleware
188
+ - `index.ts` – Public exports
189
+
190
+ ---
191
+
192
+ ## Versioning & Stability
193
+
194
+ - While in **v0.x**, the API may change between minor versions.
195
+ - Once the API stabilizes, `@arikajs/dispatcher` will move to **v1.0** and follow **semver** strictly.
196
+
197
+ ---
198
+
199
+ ## Contributing
200
+
201
+ Contributions are welcome, especially around:
202
+
203
+ - Advanced dependency injection in controller methods
204
+ - Performance optimizations for middleware execution
205
+ - Enhanced error handling hooks
206
+ - Support for attribute-based routing
207
+
208
+ Before submitting a PR:
209
+
210
+ - Run the test suite
211
+ - Add tests for any new behavior
212
+ - Keep the public API focused and well-documented
213
+
214
+ ---
215
+
216
+ ## License
217
+
218
+ `@arikajs/dispatcher` is open-sourced software licensed under the **MIT license**.
219
+
220
+ ---
221
+
222
+ ## Philosophy
223
+
224
+ > “Routing decides the path. Dispatcher executes the journey.”
@@ -0,0 +1,4 @@
1
+ export interface Container {
2
+ make<T = any>(token: any): T;
3
+ }
4
+ //# sourceMappingURL=Container.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Container.d.ts","sourceRoot":"","sources":["../../src/Contracts/Container.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,SAAS;IACtB,IAAI,CAAC,CAAC,GAAG,GAAG,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,CAAC;CAChC"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=Container.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Container.js","sourceRoot":"","sources":["../../src/Contracts/Container.ts"],"names":[],"mappings":""}
@@ -0,0 +1,10 @@
1
+ export interface Request {
2
+ [key: string]: any;
3
+ }
4
+ export interface Response {
5
+ status(code: number): this;
6
+ send(data: any): this;
7
+ json(data: any): this;
8
+ [key: string]: any;
9
+ }
10
+ //# sourceMappingURL=Http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Http.d.ts","sourceRoot":"","sources":["../../src/Contracts/Http.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,OAAO;IACpB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ;IACrB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,IAAI,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI,CAAC;IACtB,IAAI,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI,CAAC;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACtB"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=Http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Http.js","sourceRoot":"","sources":["../../src/Contracts/Http.ts"],"names":[],"mappings":""}
@@ -0,0 +1,9 @@
1
+ export interface MatchedRoute {
2
+ route: {
3
+ handler: any;
4
+ middleware?: any[];
5
+ [key: string]: any;
6
+ };
7
+ params: Record<string, string>;
8
+ }
9
+ //# sourceMappingURL=Router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Router.d.ts","sourceRoot":"","sources":["../../src/Contracts/Router.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,YAAY;IACzB,KAAK,EAAE;QACH,OAAO,EAAE,GAAG,CAAC;QACb,UAAU,CAAC,EAAE,GAAG,EAAE,CAAC;QACnB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACtB,CAAC;IACF,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=Router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Router.js","sourceRoot":"","sources":["../../src/Contracts/Router.ts"],"names":[],"mappings":""}
@@ -0,0 +1,12 @@
1
+ export declare class ControllerResolver {
2
+ private container;
3
+ constructor(container: any);
4
+ /**
5
+ * Resolve the controller instance and method name.
6
+ */
7
+ resolve(handler: any[]): {
8
+ controller: any;
9
+ method: string;
10
+ };
11
+ }
12
+ //# sourceMappingURL=ControllerResolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ControllerResolver.d.ts","sourceRoot":"","sources":["../src/ControllerResolver.ts"],"names":[],"mappings":"AAAA,qBAAa,kBAAkB;IACf,OAAO,CAAC,SAAS;gBAAT,SAAS,EAAE,GAAG;IAElC;;OAEG;IACI,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG;QAAE,UAAU,EAAE,GAAG,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;CAiBtE"}
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ControllerResolver = void 0;
4
+ class ControllerResolver {
5
+ constructor(container) {
6
+ this.container = container;
7
+ }
8
+ /**
9
+ * Resolve the controller instance and method name.
10
+ */
11
+ resolve(handler) {
12
+ const [controllerClass, method] = handler;
13
+ if (!this.container) {
14
+ throw new Error('Container required for controller resolution.');
15
+ }
16
+ const controller = this.container.make(controllerClass);
17
+ if (typeof controller[method] !== 'function') {
18
+ throw new Error(`Controller method "${method}" not found on ${controllerClass.name || controllerClass}.`);
19
+ }
20
+ return { controller, method };
21
+ }
22
+ }
23
+ exports.ControllerResolver = ControllerResolver;
24
+ //# sourceMappingURL=ControllerResolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ControllerResolver.js","sourceRoot":"","sources":["../src/ControllerResolver.ts"],"names":[],"mappings":";;;AAAA,MAAa,kBAAkB;IAC3B,YAAoB,SAAc;QAAd,cAAS,GAAT,SAAS,CAAK;IAAI,CAAC;IAEvC;;OAEG;IACI,OAAO,CAAC,OAAc;QACzB,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC;QAE1C,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;QACrE,CAAC;QAED,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,eAAe,CAAQ,CAAC;QAE/D,IAAI,OAAO,UAAU,CAAC,MAAM,CAAC,KAAK,UAAU,EAAE,CAAC;YAC3C,MAAM,IAAI,KAAK,CACX,sBAAsB,MAAM,kBAAkB,eAAe,CAAC,IAAI,IAAI,eAAe,GAAG,CAC3F,CAAC;QACN,CAAC;QAED,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;IAClC,CAAC;CACJ;AAvBD,gDAuBC"}
@@ -0,0 +1,37 @@
1
+ import { Request, Response } from './Contracts/Http';
2
+ import { MatchedRoute } from './Contracts/Router';
3
+ export declare class Dispatcher {
4
+ private container?;
5
+ private controllerResolver?;
6
+ private invoker;
7
+ private responseResolver;
8
+ private middlewareGroups;
9
+ private routeMiddleware;
10
+ private parameterBinders;
11
+ constructor(container?: any | undefined);
12
+ /**
13
+ * Set the container for resolving controllers.
14
+ */
15
+ setContainer(container: any): this;
16
+ /**
17
+ * Set the middleware groups mapping.
18
+ */
19
+ setMiddlewareGroups(groups: Record<string, any[]>): this;
20
+ /**
21
+ * Set the route middleware mapping.
22
+ */
23
+ setRouteMiddleware(middleware: Record<string, any>): this;
24
+ /**
25
+ * Register a route parameter binder.
26
+ */
27
+ bind(key: string, resolver: any): this;
28
+ /**
29
+ * Dispatch the matched route to its handler.
30
+ */
31
+ dispatch(matchedRoute: MatchedRoute, request: Request, response: Response): Promise<Response>;
32
+ /**
33
+ * Resolve route parameters using registered binders.
34
+ */
35
+ private resolveParameters;
36
+ }
37
+ //# sourceMappingURL=Dispatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Dispatcher.d.ts","sourceRoot":"","sources":["../src/Dispatcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAMlD,qBAAa,UAAU;IAQP,OAAO,CAAC,SAAS,CAAC;IAP9B,OAAO,CAAC,kBAAkB,CAAC,CAAqB;IAChD,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,gBAAgB,CAA6B;IACrD,OAAO,CAAC,eAAe,CAA2B;IAClD,OAAO,CAAC,gBAAgB,CAAwD;gBAE5D,SAAS,CAAC,EAAE,GAAG,YAAA;IAQnC;;OAEG;IACI,YAAY,CAAC,SAAS,EAAE,GAAG,GAAG,IAAI;IAMzC;;OAEG;IACI,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,IAAI;IAK/D;;OAEG;IACI,kBAAkB,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAKhE;;OAEG;IACI,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,IAAI;IAS7C;;OAEG;IACU,QAAQ,CACjB,YAAY,EAAE,YAAY,EAC1B,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,QAAQ,GACnB,OAAO,CAAC,QAAQ,CAAC;IAyCpB;;OAEG;YACW,iBAAiB;CAYlC"}
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Dispatcher = void 0;
4
+ const ControllerResolver_1 = require("./ControllerResolver");
5
+ const MethodInvoker_1 = require("./MethodInvoker");
6
+ const ResponseResolver_1 = require("./ResponseResolver");
7
+ const middleware_1 = require("@arikajs/middleware");
8
+ class Dispatcher {
9
+ constructor(container) {
10
+ this.container = container;
11
+ this.middlewareGroups = {};
12
+ this.routeMiddleware = {};
13
+ this.parameterBinders = new Map();
14
+ this.invoker = new MethodInvoker_1.MethodInvoker();
15
+ this.responseResolver = new ResponseResolver_1.ResponseResolver();
16
+ if (container) {
17
+ this.controllerResolver = new ControllerResolver_1.ControllerResolver(container);
18
+ }
19
+ }
20
+ /**
21
+ * Set the container for resolving controllers.
22
+ */
23
+ setContainer(container) {
24
+ this.container = container;
25
+ this.controllerResolver = new ControllerResolver_1.ControllerResolver(container);
26
+ return this;
27
+ }
28
+ /**
29
+ * Set the middleware groups mapping.
30
+ */
31
+ setMiddlewareGroups(groups) {
32
+ this.middlewareGroups = groups;
33
+ return this;
34
+ }
35
+ /**
36
+ * Set the route middleware mapping.
37
+ */
38
+ setRouteMiddleware(middleware) {
39
+ this.routeMiddleware = middleware;
40
+ return this;
41
+ }
42
+ /**
43
+ * Register a route parameter binder.
44
+ */
45
+ bind(key, resolver) {
46
+ if (resolver && typeof resolver.findOrFail === 'function') {
47
+ this.parameterBinders.set(key, (value) => resolver.findOrFail(value));
48
+ }
49
+ else {
50
+ this.parameterBinders.set(key, resolver);
51
+ }
52
+ return this;
53
+ }
54
+ /**
55
+ * Dispatch the matched route to its handler.
56
+ */
57
+ async dispatch(matchedRoute, request, response) {
58
+ const { route, params } = matchedRoute;
59
+ const handler = route.handler;
60
+ // 0. Resolve Route Parameters (Model Binding)
61
+ const resolvedParams = await this.resolveParameters(params);
62
+ // 1. Resolve Handler
63
+ let resolvedHandler;
64
+ if (Array.isArray(handler)) {
65
+ if (!this.controllerResolver) {
66
+ throw new Error('Container required for controller resolution.');
67
+ }
68
+ resolvedHandler = this.controllerResolver.resolve(handler);
69
+ }
70
+ else if (typeof handler === 'function') {
71
+ resolvedHandler = handler;
72
+ }
73
+ else {
74
+ throw new Error('Invalid route handler.');
75
+ }
76
+ // 2. Prepare Middleware Pipeline
77
+ const pipeline = new middleware_1.Pipeline(this.container);
78
+ pipeline.setMiddlewareGroups(this.middlewareGroups);
79
+ pipeline.setAliases(this.routeMiddleware);
80
+ // Add route-level middleware
81
+ if (route.middleware && route.middleware.length > 0) {
82
+ pipeline.pipe(route.middleware);
83
+ }
84
+ // 3. Execute Pipeline
85
+ return await pipeline.handle(request, async (req) => {
86
+ // 4. Invoke Handler
87
+ const result = await this.invoker.invoke(resolvedHandler, req, resolvedParams);
88
+ // 5. Normalize Response
89
+ return this.responseResolver.resolve(result, response);
90
+ }, response);
91
+ }
92
+ /**
93
+ * Resolve route parameters using registered binders.
94
+ */
95
+ async resolveParameters(params) {
96
+ const resolved = { ...params };
97
+ for (const [key, value] of Object.entries(params)) {
98
+ if (this.parameterBinders.has(key)) {
99
+ const resolver = this.parameterBinders.get(key);
100
+ resolved[key] = await resolver(value);
101
+ }
102
+ }
103
+ return resolved;
104
+ }
105
+ }
106
+ exports.Dispatcher = Dispatcher;
107
+ //# sourceMappingURL=Dispatcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Dispatcher.js","sourceRoot":"","sources":["../src/Dispatcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { Request } from './Contracts/Http';
2
+ export declare class MethodInvoker {
3
+ /**
4
+ * Invoke the handler (closure or controller method) with injected parameters.
5
+ */
6
+ invoke(handler: Function | {
7
+ controller: any;
8
+ method: string;
9
+ }, request: Request, params: Record<string, any>): Promise<any>;
10
+ }
11
+ //# sourceMappingURL=MethodInvoker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MethodInvoker.d.ts","sourceRoot":"","sources":["../src/MethodInvoker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE3C,qBAAa,aAAa;IACtB;;OAEG;IACU,MAAM,CACf,OAAO,EAAE,QAAQ,GAAG;QAAE,UAAU,EAAE,GAAG,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EACvD,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC5B,OAAO,CAAC,GAAG,CAAC;CAQlB"}
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MethodInvoker = void 0;
4
+ class MethodInvoker {
5
+ /**
6
+ * Invoke the handler (closure or controller method) with injected parameters.
7
+ */
8
+ async invoke(handler, request, params) {
9
+ if (typeof handler === 'function') {
10
+ return await handler(request, ...Object.values(params));
11
+ }
12
+ const { controller, method } = handler;
13
+ return await controller[method](request, ...Object.values(params));
14
+ }
15
+ }
16
+ exports.MethodInvoker = MethodInvoker;
17
+ //# sourceMappingURL=MethodInvoker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MethodInvoker.js","sourceRoot":"","sources":["../src/MethodInvoker.ts"],"names":[],"mappings":";;;AAEA,MAAa,aAAa;IACtB;;OAEG;IACI,KAAK,CAAC,MAAM,CACf,OAAuD,EACvD,OAAgB,EAChB,MAA2B;QAE3B,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE,CAAC;YAChC,OAAO,MAAM,OAAO,CAAC,OAAO,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;QAC5D,CAAC;QAED,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QACvC,OAAO,MAAM,UAAU,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IACvE,CAAC;CACJ;AAhBD,sCAgBC"}
@@ -0,0 +1,8 @@
1
+ import { Response } from './Contracts/Http';
2
+ export declare class ResponseResolver {
3
+ /**
4
+ * Resolve and normalize the handler return value into a Response object.
5
+ */
6
+ resolve(value: any, response: Response): Response;
7
+ }
8
+ //# sourceMappingURL=ResponseResolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ResponseResolver.d.ts","sourceRoot":"","sources":["../src/ResponseResolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,qBAAa,gBAAgB;IACzB;;OAEG;IACI,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,GAAG,QAAQ;CAuB3D"}
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ResponseResolver = void 0;
4
+ class ResponseResolver {
5
+ /**
6
+ * Resolve and normalize the handler return value into a Response object.
7
+ */
8
+ resolve(value, response) {
9
+ // If it's already a Response object (basic duck typing check for framework response)
10
+ if (value && typeof value.send === 'function' && typeof value.status === 'function') {
11
+ return value;
12
+ }
13
+ if (typeof value === 'object' && value !== null) {
14
+ return response.json(value);
15
+ }
16
+ // If it's a string, return as text or buffer
17
+ if (typeof value === 'string' || Buffer.isBuffer(value)) {
18
+ return response.send(value);
19
+ }
20
+ // Handle null or undefined (empty response)
21
+ if (value === null || value === undefined) {
22
+ return response.status(204).send('');
23
+ }
24
+ // Default to string conversion
25
+ return response.send(String(value));
26
+ }
27
+ }
28
+ exports.ResponseResolver = ResponseResolver;
29
+ //# sourceMappingURL=ResponseResolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ResponseResolver.js","sourceRoot":"","sources":["../src/ResponseResolver.ts"],"names":[],"mappings":";;;AAEA,MAAa,gBAAgB;IACzB;;OAEG;IACI,OAAO,CAAC,KAAU,EAAE,QAAkB;QACzC,qFAAqF;QACrF,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,UAAU,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;YAClF,OAAO,KAAK,CAAC;QACjB,CAAC;QAED,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YAC9C,OAAO,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;QAED,6CAA6C;QAC7C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACtD,OAAO,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;QAED,4CAA4C;QAC5C,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxC,OAAO,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACzC,CAAC;QAED,+BAA+B;QAC/B,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACxC,CAAC;CACJ;AA3BD,4CA2BC"}
@@ -0,0 +1,5 @@
1
+ export * from './Dispatcher';
2
+ export * from './ControllerResolver';
3
+ export * from './MethodInvoker';
4
+ export * from './ResponseResolver';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./Dispatcher"), exports);
18
+ __exportStar(require("./ControllerResolver"), exports);
19
+ __exportStar(require("./MethodInvoker"), exports);
20
+ __exportStar(require("./ResponseResolver"), exports);
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+CAA6B;AAC7B,uDAAqC;AACrC,kDAAgC;AAChC,qDAAmC"}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@arikajs/dispatcher",
3
+ "version": "0.1.0",
4
+ "description": "Request dispatching and handler execution layer of the ArikaJS framework.",
5
+ "license": "MIT",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "tsc -p tsconfig.json",
10
+ "clean": "rm -rf dist",
11
+ "prepare": "echo skip",
12
+ "test": "npx tsx tests/Dispatcher.test.ts",
13
+ "test:watch": "npx tsx --watch tests/Dispatcher.test.ts"
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "keywords": [
19
+ "arika",
20
+ "arika-js",
21
+ "framework",
22
+ "dispatcher",
23
+ "controller",
24
+ "middleware"
25
+ ],
26
+ "engines": {
27
+ "node": ">=20.0.0"
28
+ },
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/arikajs/dispatcher.git"
32
+ },
33
+ "bugs": {
34
+ "url": "https://github.com/arikajs/dispatcher/issues"
35
+ },
36
+ "homepage": "https://github.com/arikajs/dispatcher#readme",
37
+ "dependencies": {},
38
+ "devDependencies": {
39
+ "@types/node": "^20.11.24",
40
+ "typescript": "^5.3.3"
41
+ },
42
+ "author": "Prakash Tank"
43
+ }