@kurdel/core 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +54 -0
- package/lib/app/app-config.d.ts +9 -0
- package/lib/app/app-config.js +2 -0
- package/lib/app/app-config.js.map +1 -0
- package/lib/app/app-module.d.ts +75 -0
- package/lib/app/app-module.js +2 -0
- package/lib/app/app-module.js.map +1 -0
- package/lib/app/app-tokens.d.ts +4 -0
- package/lib/app/app-tokens.js +6 -0
- package/lib/app/app-tokens.js.map +1 -0
- package/lib/app/application.d.ts +23 -0
- package/lib/app/application.js +2 -0
- package/lib/app/application.js.map +1 -0
- package/lib/app/index.d.ts +6 -0
- package/lib/app/index.js +7 -0
- package/lib/app/index.js.map +1 -0
- package/lib/app/lifecycle.d.ts +3 -0
- package/lib/app/lifecycle.js +2 -0
- package/lib/app/lifecycle.js.map +1 -0
- package/lib/app/module-priority.d.ts +33 -0
- package/lib/app/module-priority.js +35 -0
- package/lib/app/module-priority.js.map +1 -0
- package/lib/db/index.d.ts +1 -0
- package/lib/db/index.js +2 -0
- package/lib/db/index.js.map +1 -0
- package/lib/db/model.d.ts +23 -0
- package/lib/db/model.js +17 -0
- package/lib/db/model.js.map +1 -0
- package/lib/http/errors/http-error.d.ts +5 -0
- package/lib/http/errors/http-error.js +9 -0
- package/lib/http/errors/http-error.js.map +1 -0
- package/lib/http/errors/index.d.ts +2 -0
- package/lib/http/errors/index.js +3 -0
- package/lib/http/errors/index.js.map +1 -0
- package/lib/http/errors/validation-error.d.ts +9 -0
- package/lib/http/errors/validation-error.js +12 -0
- package/lib/http/errors/validation-error.js.map +1 -0
- package/lib/http/http-context.d.ts +107 -0
- package/lib/http/http-context.js +2 -0
- package/lib/http/http-context.js.map +1 -0
- package/lib/http/http-module.d.ts +61 -0
- package/lib/http/http-module.js +2 -0
- package/lib/http/http-module.js.map +1 -0
- package/lib/http/http-tokens.d.ts +10 -0
- package/lib/http/http-tokens.js +11 -0
- package/lib/http/http-tokens.js.map +1 -0
- package/lib/http/index.d.ts +9 -0
- package/lib/http/index.js +10 -0
- package/lib/http/index.js.map +1 -0
- package/lib/http/middleware/index.d.ts +3 -0
- package/lib/http/middleware/index.js +4 -0
- package/lib/http/middleware/index.js.map +1 -0
- package/lib/http/middleware/middleware-registration.d.ts +25 -0
- package/lib/http/middleware/middleware-registration.js +2 -0
- package/lib/http/middleware/middleware-registration.js.map +1 -0
- package/lib/http/middleware/middleware-registry.d.ts +15 -0
- package/lib/http/middleware/middleware-registry.js +2 -0
- package/lib/http/middleware/middleware-registry.js.map +1 -0
- package/lib/http/middleware/middleware.d.ts +2 -0
- package/lib/http/middleware/middleware.js +2 -0
- package/lib/http/middleware/middleware.js.map +1 -0
- package/lib/http/response/action-result.d.ts +69 -0
- package/lib/http/response/action-result.js +2 -0
- package/lib/http/response/action-result.js.map +1 -0
- package/lib/http/response/http-results.d.ts +17 -0
- package/lib/http/response/http-results.js +46 -0
- package/lib/http/response/http-results.js.map +1 -0
- package/lib/http/response/index.d.ts +3 -0
- package/lib/http/response/index.js +4 -0
- package/lib/http/response/index.js.map +1 -0
- package/lib/http/response/response-renderer.d.ts +6 -0
- package/lib/http/response/response-renderer.js +2 -0
- package/lib/http/response/response-renderer.js.map +1 -0
- package/lib/http/routing/controller-config.d.ts +24 -0
- package/lib/http/routing/controller-config.js +2 -0
- package/lib/http/routing/controller-config.js.map +1 -0
- package/lib/http/routing/controller-resolver.d.ts +5 -0
- package/lib/http/routing/controller-resolver.js +2 -0
- package/lib/http/routing/controller-resolver.js.map +1 -0
- package/lib/http/routing/controller.d.ts +22 -0
- package/lib/http/routing/controller.js +47 -0
- package/lib/http/routing/controller.js.map +1 -0
- package/lib/http/routing/index.d.ts +7 -0
- package/lib/http/routing/index.js +8 -0
- package/lib/http/routing/index.js.map +1 -0
- package/lib/http/routing/route-match.d.ts +27 -0
- package/lib/http/routing/route-match.js +2 -0
- package/lib/http/routing/route-match.js.map +1 -0
- package/lib/http/routing/route-schema.d.ts +16 -0
- package/lib/http/routing/route-schema.js +2 -0
- package/lib/http/routing/route-schema.js.map +1 -0
- package/lib/http/routing/route.d.ts +112 -0
- package/lib/http/routing/route.js +21 -0
- package/lib/http/routing/route.js.map +1 -0
- package/lib/http/routing/router.d.ts +27 -0
- package/lib/http/routing/router.js +2 -0
- package/lib/http/routing/router.js.map +1 -0
- package/lib/http/server/index.d.ts +2 -0
- package/lib/http/server/index.js +3 -0
- package/lib/http/server/index.js.map +1 -0
- package/lib/http/server/running-server.d.ts +8 -0
- package/lib/http/server/running-server.js +2 -0
- package/lib/http/server/running-server.js.map +1 -0
- package/lib/http/server/server-adapter.d.ts +82 -0
- package/lib/http/server/server-adapter.js +2 -0
- package/lib/http/server/server-adapter.js.map +1 -0
- package/lib/http/types.d.ts +10 -0
- package/lib/http/types.js +2 -0
- package/lib/http/types.js.map +1 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.js +6 -0
- package/lib/index.js.map +1 -0
- package/lib/template/index.d.ts +2 -0
- package/lib/template/index.js +3 -0
- package/lib/template/index.js.map +1 -0
- package/lib/template/template-engine.d.ts +3 -0
- package/lib/template/template-engine.js +2 -0
- package/lib/template/template-engine.js.map +1 -0
- package/lib/template/template-tokens.d.ts +2 -0
- package/lib/template/template-tokens.js +3 -0
- package/lib/template/template-tokens.js.map +1 -0
- package/lib/tokens/index.d.ts +1 -0
- package/lib/tokens/index.js +2 -0
- package/lib/tokens/index.js.map +1 -0
- package/lib/tokens/tokens.d.ts +12 -0
- package/lib/tokens/tokens.js +9 -0
- package/lib/tokens/tokens.js.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createGlobalToken } from '@kurdel/ioc';
|
|
2
|
+
export const HTTP_TOKENS = {
|
|
3
|
+
ServerAdapter: createGlobalToken('@kurdel/core/hhtp:server-adapter'),
|
|
4
|
+
Router: createGlobalToken('@kurdel/core/http:router'),
|
|
5
|
+
ControllerClasses: createGlobalToken('@kurdel/core/http:controller-classes'),
|
|
6
|
+
ControllerConfigs: createGlobalToken('@kurdel/core/http:controller-configs'),
|
|
7
|
+
MiddlewareRegistry: createGlobalToken('@kurdel/core/http:middleware-registry'),
|
|
8
|
+
ControllerResolver: createGlobalToken('@kurdel/core/http:controller-resolver'),
|
|
9
|
+
ResponseRenderer: createGlobalToken('@kurdel/core/http:response-renderer'),
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=http-tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-tokens.js","sourceRoot":"","sources":["../../src/http/http-tokens.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAYhD,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,aAAa,EAAE,iBAAiB,CAAgB,kCAAkC,CAAC;IACnF,MAAM,EAAE,iBAAiB,CAAS,0BAA0B,CAAC;IAC7D,iBAAiB,EAAE,iBAAiB,CAAe,sCAAsC,CAAC;IAC1F,iBAAiB,EAAE,iBAAiB,CAAqB,sCAAsC,CAAC;IAChG,kBAAkB,EAAE,iBAAiB,CACnC,uCAAuC,CACxC;IACD,kBAAkB,EAAE,iBAAiB,CACnC,uCAAuC,CACxC;IACD,gBAAgB,EAAE,iBAAiB,CAAmB,qCAAqC,CAAC;CAC7F,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './errors/index.js';
|
|
2
|
+
export * from './middleware/index.js';
|
|
3
|
+
export * from './response/index.js';
|
|
4
|
+
export * from './routing/index.js';
|
|
5
|
+
export * from './server/index.js';
|
|
6
|
+
export * from './types.js';
|
|
7
|
+
export * from './http-context.js';
|
|
8
|
+
export * from './http-module.js';
|
|
9
|
+
export * from './http-tokens.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './errors/index.js';
|
|
2
|
+
export * from './middleware/index.js';
|
|
3
|
+
export * from './response/index.js';
|
|
4
|
+
export * from './routing/index.js';
|
|
5
|
+
export * from './server/index.js';
|
|
6
|
+
export * from './types.js';
|
|
7
|
+
export * from './http-context.js';
|
|
8
|
+
export * from './http-module.js';
|
|
9
|
+
export * from './http-tokens.js';
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/http/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/http/middleware/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Middleware } from '../../http/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Middleware execution zones in the Kurdel runtime pipeline.
|
|
4
|
+
*
|
|
5
|
+
* Now includes:
|
|
6
|
+
* - `auth` → executes after validation, before PRE
|
|
7
|
+
* - `pre` → before controller
|
|
8
|
+
* - `post` → after render
|
|
9
|
+
* - `error` → on exceptions
|
|
10
|
+
* - `final` → always
|
|
11
|
+
*/
|
|
12
|
+
export type MiddlewareZone = 'auth' | 'pre' | 'post' | 'error' | 'final';
|
|
13
|
+
/**
|
|
14
|
+
* Describes a single middleware registration entry.
|
|
15
|
+
*
|
|
16
|
+
* - `zone`: execution stage (`pre`, `post`, `error`, or `final`)
|
|
17
|
+
* - `priority`: numeric priority (lower runs first)
|
|
18
|
+
* - `action`: optional controller action this middleware applies to
|
|
19
|
+
*/
|
|
20
|
+
export interface MiddlewareRegistration {
|
|
21
|
+
readonly use: Middleware;
|
|
22
|
+
readonly zone: MiddlewareZone;
|
|
23
|
+
readonly priority: number;
|
|
24
|
+
readonly action?: string;
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware-registration.js","sourceRoot":"","sources":["../../../src/http/middleware/middleware-registration.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Newable } from '@kurdel/common';
|
|
2
|
+
import type { Controller, Middleware, MiddlewareRegistration, MiddlewareZone } from '../../http/index.js';
|
|
3
|
+
export interface MiddlewareRegistry {
|
|
4
|
+
use(mw: Middleware, opts?: {
|
|
5
|
+
zone?: MiddlewareZone;
|
|
6
|
+
priority?: number;
|
|
7
|
+
}): void;
|
|
8
|
+
useFor(controller: Newable<Controller>, mw: Middleware, opts?: {
|
|
9
|
+
zone?: MiddlewareZone;
|
|
10
|
+
priority?: number;
|
|
11
|
+
action?: string;
|
|
12
|
+
}): void;
|
|
13
|
+
for(controller: Newable<Controller>, zone?: MiddlewareZone, action?: string): MiddlewareRegistration[];
|
|
14
|
+
all(zone?: MiddlewareZone): MiddlewareRegistration[];
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware-registry.js","sourceRoot":"","sources":["../../../src/http/middleware/middleware-registry.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../../src/http/middleware/middleware.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { JsonValue } from '../../http/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Base shape for all HTTP responses returned by controller actions.
|
|
4
|
+
*
|
|
5
|
+
* All ActionResult variants share a status code and may include optional headers or cookies.
|
|
6
|
+
*/
|
|
7
|
+
export interface BaseResult {
|
|
8
|
+
/** HTTP status code (e.g., 200, 404, 500) */
|
|
9
|
+
status: number;
|
|
10
|
+
/** Optional response headers */
|
|
11
|
+
headers?: Record<string, string>;
|
|
12
|
+
/** Optional cookies to set in response */
|
|
13
|
+
cookies?: Record<string, string>;
|
|
14
|
+
/** Optional error payload (for diagnostics/logging) */
|
|
15
|
+
error?: unknown;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Streaming response — e.g. file download, SSE, proxied stream.
|
|
19
|
+
*/
|
|
20
|
+
export interface StreamResult<TReadable = unknown> extends BaseResult {
|
|
21
|
+
kind: 'stream';
|
|
22
|
+
/** Platform-neutral stream descriptor */
|
|
23
|
+
body: TReadable;
|
|
24
|
+
/** Optional MIME type */
|
|
25
|
+
contentType?: string;
|
|
26
|
+
/** Optional content length (bytes) */
|
|
27
|
+
contentLength?: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* JSON response result.
|
|
31
|
+
*/
|
|
32
|
+
export interface JsonResult extends BaseResult {
|
|
33
|
+
kind: 'json';
|
|
34
|
+
body: JsonValue;
|
|
35
|
+
contentType?: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Plain text or pre-rendered HTML response.
|
|
39
|
+
*/
|
|
40
|
+
export interface TextResult extends BaseResult {
|
|
41
|
+
kind: 'text';
|
|
42
|
+
body: string;
|
|
43
|
+
contentType?: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Result representing a rendered HTML template.
|
|
47
|
+
*/
|
|
48
|
+
export interface HtmlResult extends BaseResult {
|
|
49
|
+
kind: 'html';
|
|
50
|
+
body: string;
|
|
51
|
+
contentType?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Redirect response (302, 301, 307, etc.)
|
|
55
|
+
*/
|
|
56
|
+
export interface RedirectResult extends BaseResult {
|
|
57
|
+
kind: 'redirect';
|
|
58
|
+
location: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Empty response (204 No Content, etc.)
|
|
62
|
+
*/
|
|
63
|
+
export interface EmptyResult extends BaseResult {
|
|
64
|
+
kind: 'empty';
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Union of all possible controller return types.
|
|
68
|
+
*/
|
|
69
|
+
export type ActionResult<TReadable = unknown> = JsonResult | TextResult | HtmlResult | RedirectResult | EmptyResult | StreamResult<TReadable>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-result.js","sourceRoot":"","sources":["../../../src/http/response/action-result.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ActionResult, HtmlResult, JsonValue } from '../../http/index.js';
|
|
2
|
+
import { HttpError } from '../../http/index.js';
|
|
3
|
+
export declare const Ok: (body?: JsonValue) => ActionResult;
|
|
4
|
+
export declare const Created: (body?: JsonValue) => ActionResult;
|
|
5
|
+
export declare const NoContent: () => ActionResult;
|
|
6
|
+
export declare const Text: (body: string, status?: number) => ActionResult;
|
|
7
|
+
export declare const Json: (data: JsonValue, status?: number) => ActionResult;
|
|
8
|
+
export declare const Html: (body: string, status?: number) => HtmlResult;
|
|
9
|
+
export declare const Redirect: (location: string, status?: number) => ActionResult;
|
|
10
|
+
export declare const BadRequest: (msg: string, details?: unknown) => HttpError;
|
|
11
|
+
export declare const Unauthorized: (msg?: string, details?: JsonValue) => HttpError;
|
|
12
|
+
export declare const Forbidden: (msg?: string, details?: JsonValue) => HttpError;
|
|
13
|
+
export declare const NotFound: (msg?: string, details?: JsonValue) => HttpError;
|
|
14
|
+
export declare const Conflict: (msg?: string, details?: JsonValue) => HttpError;
|
|
15
|
+
export declare const InternalServerError: (msg?: string, details?: JsonValue) => HttpError;
|
|
16
|
+
export declare const NotImplemented: (msg?: string, details?: JsonValue) => HttpError;
|
|
17
|
+
export declare const ServiceUnavailable: (msg?: string, details?: JsonValue) => HttpError;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { HttpError } from '../../http/index.js';
|
|
2
|
+
// success
|
|
3
|
+
export const Ok = (body = { message: 'OK' }) => ({
|
|
4
|
+
kind: 'json',
|
|
5
|
+
status: 200,
|
|
6
|
+
body,
|
|
7
|
+
});
|
|
8
|
+
export const Created = (body = { message: 'OK' }) => ({
|
|
9
|
+
kind: 'json',
|
|
10
|
+
status: 201,
|
|
11
|
+
body,
|
|
12
|
+
});
|
|
13
|
+
export const NoContent = () => ({
|
|
14
|
+
kind: 'empty',
|
|
15
|
+
status: 204,
|
|
16
|
+
});
|
|
17
|
+
export const Text = (body, status = 200) => ({
|
|
18
|
+
kind: 'text',
|
|
19
|
+
status,
|
|
20
|
+
body,
|
|
21
|
+
});
|
|
22
|
+
export const Json = (data, status = 200) => ({
|
|
23
|
+
kind: 'json',
|
|
24
|
+
status,
|
|
25
|
+
body: JSON.stringify(data),
|
|
26
|
+
});
|
|
27
|
+
export const Html = (body, status = 200) => ({
|
|
28
|
+
kind: 'html',
|
|
29
|
+
status,
|
|
30
|
+
body,
|
|
31
|
+
});
|
|
32
|
+
export const Redirect = (location, status = 302) => ({
|
|
33
|
+
kind: 'redirect',
|
|
34
|
+
status,
|
|
35
|
+
location,
|
|
36
|
+
});
|
|
37
|
+
// errors
|
|
38
|
+
export const BadRequest = (msg, details) => new HttpError(400, msg, details);
|
|
39
|
+
export const Unauthorized = (msg = 'Unauthorized', details) => new HttpError(401, msg, details);
|
|
40
|
+
export const Forbidden = (msg = 'Forbidden', details) => new HttpError(403, msg, details);
|
|
41
|
+
export const NotFound = (msg = 'Not Found', details) => new HttpError(404, msg, details);
|
|
42
|
+
export const Conflict = (msg = 'Conflict', details) => new HttpError(409, msg, details);
|
|
43
|
+
export const InternalServerError = (msg = 'Internal Server Error', details) => new HttpError(500, msg, details);
|
|
44
|
+
export const NotImplemented = (msg = 'Not Implemented', details) => new HttpError(501, msg, details);
|
|
45
|
+
export const ServiceUnavailable = (msg = 'Service Unavailable', details) => new HttpError(503, msg, details);
|
|
46
|
+
//# sourceMappingURL=http-results.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-results.js","sourceRoot":"","sources":["../../../src/http/response/http-results.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/http/response/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { HttpResponse } from '@kurdel/common';
|
|
2
|
+
import type { ActionResult } from '../../http/index.js';
|
|
3
|
+
export interface ResponseRenderer {
|
|
4
|
+
render(res: HttpResponse, result: ActionResult | void): void;
|
|
5
|
+
handleError(res: HttpResponse, err: unknown): void;
|
|
6
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-renderer.js","sourceRoot":"","sources":["../../../src/http/response/response-renderer.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Newable } from '@kurdel/common';
|
|
2
|
+
import type { Identifier } from '@kurdel/ioc';
|
|
3
|
+
import type { MiddlewareRegistration, RouteAuth } from '@kurdel/core/http';
|
|
4
|
+
import type { Controller, Middleware } from '../../http/index.js';
|
|
5
|
+
/**
|
|
6
|
+
* Declares how a controller should be registered and instantiated.
|
|
7
|
+
*/
|
|
8
|
+
export interface ControllerConfig {
|
|
9
|
+
/** Controller class */
|
|
10
|
+
use: Newable<Controller<any>>;
|
|
11
|
+
/** Dependencies to be injected from IoC */
|
|
12
|
+
deps?: Record<string, Identifier>;
|
|
13
|
+
/**
|
|
14
|
+
* Middlewares applied to this controller or its specific actions.
|
|
15
|
+
*
|
|
16
|
+
* Each entry can be either:
|
|
17
|
+
* - A raw `Middleware` (shorthand for `{ middleware, zone: 'pre', priority: 0 }`)
|
|
18
|
+
* - A structured `MiddlewareRegistration` with zone, priority, and action.
|
|
19
|
+
*/
|
|
20
|
+
middlewares?: Array<Middleware | MiddlewareRegistration>;
|
|
21
|
+
/** Optional route prefix applied to all controller routes */
|
|
22
|
+
prefix?: string;
|
|
23
|
+
auth?: RouteAuth;
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller-config.js","sourceRoot":"","sources":["../../../src/http/routing/controller-config.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller-resolver.js","sourceRoot":"","sources":["../../../src/http/routing/controller-resolver.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ActionResult, HtmlResult, HttpContext, Middleware, RouteConfig } from '../../http/index.js';
|
|
2
|
+
export declare abstract class Controller<TDeps extends Record<string, any> = {}> {
|
|
3
|
+
protected readonly deps: TDeps;
|
|
4
|
+
constructor(deps: TDeps);
|
|
5
|
+
abstract readonly routes: RouteConfig;
|
|
6
|
+
private middlewares;
|
|
7
|
+
use(mw: Middleware): void;
|
|
8
|
+
/** Exposed for runtime to resolve an action. */
|
|
9
|
+
getAction(actionName: string): import("../../http/index.js").RouteHandler<any, any>;
|
|
10
|
+
/** Optional explicit action resolver (runtime uses `routes` by default). */
|
|
11
|
+
protected resolveAction(actionName: string): import("../../http/index.js").RouteHandler<any, any>;
|
|
12
|
+
/**
|
|
13
|
+
* Render HTML via TemplateEngine injected into controller dependencies.
|
|
14
|
+
* Throws if no TemplateEngine is available.
|
|
15
|
+
*/
|
|
16
|
+
protected render(template: string, data?: Record<string, unknown>, status?: number): Promise<HtmlResult>;
|
|
17
|
+
/**
|
|
18
|
+
* Optionally overridden in tests or custom adapters,
|
|
19
|
+
* but normally invoked through RuntimeControllerExecutor.
|
|
20
|
+
*/
|
|
21
|
+
handle(actionName: string, ctx: HttpContext): Promise<ActionResult | void>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
2
|
+
export class Controller {
|
|
3
|
+
constructor(deps) {
|
|
4
|
+
this.deps = deps;
|
|
5
|
+
this.middlewares = [];
|
|
6
|
+
}
|
|
7
|
+
use(mw) {
|
|
8
|
+
this.middlewares.push(mw);
|
|
9
|
+
}
|
|
10
|
+
/** Exposed for runtime to resolve an action. */
|
|
11
|
+
getAction(actionName) {
|
|
12
|
+
return this.routes[actionName];
|
|
13
|
+
}
|
|
14
|
+
/** Optional explicit action resolver (runtime uses `routes` by default). */
|
|
15
|
+
resolveAction(actionName) {
|
|
16
|
+
return this.routes[actionName];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Render HTML via TemplateEngine injected into controller dependencies.
|
|
20
|
+
* Throws if no TemplateEngine is available.
|
|
21
|
+
*/
|
|
22
|
+
async render(template, data, status = 200) {
|
|
23
|
+
const view = this.deps.view;
|
|
24
|
+
if (!view) {
|
|
25
|
+
throw new Error(`TemplateEngine is not registered in controller dependencies (controller: ${this.constructor.name})`);
|
|
26
|
+
}
|
|
27
|
+
const html = await view.render(template, data ?? {});
|
|
28
|
+
return {
|
|
29
|
+
status,
|
|
30
|
+
kind: 'html',
|
|
31
|
+
body: html,
|
|
32
|
+
contentType: 'text/html; charset=utf-8',
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Optionally overridden in tests or custom adapters,
|
|
37
|
+
* but normally invoked through RuntimeControllerExecutor.
|
|
38
|
+
*/
|
|
39
|
+
async handle(actionName, ctx) {
|
|
40
|
+
const fn = this.resolveAction(actionName);
|
|
41
|
+
if (typeof fn !== 'function') {
|
|
42
|
+
throw new Error(`Action '${actionName}' not found in ${this.constructor.name}`);
|
|
43
|
+
}
|
|
44
|
+
return await fn.call(this, ctx);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller.js","sourceRoot":"","sources":["../../../src/http/routing/controller.ts"],"names":[],"mappings":"AASA,mEAAmE;AACnE,MAAM,OAAgB,UAAU;IAC9B,YAA+B,IAAW;QAAX,SAAI,GAAJ,IAAI,CAAO;QAKlC,gBAAW,GAAiB,EAAE,CAAC;IALM,CAAC;IAO9C,GAAG,CAAC,EAAc;QAChB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5B,CAAC;IAED,gDAAgD;IAChD,SAAS,CAAC,UAAkB;QAC1B,OAAO,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACjC,CAAC;IAED,4EAA4E;IAClE,aAAa,CAAC,UAAkB;QACxC,OAAO,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACjC,CAAC;IAED;;;OAGG;IACO,KAAK,CAAC,MAAM,CACpB,QAAgB,EAChB,IAA8B,EAC9B,MAAM,GAAG,GAAG;QAEZ,MAAM,IAAI,GAAI,IAAI,CAAC,IAAY,CAAC,IAAkC,CAAC;QACnE,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CACb,4EAA4E,IAAI,CAAC,WAAW,CAAC,IAAI,GAAG,CACrG,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;QACrD,OAAO;YACL,MAAM;YACN,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,IAAI;YACV,WAAW,EAAE,0BAA0B;SACxC,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,MAAM,CAAC,UAAkB,EAAE,GAAgB;QAC/C,MAAM,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;QAC1C,IAAI,OAAO,EAAE,KAAK,UAAU,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CAAC,WAAW,UAAU,kBAAkB,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;QAClF,CAAC;QACD,OAAO,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAClC,CAAC;CACF"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './controller-config.js';
|
|
2
|
+
export * from './controller.js';
|
|
3
|
+
export * from './controller-resolver.js';
|
|
4
|
+
export * from './route.js';
|
|
5
|
+
export * from './route-match.js';
|
|
6
|
+
export * from './route-schema.js';
|
|
7
|
+
export * from './router.js';
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/http/routing/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,iBAAiB,CAAC;AAChC,cAAc,0BAA0B,CAAC;AACzC,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Controller, Method, RouteAuth, RouteSchema } from '../../http/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* ## RouteMatch
|
|
4
|
+
*
|
|
5
|
+
* The normalized result of router resolution.
|
|
6
|
+
* Returned by `Router.resolve()` and consumed by the orchestrator
|
|
7
|
+
* to drive validation, authentication, middleware selection,
|
|
8
|
+
* and controller execution.
|
|
9
|
+
*
|
|
10
|
+
* Fields:
|
|
11
|
+
* - controller → DI-resolved controller instance for this request
|
|
12
|
+
* - method → HTTP method used for routing
|
|
13
|
+
* - action → name of the controller method to invoke
|
|
14
|
+
* - path → normalized route path (prefix + declared path)
|
|
15
|
+
* - params → extracted path parameters
|
|
16
|
+
* - auth → optional per-route authentication/authorization rules
|
|
17
|
+
* - schema → optional validation schema for params/query/body
|
|
18
|
+
*/
|
|
19
|
+
export interface RouteMatch {
|
|
20
|
+
controller: Controller<any>;
|
|
21
|
+
method: Method;
|
|
22
|
+
action: string;
|
|
23
|
+
path: string;
|
|
24
|
+
params: Record<string, string>;
|
|
25
|
+
auth?: RouteAuth;
|
|
26
|
+
schema?: RouteSchema;
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-match.js","sourceRoot":"","sources":["../../../src/http/routing/route-match.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Describes expected input for a given route.
|
|
3
|
+
* Framework-agnostic: any validator (Zod, Valibot, Yup) can implement it.
|
|
4
|
+
*/
|
|
5
|
+
export interface RouteSchema<TBody = unknown, TParams = Record<string, string>, TQuery = Record<string, string>> {
|
|
6
|
+
readonly body?: SchemaValidator<TBody>;
|
|
7
|
+
readonly params?: SchemaValidator<TParams>;
|
|
8
|
+
readonly query?: SchemaValidator<TQuery>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Minimal contract any schema validator must satisfy.
|
|
12
|
+
*/
|
|
13
|
+
export interface SchemaValidator<T> {
|
|
14
|
+
/** Validates value and returns it typed, or throws ValidationError */
|
|
15
|
+
validate(value: unknown): T | Promise<T>;
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-schema.js","sourceRoot":"","sources":["../../../src/http/routing/route-schema.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { Method, HttpContext, ActionResult, RouteSchema } from '../../http/index.js';
|
|
2
|
+
export declare const ROUTE_META: unique symbol;
|
|
3
|
+
/**
|
|
4
|
+
* ## RouteAuth
|
|
5
|
+
*
|
|
6
|
+
* Declarative authentication/authorization rules attached to a route
|
|
7
|
+
* or inherited from its controller.
|
|
8
|
+
*
|
|
9
|
+
* A route becomes protected **only if at least one auth field is present**
|
|
10
|
+
* (unless explicitly marked as `public: true`).
|
|
11
|
+
*
|
|
12
|
+
* ### Semantics
|
|
13
|
+
* - `public: true`
|
|
14
|
+
* Marks a route as publicly accessible, bypassing authentication entirely.
|
|
15
|
+
* This **overrides controller-level security**.
|
|
16
|
+
*
|
|
17
|
+
* - `strategy`
|
|
18
|
+
* Name of an authentication strategy registered in `AuthStrategyRegistry`
|
|
19
|
+
* (e.g. `"api-key"`, `"jwt"`, `"session"`).
|
|
20
|
+
* If set, the corresponding strategy is invoked before controller execution.
|
|
21
|
+
*
|
|
22
|
+
* - `roles`
|
|
23
|
+
* List of roles required for access.
|
|
24
|
+
* Authorization succeeds if the authenticated user has **at least one**
|
|
25
|
+
* matching role.
|
|
26
|
+
*
|
|
27
|
+
* ### Controller-level vs Route-level
|
|
28
|
+
* Controller `auth` rules act as defaults and are *merged* with route rules.
|
|
29
|
+
* Route-level fields override controller-level ones:
|
|
30
|
+
*
|
|
31
|
+
* - `public` overrides everything (`public: true` makes route open even inside
|
|
32
|
+
* protected controller)
|
|
33
|
+
* - `strategy` overrides controller strategy
|
|
34
|
+
* - `roles` override controller roles
|
|
35
|
+
*
|
|
36
|
+
* @example:
|
|
37
|
+
* ```ts
|
|
38
|
+
* // Public route (even inside protected controller)
|
|
39
|
+
* { public: true }
|
|
40
|
+
*
|
|
41
|
+
* // Simple protected route
|
|
42
|
+
* { strategy: 'api-key' }
|
|
43
|
+
*
|
|
44
|
+
* // Role-restricted route
|
|
45
|
+
* { strategy: 'api-key', roles: ['admin', 'root'] }
|
|
46
|
+
*
|
|
47
|
+
* // Controller-level default + route override:
|
|
48
|
+
* controller.auth = { strategy: 'jwt', roles: ['user'] }
|
|
49
|
+
* route.auth = { roles: ['manager'] } // => result: { strategy: 'jwt', roles: ['manager'] }
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export type RouteAuth = {
|
|
53
|
+
/** Allows bypassing all authentication. Route is always accessible. */
|
|
54
|
+
public?: boolean;
|
|
55
|
+
/** Name of the strategy registered in AuthStrategyRegistry. */
|
|
56
|
+
strategy?: string;
|
|
57
|
+
/** Allowed user roles; user must have at least one. */
|
|
58
|
+
roles?: string[];
|
|
59
|
+
/** Named authorization policies; every policy must grant access. */
|
|
60
|
+
policies?: string[];
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* ## RouteMeta
|
|
64
|
+
*
|
|
65
|
+
* Static metadata describing a single route produced by `route(meta)(handler)`.
|
|
66
|
+
* This metadata is read by `RuntimeRouter` at application bootstrap and
|
|
67
|
+
* becomes part of the compiled routing table.
|
|
68
|
+
*
|
|
69
|
+
* Fields:
|
|
70
|
+
* - `method` — HTTP method (GET/POST/PUT/DELETE/...)
|
|
71
|
+
* - `path` — route path template (`/users/:id`)
|
|
72
|
+
* - `auth` — optional authentication & authorization constraints
|
|
73
|
+
* - `schema` — optional validation schema (params/query/body)
|
|
74
|
+
*
|
|
75
|
+
* Notes:
|
|
76
|
+
* - This metadata is **pure** and never mutated at runtime.
|
|
77
|
+
* - Routers do not execute any logic here — they simply store and return it.
|
|
78
|
+
* - Orchestrator uses `schema` for validation and `auth` for authentication.
|
|
79
|
+
*/
|
|
80
|
+
export type RouteMeta<P extends string = string> = {
|
|
81
|
+
method: Method;
|
|
82
|
+
path: P;
|
|
83
|
+
auth?: RouteAuth;
|
|
84
|
+
schema?: RouteSchema;
|
|
85
|
+
};
|
|
86
|
+
type Split<S extends string> = S extends '' ? [] : S extends `/${infer R}` ? Split<R> : S extends `${infer A}/${infer B}` ? [A, ...Split<B>] : [
|
|
87
|
+
S
|
|
88
|
+
];
|
|
89
|
+
type ParamName<Seg extends string> = Seg extends `:${infer Name}` ? Name : Seg extends `:${infer Name}<${string}>` ? Name : never;
|
|
90
|
+
export type RouteParams<Path extends string> = {
|
|
91
|
+
[K in ParamName<Split<Path>[number]>]: string;
|
|
92
|
+
} & {};
|
|
93
|
+
/**
|
|
94
|
+
* A concrete action handler executed by runtime.
|
|
95
|
+
*
|
|
96
|
+
* TBody = validated body type
|
|
97
|
+
* TParams = validated params type
|
|
98
|
+
*/
|
|
99
|
+
export type RouteHandler<TBody = unknown, TParams extends Record<string, string> = Record<string, string>> = (ctx: HttpContext<TBody, TParams>) => Promise<ActionResult | void>;
|
|
100
|
+
/**
|
|
101
|
+
* RouteConfig maps action names to handlers.
|
|
102
|
+
*/
|
|
103
|
+
export type RouteConfig = {
|
|
104
|
+
[key: string]: RouteHandler<any, any>;
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* Decorates the given handler with metadata (method, path, schema).
|
|
108
|
+
*
|
|
109
|
+
* Typing also narrows ctx.params based on meta.path automatically.
|
|
110
|
+
*/
|
|
111
|
+
export declare function route<const M extends RouteMeta>(meta: M): <TBody = unknown, P extends RouteParams<M["path"]> = RouteParams<M["path"]>>(fn: RouteHandler<TBody, P>) => typeof fn;
|
|
112
|
+
export {};
|