@kurdel/runtime 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 +44 -0
- package/lib/app/errors/controller-action-missing-result-error.d.ts +9 -0
- package/lib/app/errors/controller-action-missing-result-error.js +14 -0
- package/lib/app/errors/controller-action-missing-result-error.js.map +1 -0
- package/lib/app/errors/module-validation-error.d.ts +25 -0
- package/lib/app/errors/module-validation-error.js +29 -0
- package/lib/app/errors/module-validation-error.js.map +1 -0
- package/lib/app/errors/provider-configuration-error.d.ts +24 -0
- package/lib/app/errors/provider-configuration-error.js +28 -0
- package/lib/app/errors/provider-configuration-error.js.map +1 -0
- package/lib/app/index.d.ts +1 -0
- package/lib/app/index.js +2 -0
- package/lib/app/index.js.map +1 -0
- package/lib/app/lifecycle-manager.d.ts +26 -0
- package/lib/app/lifecycle-manager.js +40 -0
- package/lib/app/lifecycle-manager.js.map +1 -0
- package/lib/app/module-loader.d.ts +29 -0
- package/lib/app/module-loader.js +61 -0
- package/lib/app/module-loader.js.map +1 -0
- package/lib/app/provider-registrar.d.ts +29 -0
- package/lib/app/provider-registrar.js +67 -0
- package/lib/app/provider-registrar.js.map +1 -0
- package/lib/app/runtime-application.d.ts +75 -0
- package/lib/app/runtime-application.js +97 -0
- package/lib/app/runtime-application.js.map +1 -0
- package/lib/app/runtime-composer.d.ts +25 -0
- package/lib/app/runtime-composer.js +100 -0
- package/lib/app/runtime-composer.js.map +1 -0
- package/lib/app/server-runner.d.ts +36 -0
- package/lib/app/server-runner.js +65 -0
- package/lib/app/server-runner.js.map +1 -0
- package/lib/http/index.d.ts +6 -0
- package/lib/http/index.js +7 -0
- package/lib/http/index.js.map +1 -0
- package/lib/http/is-http-error.d.ts +2 -0
- package/lib/http/is-http-error.js +7 -0
- package/lib/http/is-http-error.js.map +1 -0
- package/lib/http/noop-response-renderer.d.ts +7 -0
- package/lib/http/noop-response-renderer.js +10 -0
- package/lib/http/noop-response-renderer.js.map +1 -0
- package/lib/http/render-action-result.d.ts +4 -0
- package/lib/http/render-action-result.js +24 -0
- package/lib/http/render-action-result.js.map +1 -0
- package/lib/http/runtime-controller-pipe.d.ts +16 -0
- package/lib/http/runtime-controller-pipe.js +26 -0
- package/lib/http/runtime-controller-pipe.js.map +1 -0
- package/lib/http/runtime-controller-resolver.d.ts +12 -0
- package/lib/http/runtime-controller-resolver.js +29 -0
- package/lib/http/runtime-controller-resolver.js.map +1 -0
- package/lib/http/runtime-http-context-factory.d.ts +29 -0
- package/lib/http/runtime-http-context-factory.js +91 -0
- package/lib/http/runtime-http-context-factory.js.map +1 -0
- package/lib/http/runtime-middleware-pipe.d.ts +15 -0
- package/lib/http/runtime-middleware-pipe.js +31 -0
- package/lib/http/runtime-middleware-pipe.js.map +1 -0
- package/lib/http/runtime-middleware-registry.d.ts +18 -0
- package/lib/http/runtime-middleware-registry.js +39 -0
- package/lib/http/runtime-middleware-registry.js.map +1 -0
- package/lib/http/runtime-request-orchestrator.d.ts +21 -0
- package/lib/http/runtime-request-orchestrator.js +114 -0
- package/lib/http/runtime-request-orchestrator.js.map +1 -0
- package/lib/http/runtime-response-renderer.d.ts +12 -0
- package/lib/http/runtime-response-renderer.js +37 -0
- package/lib/http/runtime-response-renderer.js.map +1 -0
- package/lib/http/runtime-router.d.ts +37 -0
- package/lib/http/runtime-router.js +135 -0
- package/lib/http/runtime-router.js.map +1 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +4 -0
- package/lib/index.js.map +1 -0
- package/lib/middlewares/create-validator.d.ts +7 -0
- package/lib/middlewares/create-validator.js +21 -0
- package/lib/middlewares/create-validator.js.map +1 -0
- package/lib/middlewares/error-handle.d.ts +2 -0
- package/lib/middlewares/error-handle.js +30 -0
- package/lib/middlewares/error-handle.js.map +1 -0
- package/lib/middlewares/index.d.ts +2 -0
- package/lib/middlewares/index.js +3 -0
- package/lib/middlewares/index.js.map +1 -0
- package/lib/middlewares/json-body-parser.d.ts +14 -0
- package/lib/middlewares/json-body-parser.js +23 -0
- package/lib/middlewares/json-body-parser.js.map +1 -0
- package/lib/middlewares/schema-validator.d.ts +13 -0
- package/lib/middlewares/schema-validator.js +44 -0
- package/lib/middlewares/schema-validator.js.map +1 -0
- package/lib/modules/controller-module.d.ts +23 -0
- package/lib/modules/controller-module.js +76 -0
- package/lib/modules/controller-module.js.map +1 -0
- package/lib/modules/database-module.d.ts +25 -0
- package/lib/modules/database-module.js +40 -0
- package/lib/modules/database-module.js.map +1 -0
- package/lib/modules/index.d.ts +1 -0
- package/lib/modules/index.js +2 -0
- package/lib/modules/index.js.map +1 -0
- package/lib/modules/lifecycle-module.d.ts +9 -0
- package/lib/modules/lifecycle-module.js +17 -0
- package/lib/modules/lifecycle-module.js.map +1 -0
- package/lib/modules/middleware-module.d.ts +18 -0
- package/lib/modules/middleware-module.js +45 -0
- package/lib/modules/middleware-module.js.map +1 -0
- package/lib/modules/model-module.d.ts +17 -0
- package/lib/modules/model-module.js +24 -0
- package/lib/modules/model-module.js.map +1 -0
- package/lib/modules/server-module.d.ts +30 -0
- package/lib/modules/server-module.js +65 -0
- package/lib/modules/server-module.js.map +1 -0
- package/lib/template/ensure-template-engine-binding.d.ts +2 -0
- package/lib/template/ensure-template-engine-binding.js +8 -0
- package/lib/template/ensure-template-engine-binding.js.map +1 -0
- package/lib/template/index.d.ts +1 -0
- package/lib/template/index.js +2 -0
- package/lib/template/index.js.map +1 -0
- package/lib/template/noop-template-engine.d.ts +4 -0
- package/lib/template/noop-template-engine.js +6 -0
- package/lib/template/noop-template-engine.js.map +1 -0
- package/package.json +64 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 Andrii Sorokin
|
|
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,44 @@
|
|
|
1
|
+
# @kurdel/runtime
|
|
2
|
+
|
|
3
|
+
Runtime **implementation layer** of Kurdel.
|
|
4
|
+
Includes router, controller resolver, lifecycle orchestration, and middleware handling.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 📦 Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @kurdel/runtime
|
|
12
|
+
````
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 🧩 Responsibilities
|
|
17
|
+
|
|
18
|
+
* Routing and controller dispatch
|
|
19
|
+
* Middleware registry and composition
|
|
20
|
+
* Module loading and lifecycle management
|
|
21
|
+
* Template engine integration (via `TemplateEngine` interface)
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 🚀 Example
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { route, Controller, Ok } from '@kurdel/core/http';
|
|
29
|
+
|
|
30
|
+
export class PingController extends Controller {
|
|
31
|
+
readonly routes = {
|
|
32
|
+
ping: route({ method: 'GET', path: '/ping' })(this.ping),
|
|
33
|
+
};
|
|
34
|
+
async ping() { return Ok({ ok: true }); }
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Used indirectly via `@kurdel/facade`.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 📄 License
|
|
43
|
+
|
|
44
|
+
MIT © Andrii Sorokin
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { HttpError } from '@kurdel/core/http';
|
|
2
|
+
import type { Controller } from '@kurdel/core/http';
|
|
3
|
+
/**
|
|
4
|
+
* Thrown when a controller action returns `undefined`,
|
|
5
|
+
* which violates the ActionResult contract.
|
|
6
|
+
*/
|
|
7
|
+
export declare class ControllerActionMissingResultError extends HttpError {
|
|
8
|
+
constructor(controller: Controller, action: string);
|
|
9
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { HttpError } from '@kurdel/core/http';
|
|
2
|
+
/**
|
|
3
|
+
* Thrown when a controller action returns `undefined`,
|
|
4
|
+
* which violates the ActionResult contract.
|
|
5
|
+
*/
|
|
6
|
+
export class ControllerActionMissingResultError extends HttpError {
|
|
7
|
+
constructor(controller, action) {
|
|
8
|
+
super(500, `Controller action returned undefined: ${controller.constructor.name}.${action}`, {
|
|
9
|
+
controller: controller.constructor.name,
|
|
10
|
+
action,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=controller-action-missing-result-error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller-action-missing-result-error.js","sourceRoot":"","sources":["../../../src/app/errors/controller-action-missing-result-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAG9C;;;GAGG;AACH,MAAM,OAAO,kCAAmC,SAAQ,SAAS;IAC/D,YAAY,UAAsB,EAAE,MAAc;QAChD,KAAK,CACH,GAAG,EACH,yCAAyC,UAAU,CAAC,WAAW,CAAC,IAAI,IAAI,MAAM,EAAE,EAChF;YACE,UAAU,EAAE,UAAU,CAAC,WAAW,CAAC,IAAI;YACvC,MAAM;SACP,CACF,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown when a module fails structural or dependency validation
|
|
3
|
+
* during the application bootstrap phase.
|
|
4
|
+
*
|
|
5
|
+
* Typical causes:
|
|
6
|
+
* - Duplicate provider tokens declared across multiple modules
|
|
7
|
+
* - Missing imported tokens that were never exported by any module
|
|
8
|
+
* - Declared exports that are not actually registered in the container
|
|
9
|
+
*
|
|
10
|
+
* This error represents a higher-level composition problem —
|
|
11
|
+
* it indicates that the module graph itself is inconsistent,
|
|
12
|
+
* not an issue within a single provider definition.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* throw new ModuleValidationError(
|
|
17
|
+
* `Duplicate provider for token "${String(token)}" between modules "${a}" and "${b}".`
|
|
18
|
+
* );
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @see ProviderConfigurationError - for per-provider misconfiguration
|
|
22
|
+
*/
|
|
23
|
+
export declare class ModuleValidationError extends Error {
|
|
24
|
+
constructor(message: string);
|
|
25
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown when a module fails structural or dependency validation
|
|
3
|
+
* during the application bootstrap phase.
|
|
4
|
+
*
|
|
5
|
+
* Typical causes:
|
|
6
|
+
* - Duplicate provider tokens declared across multiple modules
|
|
7
|
+
* - Missing imported tokens that were never exported by any module
|
|
8
|
+
* - Declared exports that are not actually registered in the container
|
|
9
|
+
*
|
|
10
|
+
* This error represents a higher-level composition problem —
|
|
11
|
+
* it indicates that the module graph itself is inconsistent,
|
|
12
|
+
* not an issue within a single provider definition.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* throw new ModuleValidationError(
|
|
17
|
+
* `Duplicate provider for token "${String(token)}" between modules "${a}" and "${b}".`
|
|
18
|
+
* );
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @see ProviderConfigurationError - for per-provider misconfiguration
|
|
22
|
+
*/
|
|
23
|
+
export class ModuleValidationError extends Error {
|
|
24
|
+
constructor(message) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.name = 'ModuleValidationError';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=module-validation-error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module-validation-error.js","sourceRoot":"","sources":["../../../src/app/errors/module-validation-error.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown when a provider declaration is structurally valid
|
|
3
|
+
* but logically inconsistent with IoC container rules.
|
|
4
|
+
*
|
|
5
|
+
* Typical causes:
|
|
6
|
+
* - Declaring an async factory without marking it as `singleton`
|
|
7
|
+
* - Using `useFactory` or `useClass` with incompatible `deps`
|
|
8
|
+
* - Attempting to bind a provider that resolves to `undefined`
|
|
9
|
+
*
|
|
10
|
+
* This error indicates a configuration problem within a single
|
|
11
|
+
* provider definition, not a higher-level module composition issue.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* throw new ProviderConfigurationError(
|
|
16
|
+
* `Async factory for "${String(provider.provide)}" must be declared as singleton.`
|
|
17
|
+
* );
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* @see ModuleValidationError - for module-level composition issues
|
|
21
|
+
*/
|
|
22
|
+
export declare class ProviderConfigurationError extends Error {
|
|
23
|
+
constructor(message: string);
|
|
24
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown when a provider declaration is structurally valid
|
|
3
|
+
* but logically inconsistent with IoC container rules.
|
|
4
|
+
*
|
|
5
|
+
* Typical causes:
|
|
6
|
+
* - Declaring an async factory without marking it as `singleton`
|
|
7
|
+
* - Using `useFactory` or `useClass` with incompatible `deps`
|
|
8
|
+
* - Attempting to bind a provider that resolves to `undefined`
|
|
9
|
+
*
|
|
10
|
+
* This error indicates a configuration problem within a single
|
|
11
|
+
* provider definition, not a higher-level module composition issue.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* throw new ProviderConfigurationError(
|
|
16
|
+
* `Async factory for "${String(provider.provide)}" must be declared as singleton.`
|
|
17
|
+
* );
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* @see ModuleValidationError - for module-level composition issues
|
|
21
|
+
*/
|
|
22
|
+
export class ProviderConfigurationError extends Error {
|
|
23
|
+
constructor(message) {
|
|
24
|
+
super(message);
|
|
25
|
+
this.name = 'ProviderConfigurationError';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=provider-configuration-error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-configuration-error.js","sourceRoot":"","sources":["../../../src/app/errors/provider-configuration-error.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,OAAO,0BAA2B,SAAQ,KAAK;IACnD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,4BAA4B,CAAC;IAC3C,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { RuntimeApplication } from './runtime-application.js';
|
package/lib/app/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/app/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { OnStartHook, OnShutdownHook } from '@kurdel/core/app';
|
|
2
|
+
/**
|
|
3
|
+
* Executes lifecycle hooks in sequence with error handling and optional logging.
|
|
4
|
+
*
|
|
5
|
+
* Used by the runtime to manage application startup and shutdown phases.
|
|
6
|
+
*
|
|
7
|
+
* Design goals:
|
|
8
|
+
* - Run hooks sequentially (not in parallel) to preserve dependency order.
|
|
9
|
+
* - Stop execution immediately on first error (fail fast).
|
|
10
|
+
* - Log hook errors if a logger is provided, but never swallow them silently.
|
|
11
|
+
*/
|
|
12
|
+
export declare class LifecycleManager {
|
|
13
|
+
private readonly logger?;
|
|
14
|
+
constructor(logger?: {
|
|
15
|
+
error?: (...args: any[]) => void;
|
|
16
|
+
} | undefined);
|
|
17
|
+
/**
|
|
18
|
+
* Run all registered lifecycle hooks of a given kind.
|
|
19
|
+
*
|
|
20
|
+
* @param kind - Indicates whether start or shutdown hooks are being executed.
|
|
21
|
+
* @param hooks - Ordered list of async or sync hooks to run.
|
|
22
|
+
*
|
|
23
|
+
* If any hook throws, execution stops and the error is rethrown after logging.
|
|
24
|
+
*/
|
|
25
|
+
runHooks(kind: 'start' | 'shutdown', hooks: Array<OnStartHook | OnShutdownHook>): Promise<void>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Executes lifecycle hooks in sequence with error handling and optional logging.
|
|
3
|
+
*
|
|
4
|
+
* Used by the runtime to manage application startup and shutdown phases.
|
|
5
|
+
*
|
|
6
|
+
* Design goals:
|
|
7
|
+
* - Run hooks sequentially (not in parallel) to preserve dependency order.
|
|
8
|
+
* - Stop execution immediately on first error (fail fast).
|
|
9
|
+
* - Log hook errors if a logger is provided, but never swallow them silently.
|
|
10
|
+
*/
|
|
11
|
+
export class LifecycleManager {
|
|
12
|
+
constructor(logger) {
|
|
13
|
+
this.logger = logger;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Run all registered lifecycle hooks of a given kind.
|
|
17
|
+
*
|
|
18
|
+
* @param kind - Indicates whether start or shutdown hooks are being executed.
|
|
19
|
+
* @param hooks - Ordered list of async or sync hooks to run.
|
|
20
|
+
*
|
|
21
|
+
* If any hook throws, execution stops and the error is rethrown after logging.
|
|
22
|
+
*/
|
|
23
|
+
async runHooks(kind, hooks) {
|
|
24
|
+
for (const hook of hooks) {
|
|
25
|
+
try {
|
|
26
|
+
await hook();
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
try {
|
|
30
|
+
this.logger?.error?.(`[lifecycle:${kind}]`, err);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
/* ignore logging errors */
|
|
34
|
+
}
|
|
35
|
+
throw err;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=lifecycle-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle-manager.js","sourceRoot":"","sources":["../../src/app/lifecycle-manager.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,MAAM,OAAO,gBAAgB;IAC3B,YAA6B,MAA6C;QAA7C,WAAM,GAAN,MAAM,CAAuC;IAAG,CAAC;IAE9E;;;;;;;OAOG;IACH,KAAK,CAAC,QAAQ,CACZ,IAA0B,EAC1B,KAA0C;QAE1C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC;gBACH,MAAM,IAAI,EAAE,CAAC;YACf,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC;oBACH,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,cAAc,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC;gBACnD,CAAC;gBAAC,MAAM,CAAC;oBACP,2BAA2B;gBAC7B,CAAC;gBACD,MAAM,GAAG,CAAC;YACZ,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { AppModule, AppConfig } from '@kurdel/core/app';
|
|
2
|
+
import type { Container } from '@kurdel/ioc';
|
|
3
|
+
/**
|
|
4
|
+
* Loads and initializes all application modules.
|
|
5
|
+
*
|
|
6
|
+
* Responsibilities:
|
|
7
|
+
* - Validate declared `imports` (fail fast if required tokens are missing)
|
|
8
|
+
* - Register `providers` into the IoC container using ProviderRegistrar
|
|
9
|
+
* - Execute each module’s optional `register(ioc, config)` hook
|
|
10
|
+
* - Verify that declared `exports` are actually bound in the container
|
|
11
|
+
*
|
|
12
|
+
* Design notes:
|
|
13
|
+
* - Modules are processed sequentially to ensure deterministic initialization order.
|
|
14
|
+
* - Each module operates within the same root container (no nested scopes here).
|
|
15
|
+
* - The loader itself performs no lifecycle logic — only structural assembly.
|
|
16
|
+
*/
|
|
17
|
+
export declare class ModuleLoader {
|
|
18
|
+
private readonly ioc;
|
|
19
|
+
constructor(ioc: Container);
|
|
20
|
+
/**
|
|
21
|
+
* Initialize the given list of modules in order.
|
|
22
|
+
*
|
|
23
|
+
* @param modules - Ordered list of application modules to load.
|
|
24
|
+
* @param config - Immutable application configuration.
|
|
25
|
+
*
|
|
26
|
+
* Throws if a module import or export token is missing in the container.
|
|
27
|
+
*/
|
|
28
|
+
init(modules: AppModule[], config: AppConfig): Promise<void>;
|
|
29
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { ProviderRegistrar } from '../app/provider-registrar.js';
|
|
2
|
+
import { ModuleValidationError } from '../app/errors/module-validation-error.js';
|
|
3
|
+
/**
|
|
4
|
+
* Loads and initializes all application modules.
|
|
5
|
+
*
|
|
6
|
+
* Responsibilities:
|
|
7
|
+
* - Validate declared `imports` (fail fast if required tokens are missing)
|
|
8
|
+
* - Register `providers` into the IoC container using ProviderRegistrar
|
|
9
|
+
* - Execute each module’s optional `register(ioc, config)` hook
|
|
10
|
+
* - Verify that declared `exports` are actually bound in the container
|
|
11
|
+
*
|
|
12
|
+
* Design notes:
|
|
13
|
+
* - Modules are processed sequentially to ensure deterministic initialization order.
|
|
14
|
+
* - Each module operates within the same root container (no nested scopes here).
|
|
15
|
+
* - The loader itself performs no lifecycle logic — only structural assembly.
|
|
16
|
+
*/
|
|
17
|
+
export class ModuleLoader {
|
|
18
|
+
constructor(ioc) {
|
|
19
|
+
this.ioc = ioc;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Initialize the given list of modules in order.
|
|
23
|
+
*
|
|
24
|
+
* @param modules - Ordered list of application modules to load.
|
|
25
|
+
* @param config - Immutable application configuration.
|
|
26
|
+
*
|
|
27
|
+
* Throws if a module import or export token is missing in the container.
|
|
28
|
+
*/
|
|
29
|
+
async init(modules, config) {
|
|
30
|
+
const registrar = new ProviderRegistrar(this.ioc);
|
|
31
|
+
for (const mod of modules) {
|
|
32
|
+
// --- 1️⃣ Validate declared imports ---
|
|
33
|
+
if (mod.imports) {
|
|
34
|
+
for (const dep of Object.values(mod.imports)) {
|
|
35
|
+
if (!this.ioc.has(dep)) {
|
|
36
|
+
throw new ModuleValidationError(`Module ${mod.constructor.name} missing dependency: ${String(dep)}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// --- 2️⃣ Register providers ---
|
|
41
|
+
if (mod.providers) {
|
|
42
|
+
for (const provider of mod.providers) {
|
|
43
|
+
registrar.register(provider);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// --- 3️⃣ Execute custom register() hook ---
|
|
47
|
+
if (typeof mod.register === 'function') {
|
|
48
|
+
await mod.register(this.ioc, config);
|
|
49
|
+
}
|
|
50
|
+
// --- 4️⃣ Validate declared exports ---
|
|
51
|
+
if (mod.exports) {
|
|
52
|
+
for (const token of Object.values(mod.exports)) {
|
|
53
|
+
if (!this.ioc.has(token)) {
|
|
54
|
+
throw new ModuleValidationError(`Module did not register expected export: ${String(token)}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=module-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module-loader.js","sourceRoot":"","sources":["../../src/app/module-loader.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2CAA2C,CAAC;AAElF;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,YAAY;IACvB,YAA6B,GAAc;QAAd,QAAG,GAAH,GAAG,CAAW;IAAG,CAAC;IAE/C;;;;;;;OAOG;IACH,KAAK,CAAC,IAAI,CAAC,OAAoB,EAAE,MAAiB;QAChD,MAAM,SAAS,GAAG,IAAI,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAElD,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;YAC1B,wCAAwC;YACxC,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;gBAChB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;oBAC7C,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;wBACvB,MAAM,IAAI,qBAAqB,CAC7B,UAAU,GAAG,CAAC,WAAW,CAAC,IAAI,wBAAwB,MAAM,CAAC,GAAG,CAAC,EAAE,CACpE,CAAC;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC;YAED,iCAAiC;YACjC,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;gBAClB,KAAK,MAAM,QAAQ,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;oBACrC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;gBAC/B,CAAC;YACH,CAAC;YAED,6CAA6C;YAC7C,IAAI,OAAO,GAAG,CAAC,QAAQ,KAAK,UAAU,EAAE,CAAC;gBACvC,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YACvC,CAAC;YAED,wCAAwC;YACxC,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;gBAChB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;oBAC/C,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;wBACzB,MAAM,IAAI,qBAAqB,CAC7B,4CAA4C,MAAM,CAAC,KAAK,CAAC,EAAE,CAC5D,CAAC;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ProviderConfig } from '@kurdel/core/app';
|
|
2
|
+
import type { Container } from '@kurdel/ioc';
|
|
3
|
+
/**
|
|
4
|
+
* Registers dependency providers in the IoC container according to DI semantics.
|
|
5
|
+
*
|
|
6
|
+
* Supported provider forms:
|
|
7
|
+
* - `useClass` — binds a class (constructor) with optional dependencies and lifecycle hint.
|
|
8
|
+
* - `useInstance` — binds an existing instance (always singleton).
|
|
9
|
+
* - `useFactory` — binds a factory function (sync or async), optionally cached as singleton.
|
|
10
|
+
*
|
|
11
|
+
* Async factories are handled gracefully:
|
|
12
|
+
* - If the factory returns a Promise and `singleton` is true, the resolved instance is bound.
|
|
13
|
+
* - Otherwise, async non-singleton factories are disallowed (runtime error).
|
|
14
|
+
*
|
|
15
|
+
* Design notes:
|
|
16
|
+
* - This class is a low-level helper used by `ModuleLoader`.
|
|
17
|
+
* - It performs no validation beyond binding logic — imports/exports are checked elsewhere.
|
|
18
|
+
*/
|
|
19
|
+
export declare class ProviderRegistrar {
|
|
20
|
+
private readonly ioc;
|
|
21
|
+
constructor(ioc: Container);
|
|
22
|
+
/**
|
|
23
|
+
* Register a single provider within the container.
|
|
24
|
+
*
|
|
25
|
+
* @param provider - Declarative provider configuration.
|
|
26
|
+
* @throws {Error} If async factory is declared without `singleton: true`.
|
|
27
|
+
*/
|
|
28
|
+
register<T>(provider: ProviderConfig<T>): void;
|
|
29
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { ProviderConfigurationError } from '../app/errors/provider-configuration-error.js';
|
|
2
|
+
/**
|
|
3
|
+
* Registers dependency providers in the IoC container according to DI semantics.
|
|
4
|
+
*
|
|
5
|
+
* Supported provider forms:
|
|
6
|
+
* - `useClass` — binds a class (constructor) with optional dependencies and lifecycle hint.
|
|
7
|
+
* - `useInstance` — binds an existing instance (always singleton).
|
|
8
|
+
* - `useFactory` — binds a factory function (sync or async), optionally cached as singleton.
|
|
9
|
+
*
|
|
10
|
+
* Async factories are handled gracefully:
|
|
11
|
+
* - If the factory returns a Promise and `singleton` is true, the resolved instance is bound.
|
|
12
|
+
* - Otherwise, async non-singleton factories are disallowed (runtime error).
|
|
13
|
+
*
|
|
14
|
+
* Design notes:
|
|
15
|
+
* - This class is a low-level helper used by `ModuleLoader`.
|
|
16
|
+
* - It performs no validation beyond binding logic — imports/exports are checked elsewhere.
|
|
17
|
+
*/
|
|
18
|
+
export class ProviderRegistrar {
|
|
19
|
+
constructor(ioc) {
|
|
20
|
+
this.ioc = ioc;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Register a single provider within the container.
|
|
24
|
+
*
|
|
25
|
+
* @param provider - Declarative provider configuration.
|
|
26
|
+
* @throws {Error} If async factory is declared without `singleton: true`.
|
|
27
|
+
*/
|
|
28
|
+
register(provider) {
|
|
29
|
+
// --- 1️⃣ useClass: bind class constructor ---
|
|
30
|
+
if ('useClass' in provider) {
|
|
31
|
+
const binding = this.ioc.bind(provider.provide).to(provider.useClass);
|
|
32
|
+
if (provider.deps)
|
|
33
|
+
binding.with(provider.deps);
|
|
34
|
+
if (provider.singleton)
|
|
35
|
+
binding.inSingletonScope();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
// --- 2️⃣ useInstance: bind pre-constructed instance ---
|
|
39
|
+
if ('useInstance' in provider) {
|
|
40
|
+
this.ioc.bind(provider.provide).toInstance(provider.useInstance);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
// --- 3️⃣ useFactory: bind factory (sync or async) ---
|
|
44
|
+
if ('useFactory' in provider) {
|
|
45
|
+
const result = provider.useFactory(this.ioc);
|
|
46
|
+
// Async factory case
|
|
47
|
+
if (result instanceof Promise) {
|
|
48
|
+
if (!provider.singleton) {
|
|
49
|
+
throw new ProviderConfigurationError(`Async factory for "${String(provider.provide)}" must be declared as singleton.`);
|
|
50
|
+
}
|
|
51
|
+
// When the promise resolves, bind the instance
|
|
52
|
+
result.then(instance => {
|
|
53
|
+
this.ioc.bind(provider.provide).toInstance(instance);
|
|
54
|
+
});
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
// Sync factory case
|
|
58
|
+
if (provider.singleton) {
|
|
59
|
+
this.ioc.bind(provider.provide).toInstance(result);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
this.ioc.toFactory(provider.provide, () => provider.useFactory(this.ioc));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=provider-registrar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-registrar.js","sourceRoot":"","sources":["../../src/app/provider-registrar.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,0BAA0B,EAAE,MAAM,gDAAgD,CAAC;AAE5F;;;;;;;;;;;;;;;GAeG;AACH,MAAM,OAAO,iBAAiB;IAC5B,YAA6B,GAAc;QAAd,QAAG,GAAH,GAAG,CAAW;IAAG,CAAC;IAE/C;;;;;OAKG;IACH,QAAQ,CAAI,QAA2B;QACrC,+CAA+C;QAC/C,IAAI,UAAU,IAAI,QAAQ,EAAE,CAAC;YAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAI,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAEzE,IAAI,QAAQ,CAAC,IAAI;gBAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YAC/C,IAAI,QAAQ,CAAC,SAAS;gBAAE,OAAO,CAAC,gBAAgB,EAAE,CAAC;YAEnD,OAAO;QACT,CAAC;QAED,yDAAyD;QACzD,IAAI,aAAa,IAAI,QAAQ,EAAE,CAAC;YAC9B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAI,QAAQ,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;YACpE,OAAO;QACT,CAAC;QAED,uDAAuD;QACvD,IAAI,YAAY,IAAI,QAAQ,EAAE,CAAC;YAC7B,MAAM,MAAM,GAAG,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAE7C,qBAAqB;YACrB,IAAI,MAAM,YAAY,OAAO,EAAE,CAAC;gBAC9B,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC;oBACxB,MAAM,IAAI,0BAA0B,CAClC,sBAAsB,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,kCAAkC,CACjF,CAAC;gBACJ,CAAC;gBAED,+CAA+C;gBAC/C,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE;oBACrB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAI,QAAQ,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;gBAC1D,CAAC,CAAC,CAAC;gBAEH,OAAO;YACT,CAAC;YAED,oBAAoB;YACpB,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;gBACvB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAI,QAAQ,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YACxD,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAM,CAAC,CAAC;YACjF,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { Container } from '@kurdel/ioc';
|
|
2
|
+
import type { AppConfig, Application, AppModule } from '@kurdel/core/app';
|
|
3
|
+
import type { RunningServer } from '@kurdel/core/http';
|
|
4
|
+
/**
|
|
5
|
+
* kurdel runtime application.
|
|
6
|
+
*
|
|
7
|
+
* Coordinates module initialization, dependency registration,
|
|
8
|
+
* and server lifecycle within a single runtime instance.
|
|
9
|
+
*
|
|
10
|
+
* Responsibilities:
|
|
11
|
+
* - Aggregate declarations from user modules (controllers, middlewares, models)
|
|
12
|
+
* - Compose built-in runtime modules (server/router/middleware/model/database/lifecycle)
|
|
13
|
+
* - Initialize all modules via ModuleLoader and register providers in IoC
|
|
14
|
+
* - Manage application lifecycle (OnStart / OnShutdown)
|
|
15
|
+
* - Start and stop the HTTP server via ServerAdapter (no Node.js types leak)
|
|
16
|
+
*
|
|
17
|
+
* Design notes:
|
|
18
|
+
* - Lives entirely in runtime; consumers interact only through the public Application API
|
|
19
|
+
* - “IoC first”: every dependency is resolved via tokens and interfaces
|
|
20
|
+
* - Request scope creation is delegated to ServerModule (per-request)
|
|
21
|
+
*/
|
|
22
|
+
export declare class RuntimeApplication implements Application {
|
|
23
|
+
/** Immutable app configuration passed from the facade. */
|
|
24
|
+
private readonly config;
|
|
25
|
+
/** Root IoC container — central registry for all runtime providers. */
|
|
26
|
+
private readonly ioc;
|
|
27
|
+
/** Ordered list of modules to initialize (built-in + user-defined). */
|
|
28
|
+
private readonly modules;
|
|
29
|
+
/** Handles lifecycle hook execution (OnStart / OnShutdown). */
|
|
30
|
+
private readonly lifecycle;
|
|
31
|
+
/** Read-only accessor exposing the underlying IoC container. */
|
|
32
|
+
get container(): Container;
|
|
33
|
+
/**
|
|
34
|
+
* Construct a new runtime application.
|
|
35
|
+
*
|
|
36
|
+
* Steps performed:
|
|
37
|
+
* - Store runtime configuration
|
|
38
|
+
* - Compose the module pipeline (built-ins + user modules) via RuntimeComposer
|
|
39
|
+
*/
|
|
40
|
+
constructor(config: AppConfig);
|
|
41
|
+
/**
|
|
42
|
+
* Add extra modules programmatically before bootstrap.
|
|
43
|
+
* Useful for integration tests or dynamic composition in code.
|
|
44
|
+
*/
|
|
45
|
+
use(...modules: AppModule[]): void;
|
|
46
|
+
/**
|
|
47
|
+
* Bootstrap the runtime:
|
|
48
|
+
* - Validate and initialize all modules
|
|
49
|
+
* - Register declared providers into the IoC container
|
|
50
|
+
* - Execute each module’s optional `register()` hook
|
|
51
|
+
*
|
|
52
|
+
* Must be called before `listen()`.
|
|
53
|
+
*/
|
|
54
|
+
bootstrap(): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Start the HTTP server via the registered ServerAdapter and return a handle.
|
|
57
|
+
*
|
|
58
|
+
* Lifecycle sequence:
|
|
59
|
+
* 1) Resolve ServerAdapter from IoC
|
|
60
|
+
* 2) Gather OnStart / OnShutdown hooks (LifecycleModule ensures their presence)
|
|
61
|
+
* 3) Delegate to ServerRunner to handle listen/close mechanics
|
|
62
|
+
*
|
|
63
|
+
* Supports both overloads:
|
|
64
|
+
* - listen(port, callback)
|
|
65
|
+
* - listen(port, host, callback)
|
|
66
|
+
*/
|
|
67
|
+
listen(port: number, hostOrCb?: string | (() => void), cb?: () => void): RunningServer;
|
|
68
|
+
/**
|
|
69
|
+
* Expose the underlying IoC container for advanced scenarios:
|
|
70
|
+
* diagnostics, module introspection, or test overrides.
|
|
71
|
+
*
|
|
72
|
+
* Prefer resolving dependencies via typed tokens in normal app code.
|
|
73
|
+
*/
|
|
74
|
+
getContainer(): Container;
|
|
75
|
+
}
|