@morojs/moro 1.0.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 +21 -0
- package/README.md +233 -0
- package/dist/core/config/index.d.ts +19 -0
- package/dist/core/config/index.js +59 -0
- package/dist/core/config/index.js.map +1 -0
- package/dist/core/config/loader.d.ts +6 -0
- package/dist/core/config/loader.js +288 -0
- package/dist/core/config/loader.js.map +1 -0
- package/dist/core/config/schema.d.ts +335 -0
- package/dist/core/config/schema.js +286 -0
- package/dist/core/config/schema.js.map +1 -0
- package/dist/core/config/utils.d.ts +50 -0
- package/dist/core/config/utils.js +185 -0
- package/dist/core/config/utils.js.map +1 -0
- package/dist/core/database/adapters/drizzle.d.ts +29 -0
- package/dist/core/database/adapters/drizzle.js +366 -0
- package/dist/core/database/adapters/drizzle.js.map +1 -0
- package/dist/core/database/adapters/index.d.ts +8 -0
- package/dist/core/database/adapters/index.js +48 -0
- package/dist/core/database/adapters/index.js.map +1 -0
- package/dist/core/database/adapters/mongodb.d.ts +35 -0
- package/dist/core/database/adapters/mongodb.js +215 -0
- package/dist/core/database/adapters/mongodb.js.map +1 -0
- package/dist/core/database/adapters/mysql.d.ts +23 -0
- package/dist/core/database/adapters/mysql.js +149 -0
- package/dist/core/database/adapters/mysql.js.map +1 -0
- package/dist/core/database/adapters/postgresql.d.ts +24 -0
- package/dist/core/database/adapters/postgresql.js +160 -0
- package/dist/core/database/adapters/postgresql.js.map +1 -0
- package/dist/core/database/adapters/redis.d.ts +50 -0
- package/dist/core/database/adapters/redis.js +266 -0
- package/dist/core/database/adapters/redis.js.map +1 -0
- package/dist/core/database/adapters/sqlite.d.ts +23 -0
- package/dist/core/database/adapters/sqlite.js +194 -0
- package/dist/core/database/adapters/sqlite.js.map +1 -0
- package/dist/core/database/index.d.ts +2 -0
- package/dist/core/database/index.js +20 -0
- package/dist/core/database/index.js.map +1 -0
- package/dist/core/docs/index.d.ts +63 -0
- package/dist/core/docs/index.js +170 -0
- package/dist/core/docs/index.js.map +1 -0
- package/dist/core/docs/openapi-generator.d.ts +124 -0
- package/dist/core/docs/openapi-generator.js +413 -0
- package/dist/core/docs/openapi-generator.js.map +1 -0
- package/dist/core/docs/simple-docs.d.ts +21 -0
- package/dist/core/docs/simple-docs.js +268 -0
- package/dist/core/docs/simple-docs.js.map +1 -0
- package/dist/core/docs/swagger-ui.d.ts +28 -0
- package/dist/core/docs/swagger-ui.js +317 -0
- package/dist/core/docs/swagger-ui.js.map +1 -0
- package/dist/core/docs/zod-to-openapi.d.ts +29 -0
- package/dist/core/docs/zod-to-openapi.js +414 -0
- package/dist/core/docs/zod-to-openapi.js.map +1 -0
- package/dist/core/events/event-bus.d.ts +27 -0
- package/dist/core/events/event-bus.js +193 -0
- package/dist/core/events/event-bus.js.map +1 -0
- package/dist/core/events/index.d.ts +2 -0
- package/dist/core/events/index.js +7 -0
- package/dist/core/events/index.js.map +1 -0
- package/dist/core/framework.d.ts +57 -0
- package/dist/core/framework.js +432 -0
- package/dist/core/framework.js.map +1 -0
- package/dist/core/http/http-server.d.ts +114 -0
- package/dist/core/http/http-server.js +1154 -0
- package/dist/core/http/http-server.js.map +1 -0
- package/dist/core/http/index.d.ts +3 -0
- package/dist/core/http/index.js +10 -0
- package/dist/core/http/index.js.map +1 -0
- package/dist/core/http/router.d.ts +14 -0
- package/dist/core/http/router.js +113 -0
- package/dist/core/http/router.js.map +1 -0
- package/dist/core/logger/filters.d.ts +9 -0
- package/dist/core/logger/filters.js +134 -0
- package/dist/core/logger/filters.js.map +1 -0
- package/dist/core/logger/index.d.ts +3 -0
- package/dist/core/logger/index.js +26 -0
- package/dist/core/logger/index.js.map +1 -0
- package/dist/core/logger/logger.d.ts +49 -0
- package/dist/core/logger/logger.js +332 -0
- package/dist/core/logger/logger.js.map +1 -0
- package/dist/core/logger/outputs.d.ts +42 -0
- package/dist/core/logger/outputs.js +110 -0
- package/dist/core/logger/outputs.js.map +1 -0
- package/dist/core/middleware/built-in/adapters/cache/file.d.ts +15 -0
- package/dist/core/middleware/built-in/adapters/cache/file.js +128 -0
- package/dist/core/middleware/built-in/adapters/cache/file.js.map +1 -0
- package/dist/core/middleware/built-in/adapters/cache/index.d.ts +5 -0
- package/dist/core/middleware/built-in/adapters/cache/index.js +28 -0
- package/dist/core/middleware/built-in/adapters/cache/index.js.map +1 -0
- package/dist/core/middleware/built-in/adapters/cache/memory.d.ts +11 -0
- package/dist/core/middleware/built-in/adapters/cache/memory.js +65 -0
- package/dist/core/middleware/built-in/adapters/cache/memory.js.map +1 -0
- package/dist/core/middleware/built-in/adapters/cache/redis.d.ts +17 -0
- package/dist/core/middleware/built-in/adapters/cache/redis.js +91 -0
- package/dist/core/middleware/built-in/adapters/cache/redis.js.map +1 -0
- package/dist/core/middleware/built-in/adapters/cdn/azure.d.ts +21 -0
- package/dist/core/middleware/built-in/adapters/cdn/azure.js +40 -0
- package/dist/core/middleware/built-in/adapters/cdn/azure.js.map +1 -0
- package/dist/core/middleware/built-in/adapters/cdn/cloudflare.d.ts +14 -0
- package/dist/core/middleware/built-in/adapters/cdn/cloudflare.js +77 -0
- package/dist/core/middleware/built-in/adapters/cdn/cloudflare.js.map +1 -0
- package/dist/core/middleware/built-in/adapters/cdn/cloudfront.d.ts +15 -0
- package/dist/core/middleware/built-in/adapters/cdn/cloudfront.js +73 -0
- package/dist/core/middleware/built-in/adapters/cdn/cloudfront.js.map +1 -0
- package/dist/core/middleware/built-in/adapters/cdn/index.d.ts +5 -0
- package/dist/core/middleware/built-in/adapters/cdn/index.js +28 -0
- package/dist/core/middleware/built-in/adapters/cdn/index.js.map +1 -0
- package/dist/core/middleware/built-in/adapters/index.d.ts +4 -0
- package/dist/core/middleware/built-in/adapters/index.js +26 -0
- package/dist/core/middleware/built-in/adapters/index.js.map +1 -0
- package/dist/core/middleware/built-in/auth.d.ts +2 -0
- package/dist/core/middleware/built-in/auth.js +38 -0
- package/dist/core/middleware/built-in/auth.js.map +1 -0
- package/dist/core/middleware/built-in/cache.d.ts +3 -0
- package/dist/core/middleware/built-in/cache.js +188 -0
- package/dist/core/middleware/built-in/cache.js.map +1 -0
- package/dist/core/middleware/built-in/cdn.d.ts +3 -0
- package/dist/core/middleware/built-in/cdn.js +115 -0
- package/dist/core/middleware/built-in/cdn.js.map +1 -0
- package/dist/core/middleware/built-in/cookie.d.ts +14 -0
- package/dist/core/middleware/built-in/cookie.js +68 -0
- package/dist/core/middleware/built-in/cookie.js.map +1 -0
- package/dist/core/middleware/built-in/cors.d.ts +2 -0
- package/dist/core/middleware/built-in/cors.js +29 -0
- package/dist/core/middleware/built-in/cors.js.map +1 -0
- package/dist/core/middleware/built-in/csp.d.ts +22 -0
- package/dist/core/middleware/built-in/csp.js +74 -0
- package/dist/core/middleware/built-in/csp.js.map +1 -0
- package/dist/core/middleware/built-in/csrf.d.ts +9 -0
- package/dist/core/middleware/built-in/csrf.js +66 -0
- package/dist/core/middleware/built-in/csrf.js.map +1 -0
- package/dist/core/middleware/built-in/error-tracker.d.ts +1 -0
- package/dist/core/middleware/built-in/error-tracker.js +19 -0
- package/dist/core/middleware/built-in/error-tracker.js.map +1 -0
- package/dist/core/middleware/built-in/index.d.ts +70 -0
- package/dist/core/middleware/built-in/index.js +70 -0
- package/dist/core/middleware/built-in/index.js.map +1 -0
- package/dist/core/middleware/built-in/performance-monitor.d.ts +1 -0
- package/dist/core/middleware/built-in/performance-monitor.js +22 -0
- package/dist/core/middleware/built-in/performance-monitor.js.map +1 -0
- package/dist/core/middleware/built-in/rate-limit.d.ts +6 -0
- package/dist/core/middleware/built-in/rate-limit.js +47 -0
- package/dist/core/middleware/built-in/rate-limit.js.map +1 -0
- package/dist/core/middleware/built-in/request-logger.d.ts +1 -0
- package/dist/core/middleware/built-in/request-logger.js +15 -0
- package/dist/core/middleware/built-in/request-logger.js.map +1 -0
- package/dist/core/middleware/built-in/session.d.ts +41 -0
- package/dist/core/middleware/built-in/session.js +209 -0
- package/dist/core/middleware/built-in/session.js.map +1 -0
- package/dist/core/middleware/built-in/sse.d.ts +6 -0
- package/dist/core/middleware/built-in/sse.js +73 -0
- package/dist/core/middleware/built-in/sse.js.map +1 -0
- package/dist/core/middleware/built-in/validation.d.ts +2 -0
- package/dist/core/middleware/built-in/validation.js +31 -0
- package/dist/core/middleware/built-in/validation.js.map +1 -0
- package/dist/core/middleware/index.d.ts +21 -0
- package/dist/core/middleware/index.js +152 -0
- package/dist/core/middleware/index.js.map +1 -0
- package/dist/core/modules/auto-discovery.d.ts +27 -0
- package/dist/core/modules/auto-discovery.js +255 -0
- package/dist/core/modules/auto-discovery.js.map +1 -0
- package/dist/core/modules/index.d.ts +2 -0
- package/dist/core/modules/index.js +11 -0
- package/dist/core/modules/index.js.map +1 -0
- package/dist/core/modules/modules.d.ts +10 -0
- package/dist/core/modules/modules.js +137 -0
- package/dist/core/modules/modules.js.map +1 -0
- package/dist/core/networking/index.d.ts +2 -0
- package/dist/core/networking/index.js +9 -0
- package/dist/core/networking/index.js.map +1 -0
- package/dist/core/networking/service-discovery.d.ts +38 -0
- package/dist/core/networking/service-discovery.js +233 -0
- package/dist/core/networking/service-discovery.js.map +1 -0
- package/dist/core/networking/websocket-manager.d.ts +27 -0
- package/dist/core/networking/websocket-manager.js +211 -0
- package/dist/core/networking/websocket-manager.js.map +1 -0
- package/dist/core/routing/app-integration.d.ts +42 -0
- package/dist/core/routing/app-integration.js +152 -0
- package/dist/core/routing/app-integration.js.map +1 -0
- package/dist/core/routing/index.d.ts +106 -0
- package/dist/core/routing/index.js +343 -0
- package/dist/core/routing/index.js.map +1 -0
- package/dist/core/runtime/aws-lambda-adapter.d.ts +43 -0
- package/dist/core/runtime/aws-lambda-adapter.js +108 -0
- package/dist/core/runtime/aws-lambda-adapter.js.map +1 -0
- package/dist/core/runtime/base-adapter.d.ts +16 -0
- package/dist/core/runtime/base-adapter.js +105 -0
- package/dist/core/runtime/base-adapter.js.map +1 -0
- package/dist/core/runtime/cloudflare-workers-adapter.d.ts +18 -0
- package/dist/core/runtime/cloudflare-workers-adapter.js +131 -0
- package/dist/core/runtime/cloudflare-workers-adapter.js.map +1 -0
- package/dist/core/runtime/index.d.ts +14 -0
- package/dist/core/runtime/index.js +56 -0
- package/dist/core/runtime/index.js.map +1 -0
- package/dist/core/runtime/node-adapter.d.ts +15 -0
- package/dist/core/runtime/node-adapter.js +204 -0
- package/dist/core/runtime/node-adapter.js.map +1 -0
- package/dist/core/runtime/vercel-edge-adapter.d.ts +10 -0
- package/dist/core/runtime/vercel-edge-adapter.js +106 -0
- package/dist/core/runtime/vercel-edge-adapter.js.map +1 -0
- package/dist/core/utilities/circuit-breaker.d.ts +14 -0
- package/dist/core/utilities/circuit-breaker.js +42 -0
- package/dist/core/utilities/circuit-breaker.js.map +1 -0
- package/dist/core/utilities/container.d.ts +116 -0
- package/dist/core/utilities/container.js +529 -0
- package/dist/core/utilities/container.js.map +1 -0
- package/dist/core/utilities/hooks.d.ts +24 -0
- package/dist/core/utilities/hooks.js +131 -0
- package/dist/core/utilities/hooks.js.map +1 -0
- package/dist/core/utilities/index.d.ts +4 -0
- package/dist/core/utilities/index.js +22 -0
- package/dist/core/utilities/index.js.map +1 -0
- package/dist/core/validation/index.d.ts +30 -0
- package/dist/core/validation/index.js +144 -0
- package/dist/core/validation/index.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +72 -0
- package/dist/index.js.map +1 -0
- package/dist/moro.d.ts +82 -0
- package/dist/moro.js +679 -0
- package/dist/moro.js.map +1 -0
- package/dist/types/cache.d.ts +34 -0
- package/dist/types/cache.js +3 -0
- package/dist/types/cache.js.map +1 -0
- package/dist/types/cdn.d.ts +19 -0
- package/dist/types/cdn.js +3 -0
- package/dist/types/cdn.js.map +1 -0
- package/dist/types/core.d.ts +13 -0
- package/dist/types/core.js +3 -0
- package/dist/types/core.js.map +1 -0
- package/dist/types/database.d.ts +29 -0
- package/dist/types/database.js +3 -0
- package/dist/types/database.js.map +1 -0
- package/dist/types/discovery.d.ts +6 -0
- package/dist/types/discovery.js +3 -0
- package/dist/types/discovery.js.map +1 -0
- package/dist/types/events.d.ts +116 -0
- package/dist/types/events.js +3 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/hooks.d.ts +38 -0
- package/dist/types/hooks.js +3 -0
- package/dist/types/hooks.js.map +1 -0
- package/dist/types/http.d.ts +51 -0
- package/dist/types/http.js +3 -0
- package/dist/types/http.js.map +1 -0
- package/dist/types/logger.d.ts +77 -0
- package/dist/types/logger.js +3 -0
- package/dist/types/logger.js.map +1 -0
- package/dist/types/module.d.ts +91 -0
- package/dist/types/module.js +3 -0
- package/dist/types/module.js.map +1 -0
- package/dist/types/runtime.d.ts +48 -0
- package/dist/types/runtime.js +3 -0
- package/dist/types/runtime.js.map +1 -0
- package/dist/types/session.d.ts +66 -0
- package/dist/types/session.js +3 -0
- package/dist/types/session.js.map +1 -0
- package/package.json +176 -0
- package/src/core/config/index.ts +47 -0
- package/src/core/config/loader.ts +366 -0
- package/src/core/config/schema.ts +346 -0
- package/src/core/config/utils.ts +220 -0
- package/src/core/database/README.md +228 -0
- package/src/core/database/adapters/drizzle.ts +425 -0
- package/src/core/database/adapters/index.ts +45 -0
- package/src/core/database/adapters/mongodb.ts +292 -0
- package/src/core/database/adapters/mysql.ts +217 -0
- package/src/core/database/adapters/postgresql.ts +211 -0
- package/src/core/database/adapters/redis.ts +331 -0
- package/src/core/database/adapters/sqlite.ts +255 -0
- package/src/core/database/index.ts +3 -0
- package/src/core/docs/index.ts +245 -0
- package/src/core/docs/openapi-generator.ts +588 -0
- package/src/core/docs/simple-docs.ts +305 -0
- package/src/core/docs/swagger-ui.ts +370 -0
- package/src/core/docs/zod-to-openapi.ts +532 -0
- package/src/core/events/event-bus.ts +249 -0
- package/src/core/events/index.ts +12 -0
- package/src/core/framework.ts +621 -0
- package/src/core/http/http-server.ts +1421 -0
- package/src/core/http/index.ts +11 -0
- package/src/core/http/router.ts +153 -0
- package/src/core/logger/filters.ts +148 -0
- package/src/core/logger/index.ts +20 -0
- package/src/core/logger/logger.ts +434 -0
- package/src/core/logger/outputs.ts +136 -0
- package/src/core/middleware/built-in/adapters/cache/file.ts +106 -0
- package/src/core/middleware/built-in/adapters/cache/index.ts +26 -0
- package/src/core/middleware/built-in/adapters/cache/memory.ts +73 -0
- package/src/core/middleware/built-in/adapters/cache/redis.ts +103 -0
- package/src/core/middleware/built-in/adapters/cdn/azure.ts +68 -0
- package/src/core/middleware/built-in/adapters/cdn/cloudflare.ts +100 -0
- package/src/core/middleware/built-in/adapters/cdn/cloudfront.ts +92 -0
- package/src/core/middleware/built-in/adapters/cdn/index.ts +23 -0
- package/src/core/middleware/built-in/adapters/index.ts +7 -0
- package/src/core/middleware/built-in/auth.ts +39 -0
- package/src/core/middleware/built-in/cache.ts +228 -0
- package/src/core/middleware/built-in/cdn.ts +151 -0
- package/src/core/middleware/built-in/cookie.ts +90 -0
- package/src/core/middleware/built-in/cors.ts +38 -0
- package/src/core/middleware/built-in/csp.ts +107 -0
- package/src/core/middleware/built-in/csrf.ts +87 -0
- package/src/core/middleware/built-in/error-tracker.ts +16 -0
- package/src/core/middleware/built-in/index.ts +57 -0
- package/src/core/middleware/built-in/performance-monitor.ts +25 -0
- package/src/core/middleware/built-in/rate-limit.ts +60 -0
- package/src/core/middleware/built-in/request-logger.ts +14 -0
- package/src/core/middleware/built-in/session.ts +311 -0
- package/src/core/middleware/built-in/sse.ts +91 -0
- package/src/core/middleware/built-in/validation.ts +33 -0
- package/src/core/middleware/index.ts +188 -0
- package/src/core/modules/auto-discovery.ts +265 -0
- package/src/core/modules/index.ts +6 -0
- package/src/core/modules/modules.ts +125 -0
- package/src/core/networking/index.ts +7 -0
- package/src/core/networking/service-discovery.ts +309 -0
- package/src/core/networking/websocket-manager.ts +259 -0
- package/src/core/routing/app-integration.ts +229 -0
- package/src/core/routing/index.ts +519 -0
- package/src/core/runtime/aws-lambda-adapter.ts +157 -0
- package/src/core/runtime/base-adapter.ts +140 -0
- package/src/core/runtime/cloudflare-workers-adapter.ts +166 -0
- package/src/core/runtime/index.ts +74 -0
- package/src/core/runtime/node-adapter.ts +210 -0
- package/src/core/runtime/vercel-edge-adapter.ts +125 -0
- package/src/core/utilities/circuit-breaker.ts +46 -0
- package/src/core/utilities/container.ts +760 -0
- package/src/core/utilities/hooks.ts +148 -0
- package/src/core/utilities/index.ts +16 -0
- package/src/core/validation/index.ts +216 -0
- package/src/index.ts +120 -0
- package/src/moro.ts +842 -0
- package/src/types/cache.ts +38 -0
- package/src/types/cdn.ts +22 -0
- package/src/types/core.ts +17 -0
- package/src/types/database.ts +40 -0
- package/src/types/discovery.ts +7 -0
- package/src/types/events.ts +90 -0
- package/src/types/hooks.ts +47 -0
- package/src/types/http.ts +70 -0
- package/src/types/logger.ts +109 -0
- package/src/types/module.ts +87 -0
- package/src/types/runtime.ts +91 -0
- package/src/types/session.ts +89 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,760 @@
|
|
|
1
|
+
// Enhanced Functional Dependency Injection Container
|
|
2
|
+
import { EventEmitter } from "events";
|
|
3
|
+
|
|
4
|
+
// Service lifecycle states
|
|
5
|
+
export enum ServiceLifecycle {
|
|
6
|
+
UNINITIALIZED = "uninitialized",
|
|
7
|
+
INITIALIZING = "initializing",
|
|
8
|
+
INITIALIZED = "initialized",
|
|
9
|
+
DISPOSING = "disposing",
|
|
10
|
+
DISPOSED = "disposed",
|
|
11
|
+
ERROR = "error",
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Service scopes
|
|
15
|
+
export enum ServiceScope {
|
|
16
|
+
SINGLETON = "singleton", // One instance per container
|
|
17
|
+
TRANSIENT = "transient", // New instance every time
|
|
18
|
+
REQUEST = "request", // One instance per request context
|
|
19
|
+
MODULE = "module", // One instance per module
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Service metadata and configuration
|
|
23
|
+
export interface ServiceMetadata {
|
|
24
|
+
name: string;
|
|
25
|
+
scope: ServiceScope;
|
|
26
|
+
tags: string[];
|
|
27
|
+
dependencies: string[];
|
|
28
|
+
optional: string[];
|
|
29
|
+
lifecycle?: {
|
|
30
|
+
init?: () => Promise<void> | void;
|
|
31
|
+
dispose?: () => Promise<void> | void;
|
|
32
|
+
healthCheck?: () => Promise<boolean> | boolean;
|
|
33
|
+
};
|
|
34
|
+
fallback?: () => any;
|
|
35
|
+
timeout?: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Service definition with functional patterns
|
|
39
|
+
export interface ServiceDefinition<T = any> {
|
|
40
|
+
factory: ServiceFactory<T>;
|
|
41
|
+
metadata: ServiceMetadata;
|
|
42
|
+
interceptors: ServiceInterceptor[];
|
|
43
|
+
decorators: ServiceDecorator<T>[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Functional factory type
|
|
47
|
+
export type ServiceFactory<T> = (
|
|
48
|
+
dependencies: Record<string, any>,
|
|
49
|
+
context?: ServiceContext,
|
|
50
|
+
) => T | Promise<T>;
|
|
51
|
+
|
|
52
|
+
// Service interceptor for AOP patterns
|
|
53
|
+
export type ServiceInterceptor = (
|
|
54
|
+
serviceName: string,
|
|
55
|
+
dependencies: Record<string, any>,
|
|
56
|
+
context: ServiceContext,
|
|
57
|
+
next: () => any,
|
|
58
|
+
) => any | Promise<any>;
|
|
59
|
+
|
|
60
|
+
// Service decorator for functional composition
|
|
61
|
+
export type ServiceDecorator<T> = (
|
|
62
|
+
instance: T,
|
|
63
|
+
context: ServiceContext,
|
|
64
|
+
) => T | Promise<T>;
|
|
65
|
+
|
|
66
|
+
// Service context for request-scoped services
|
|
67
|
+
export interface ServiceContext {
|
|
68
|
+
requestId?: string;
|
|
69
|
+
moduleId?: string;
|
|
70
|
+
metadata: Record<string, any>;
|
|
71
|
+
timestamp: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Service instance wrapper
|
|
75
|
+
interface ServiceInstance<T = any> {
|
|
76
|
+
value: T;
|
|
77
|
+
metadata: ServiceMetadata;
|
|
78
|
+
lifecycle: ServiceLifecycle;
|
|
79
|
+
lastAccessed: number;
|
|
80
|
+
accessCount: number;
|
|
81
|
+
context?: ServiceContext;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Higher-order functions for service composition
|
|
85
|
+
export const withLogging =
|
|
86
|
+
<T>(logger: any) =>
|
|
87
|
+
(factory: ServiceFactory<T>): ServiceFactory<T> =>
|
|
88
|
+
(deps, ctx) => {
|
|
89
|
+
logger.debug(
|
|
90
|
+
`Creating service with dependencies: ${Object.keys(deps).join(", ")}`,
|
|
91
|
+
);
|
|
92
|
+
const start = Date.now();
|
|
93
|
+
const result = factory(deps, ctx);
|
|
94
|
+
logger.debug(`Service created in ${Date.now() - start}ms`);
|
|
95
|
+
return result;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export const withCaching =
|
|
99
|
+
<T>(ttl = 300000) =>
|
|
100
|
+
(factory: ServiceFactory<T>): ServiceFactory<T> => {
|
|
101
|
+
const cache = new Map<string, { value: T; expires: number }>();
|
|
102
|
+
return async (deps, ctx) => {
|
|
103
|
+
const key = `${ctx?.requestId || "global"}_${JSON.stringify(deps)}`;
|
|
104
|
+
const cached = cache.get(key);
|
|
105
|
+
if (cached && cached.expires > Date.now()) {
|
|
106
|
+
return cached.value;
|
|
107
|
+
}
|
|
108
|
+
const result = await factory(deps, ctx);
|
|
109
|
+
cache.set(key, { value: result, expires: Date.now() + ttl });
|
|
110
|
+
return result;
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export const withRetry =
|
|
115
|
+
<T>(maxRetries = 3, delay = 1000) =>
|
|
116
|
+
(factory: ServiceFactory<T>): ServiceFactory<T> =>
|
|
117
|
+
async (deps, ctx) => {
|
|
118
|
+
let lastError: Error | null = null;
|
|
119
|
+
for (let i = 0; i <= maxRetries; i++) {
|
|
120
|
+
try {
|
|
121
|
+
return await factory(deps, ctx);
|
|
122
|
+
} catch (error) {
|
|
123
|
+
lastError = error as Error;
|
|
124
|
+
if (i < maxRetries) {
|
|
125
|
+
await new Promise((resolve) =>
|
|
126
|
+
setTimeout(resolve, delay * Math.pow(2, i)),
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
throw lastError;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const withTimeout =
|
|
135
|
+
<T>(timeoutMs = 5000) =>
|
|
136
|
+
(factory: ServiceFactory<T>): ServiceFactory<T> =>
|
|
137
|
+
async (deps, ctx) => {
|
|
138
|
+
return Promise.race([
|
|
139
|
+
factory(deps, ctx),
|
|
140
|
+
new Promise<never>((_, reject) =>
|
|
141
|
+
setTimeout(
|
|
142
|
+
() =>
|
|
143
|
+
reject(new Error(`Service creation timeout after ${timeoutMs}ms`)),
|
|
144
|
+
timeoutMs,
|
|
145
|
+
),
|
|
146
|
+
),
|
|
147
|
+
]);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// Enhanced Functional Container
|
|
151
|
+
export class FunctionalContainer extends EventEmitter {
|
|
152
|
+
private services = new Map<string, ServiceDefinition>();
|
|
153
|
+
private instances = new Map<string, ServiceInstance>();
|
|
154
|
+
private requestScopes = new Map<string, Map<string, ServiceInstance>>();
|
|
155
|
+
private moduleScopes = new Map<string, Map<string, ServiceInstance>>();
|
|
156
|
+
private globalInterceptors: ServiceInterceptor[] = [];
|
|
157
|
+
private cleanupInterval?: NodeJS.Timeout;
|
|
158
|
+
|
|
159
|
+
constructor() {
|
|
160
|
+
super();
|
|
161
|
+
this.setupCleanup();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Fluent registration API
|
|
165
|
+
register<T>(name: string): ServiceRegistrationBuilder<T> {
|
|
166
|
+
return new ServiceRegistrationBuilder<T>(this, name);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Direct registration for simple cases
|
|
170
|
+
singleton<T>(name: string, factory: ServiceFactory<T>): this {
|
|
171
|
+
this.register<T>(name).singleton().factory(factory).build();
|
|
172
|
+
return this;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
transient<T>(name: string, factory: ServiceFactory<T>): this {
|
|
176
|
+
this.register<T>(name).transient().factory(factory).build();
|
|
177
|
+
return this;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Functional service registration with HOFs
|
|
181
|
+
compose<T>(
|
|
182
|
+
name: string,
|
|
183
|
+
...compositionFns: Array<(factory: ServiceFactory<T>) => ServiceFactory<T>>
|
|
184
|
+
): ServiceRegistrationBuilder<T> {
|
|
185
|
+
const builder = this.register<T>(name);
|
|
186
|
+
return builder.compose(...compositionFns);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Enhanced resolution with context
|
|
190
|
+
async resolve<T>(name: string, context?: ServiceContext): Promise<T> {
|
|
191
|
+
const service = this.services.get(name);
|
|
192
|
+
if (!service) {
|
|
193
|
+
throw new Error(`Service '${name}' not registered`);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const scopeKey = this.getScopeKey(name, service.metadata.scope, context);
|
|
197
|
+
const instanceMap = this.getInstanceMap(service.metadata.scope, context);
|
|
198
|
+
|
|
199
|
+
let instance = instanceMap.get(scopeKey);
|
|
200
|
+
|
|
201
|
+
if (!instance || this.shouldRecreate(instance, service.metadata)) {
|
|
202
|
+
instance = await this.createInstance(name, service, context);
|
|
203
|
+
instanceMap.set(scopeKey, instance);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
instance.lastAccessed = Date.now();
|
|
207
|
+
instance.accessCount++;
|
|
208
|
+
|
|
209
|
+
return instance.value;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Synchronous resolution for non-async services
|
|
213
|
+
resolveSync<T>(name: string, context?: ServiceContext): T {
|
|
214
|
+
const service = this.services.get(name);
|
|
215
|
+
if (!service) {
|
|
216
|
+
throw new Error(`Service '${name}' not registered`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const scopeKey = this.getScopeKey(name, service.metadata.scope, context);
|
|
220
|
+
const instanceMap = this.getInstanceMap(service.metadata.scope, context);
|
|
221
|
+
|
|
222
|
+
let instance = instanceMap.get(scopeKey);
|
|
223
|
+
|
|
224
|
+
if (!instance || this.shouldRecreate(instance, service.metadata)) {
|
|
225
|
+
const result = this.createInstanceSync(name, service, context);
|
|
226
|
+
instance = {
|
|
227
|
+
value: result,
|
|
228
|
+
metadata: service.metadata,
|
|
229
|
+
lifecycle: ServiceLifecycle.INITIALIZED,
|
|
230
|
+
lastAccessed: Date.now(),
|
|
231
|
+
accessCount: 1,
|
|
232
|
+
context,
|
|
233
|
+
};
|
|
234
|
+
instanceMap.set(scopeKey, instance);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
instance.lastAccessed = Date.now();
|
|
238
|
+
instance.accessCount++;
|
|
239
|
+
|
|
240
|
+
return instance.value;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Add global interceptors
|
|
244
|
+
addInterceptor(interceptor: ServiceInterceptor): this {
|
|
245
|
+
this.globalInterceptors.push(interceptor);
|
|
246
|
+
return this;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Service health checks
|
|
250
|
+
async healthCheck(): Promise<Record<string, boolean>> {
|
|
251
|
+
const results: Record<string, boolean> = {};
|
|
252
|
+
|
|
253
|
+
for (const [name, instance] of this.instances) {
|
|
254
|
+
const service = this.services.get(name);
|
|
255
|
+
if (service?.metadata.lifecycle?.healthCheck) {
|
|
256
|
+
try {
|
|
257
|
+
results[name] = await service.metadata.lifecycle.healthCheck();
|
|
258
|
+
} catch {
|
|
259
|
+
results[name] = false;
|
|
260
|
+
}
|
|
261
|
+
} else {
|
|
262
|
+
results[name] = instance.lifecycle === ServiceLifecycle.INITIALIZED;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return results;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Clear request-scoped services
|
|
270
|
+
clearRequestScope(requestId: string): void {
|
|
271
|
+
const requestScope = this.requestScopes.get(requestId);
|
|
272
|
+
if (requestScope) {
|
|
273
|
+
requestScope.clear();
|
|
274
|
+
this.requestScopes.delete(requestId);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Clear module-scoped services
|
|
279
|
+
clearModuleScope(moduleId: string): void {
|
|
280
|
+
const moduleScope = this.moduleScopes.get(moduleId);
|
|
281
|
+
if (moduleScope) {
|
|
282
|
+
moduleScope.clear();
|
|
283
|
+
this.moduleScopes.delete(moduleId);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Service introspection
|
|
288
|
+
getServiceInfo(): Record<string, any> {
|
|
289
|
+
const info: Record<string, any> = {};
|
|
290
|
+
|
|
291
|
+
for (const [name, service] of this.services) {
|
|
292
|
+
const instance = this.instances.get(name);
|
|
293
|
+
info[name] = {
|
|
294
|
+
scope: service.metadata.scope,
|
|
295
|
+
dependencies: service.metadata.dependencies,
|
|
296
|
+
tags: service.metadata.tags,
|
|
297
|
+
lifecycle: instance?.lifecycle || ServiceLifecycle.UNINITIALIZED,
|
|
298
|
+
accessCount: instance?.accessCount || 0,
|
|
299
|
+
lastAccessed: instance?.lastAccessed
|
|
300
|
+
? new Date(instance.lastAccessed).toISOString()
|
|
301
|
+
: null,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return info;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Dispose all services
|
|
309
|
+
async dispose(): Promise<void> {
|
|
310
|
+
for (const [name, instance] of this.instances) {
|
|
311
|
+
const service = this.services.get(name);
|
|
312
|
+
if (
|
|
313
|
+
service?.metadata.lifecycle?.dispose &&
|
|
314
|
+
instance.lifecycle === ServiceLifecycle.INITIALIZED
|
|
315
|
+
) {
|
|
316
|
+
try {
|
|
317
|
+
instance.lifecycle = ServiceLifecycle.DISPOSING;
|
|
318
|
+
await service.metadata.lifecycle.dispose();
|
|
319
|
+
instance.lifecycle = ServiceLifecycle.DISPOSED;
|
|
320
|
+
} catch (error) {
|
|
321
|
+
instance.lifecycle = ServiceLifecycle.ERROR;
|
|
322
|
+
this.emit("disposeError", { name, error });
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
this.instances.clear();
|
|
328
|
+
this.requestScopes.clear();
|
|
329
|
+
this.moduleScopes.clear();
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Internal implementation methods
|
|
333
|
+
private async createInstance<T>(
|
|
334
|
+
name: string,
|
|
335
|
+
service: ServiceDefinition<T>,
|
|
336
|
+
context?: ServiceContext,
|
|
337
|
+
): Promise<ServiceInstance<T>> {
|
|
338
|
+
const instance: ServiceInstance<T> = {
|
|
339
|
+
value: undefined as any,
|
|
340
|
+
metadata: service.metadata,
|
|
341
|
+
lifecycle: ServiceLifecycle.INITIALIZING,
|
|
342
|
+
lastAccessed: Date.now(),
|
|
343
|
+
accessCount: 0,
|
|
344
|
+
context,
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
try {
|
|
348
|
+
// Resolve dependencies
|
|
349
|
+
const dependencies = await this.resolveDependencies(
|
|
350
|
+
service.metadata,
|
|
351
|
+
context,
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
// Apply interceptors
|
|
355
|
+
const interceptedFactory = this.applyInterceptors(
|
|
356
|
+
name,
|
|
357
|
+
service.factory,
|
|
358
|
+
dependencies,
|
|
359
|
+
context,
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
// Create instance
|
|
363
|
+
instance.value = await interceptedFactory();
|
|
364
|
+
|
|
365
|
+
// Apply decorators
|
|
366
|
+
instance.value = await this.applyDecorators(
|
|
367
|
+
instance.value,
|
|
368
|
+
service.decorators,
|
|
369
|
+
context,
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
// Run initialization lifecycle
|
|
373
|
+
if (service.metadata.lifecycle?.init) {
|
|
374
|
+
await service.metadata.lifecycle.init();
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
instance.lifecycle = ServiceLifecycle.INITIALIZED;
|
|
378
|
+
this.emit("serviceCreated", { name, instance });
|
|
379
|
+
} catch (error) {
|
|
380
|
+
instance.lifecycle = ServiceLifecycle.ERROR;
|
|
381
|
+
this.emit("serviceError", { name, error });
|
|
382
|
+
|
|
383
|
+
// Try fallback if available
|
|
384
|
+
if (service.metadata.fallback) {
|
|
385
|
+
instance.value = service.metadata.fallback();
|
|
386
|
+
instance.lifecycle = ServiceLifecycle.INITIALIZED;
|
|
387
|
+
} else {
|
|
388
|
+
throw error;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return instance;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
private createInstanceSync<T>(
|
|
396
|
+
name: string,
|
|
397
|
+
service: ServiceDefinition<T>,
|
|
398
|
+
context?: ServiceContext,
|
|
399
|
+
): T {
|
|
400
|
+
// Simplified sync version - no async dependencies or lifecycle
|
|
401
|
+
const dependencies = this.resolveDependenciesSync(
|
|
402
|
+
service.metadata,
|
|
403
|
+
context,
|
|
404
|
+
);
|
|
405
|
+
return service.factory(dependencies, context) as T;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
private async resolveDependencies(
|
|
409
|
+
metadata: ServiceMetadata,
|
|
410
|
+
context?: ServiceContext,
|
|
411
|
+
): Promise<Record<string, any>> {
|
|
412
|
+
const dependencies: Record<string, any> = {};
|
|
413
|
+
|
|
414
|
+
for (const dep of metadata.dependencies) {
|
|
415
|
+
dependencies[dep] = await this.resolve(dep, context);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
for (const optDep of metadata.optional) {
|
|
419
|
+
try {
|
|
420
|
+
dependencies[optDep] = await this.resolve(optDep, context);
|
|
421
|
+
} catch {
|
|
422
|
+
// Optional dependency - continue without it
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return dependencies;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
private resolveDependenciesSync(
|
|
430
|
+
metadata: ServiceMetadata,
|
|
431
|
+
context?: ServiceContext,
|
|
432
|
+
): Record<string, any> {
|
|
433
|
+
const dependencies: Record<string, any> = {};
|
|
434
|
+
|
|
435
|
+
for (const dep of metadata.dependencies) {
|
|
436
|
+
dependencies[dep] = this.resolveSync(dep, context);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
for (const optDep of metadata.optional) {
|
|
440
|
+
try {
|
|
441
|
+
dependencies[optDep] = this.resolveSync(optDep, context);
|
|
442
|
+
} catch {
|
|
443
|
+
// Optional dependency - continue without it
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
return dependencies;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
private applyInterceptors(
|
|
451
|
+
name: string,
|
|
452
|
+
factory: ServiceFactory<any>,
|
|
453
|
+
dependencies: Record<string, any>,
|
|
454
|
+
context?: ServiceContext,
|
|
455
|
+
): () => any {
|
|
456
|
+
return [...this.globalInterceptors].reduceRight(
|
|
457
|
+
(next: () => any, interceptor: ServiceInterceptor) => () =>
|
|
458
|
+
interceptor(
|
|
459
|
+
name,
|
|
460
|
+
dependencies,
|
|
461
|
+
context || this.createDefaultContext(),
|
|
462
|
+
next,
|
|
463
|
+
),
|
|
464
|
+
() => factory(dependencies, context),
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
private async applyDecorators<T>(
|
|
469
|
+
instance: T,
|
|
470
|
+
decorators: ServiceDecorator<T>[],
|
|
471
|
+
context?: ServiceContext,
|
|
472
|
+
): Promise<T> {
|
|
473
|
+
let result = instance;
|
|
474
|
+
for (const decorator of decorators) {
|
|
475
|
+
result = await decorator(result, context || this.createDefaultContext());
|
|
476
|
+
}
|
|
477
|
+
return result;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
private getScopeKey(
|
|
481
|
+
serviceName: string,
|
|
482
|
+
scope: ServiceScope,
|
|
483
|
+
context?: ServiceContext,
|
|
484
|
+
): string {
|
|
485
|
+
switch (scope) {
|
|
486
|
+
case ServiceScope.REQUEST:
|
|
487
|
+
return `${serviceName}:${context?.requestId || "default-request"}`;
|
|
488
|
+
case ServiceScope.MODULE:
|
|
489
|
+
return `${serviceName}:${context?.moduleId || "default-module"}`;
|
|
490
|
+
default:
|
|
491
|
+
return serviceName; // Each singleton service gets its own key
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
private getInstanceMap(
|
|
496
|
+
scope: ServiceScope,
|
|
497
|
+
context?: ServiceContext,
|
|
498
|
+
): Map<string, ServiceInstance> {
|
|
499
|
+
switch (scope) {
|
|
500
|
+
case ServiceScope.REQUEST: {
|
|
501
|
+
const requestId = context?.requestId || "default-request";
|
|
502
|
+
if (!this.requestScopes.has(requestId)) {
|
|
503
|
+
this.requestScopes.set(requestId, new Map());
|
|
504
|
+
}
|
|
505
|
+
return this.requestScopes.get(requestId)!;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
case ServiceScope.MODULE: {
|
|
509
|
+
const moduleId = context?.moduleId || "default-module";
|
|
510
|
+
if (!this.moduleScopes.has(moduleId)) {
|
|
511
|
+
this.moduleScopes.set(moduleId, new Map());
|
|
512
|
+
}
|
|
513
|
+
return this.moduleScopes.get(moduleId)!;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
default:
|
|
517
|
+
return this.instances;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
private shouldRecreate(
|
|
522
|
+
instance: ServiceInstance,
|
|
523
|
+
metadata: ServiceMetadata,
|
|
524
|
+
): boolean {
|
|
525
|
+
return (
|
|
526
|
+
metadata.scope === ServiceScope.TRANSIENT ||
|
|
527
|
+
instance.lifecycle === ServiceLifecycle.ERROR ||
|
|
528
|
+
instance.lifecycle === ServiceLifecycle.DISPOSED
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
private createDefaultContext(): ServiceContext {
|
|
533
|
+
return {
|
|
534
|
+
metadata: {},
|
|
535
|
+
timestamp: Date.now(),
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
private setupCleanup(): void {
|
|
540
|
+
// Cleanup request scopes after timeout
|
|
541
|
+
this.cleanupInterval = setInterval(
|
|
542
|
+
() => {
|
|
543
|
+
const now = Date.now();
|
|
544
|
+
const timeout = 30 * 60 * 1000; // 30 minutes
|
|
545
|
+
|
|
546
|
+
for (const [requestId, scope] of this.requestScopes) {
|
|
547
|
+
const hasRecentActivity = Array.from(scope.values()).some(
|
|
548
|
+
(instance) => now - instance.lastAccessed < timeout,
|
|
549
|
+
);
|
|
550
|
+
|
|
551
|
+
if (!hasRecentActivity) {
|
|
552
|
+
this.clearRequestScope(requestId);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
},
|
|
556
|
+
5 * 60 * 1000,
|
|
557
|
+
); // Check every 5 minutes
|
|
558
|
+
|
|
559
|
+
// Unref the interval so it doesn't keep the process alive during testing
|
|
560
|
+
this.cleanupInterval.unref();
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// Cleanup and destroy the container
|
|
564
|
+
destroy(): void {
|
|
565
|
+
if (this.cleanupInterval) {
|
|
566
|
+
clearInterval(this.cleanupInterval);
|
|
567
|
+
this.cleanupInterval = undefined;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// Clear all scopes
|
|
571
|
+
this.requestScopes.clear();
|
|
572
|
+
this.moduleScopes.clear();
|
|
573
|
+
this.instances.clear();
|
|
574
|
+
this.services.clear();
|
|
575
|
+
|
|
576
|
+
this.emit("containerDestroyed");
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// Internal registration method
|
|
580
|
+
_registerService<T>(name: string, definition: ServiceDefinition<T>): this {
|
|
581
|
+
this.services.set(name, definition);
|
|
582
|
+
this.emit("serviceRegistered", { name, metadata: definition.metadata });
|
|
583
|
+
return this;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// Check if service exists
|
|
587
|
+
has(name: string): boolean {
|
|
588
|
+
return this.services.has(name);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// Fluent registration builder
|
|
593
|
+
export class ServiceRegistrationBuilder<T> {
|
|
594
|
+
private metadata: Partial<ServiceMetadata> = {
|
|
595
|
+
scope: ServiceScope.SINGLETON,
|
|
596
|
+
tags: [],
|
|
597
|
+
dependencies: [],
|
|
598
|
+
optional: [],
|
|
599
|
+
};
|
|
600
|
+
private _factory?: ServiceFactory<T>;
|
|
601
|
+
private interceptors: ServiceInterceptor[] = [];
|
|
602
|
+
private decorators: ServiceDecorator<T>[] = [];
|
|
603
|
+
|
|
604
|
+
constructor(
|
|
605
|
+
private container: FunctionalContainer,
|
|
606
|
+
private name: string,
|
|
607
|
+
) {}
|
|
608
|
+
|
|
609
|
+
// Scope configuration
|
|
610
|
+
singleton(): this {
|
|
611
|
+
this.metadata.scope = ServiceScope.SINGLETON;
|
|
612
|
+
return this;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
transient(): this {
|
|
616
|
+
this.metadata.scope = ServiceScope.TRANSIENT;
|
|
617
|
+
return this;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
requestScoped(): this {
|
|
621
|
+
this.metadata.scope = ServiceScope.REQUEST;
|
|
622
|
+
return this;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
moduleScoped(): this {
|
|
626
|
+
this.metadata.scope = ServiceScope.MODULE;
|
|
627
|
+
return this;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// Dependencies
|
|
631
|
+
dependsOn(...deps: string[]): this {
|
|
632
|
+
this.metadata.dependencies = [
|
|
633
|
+
...(this.metadata.dependencies || []),
|
|
634
|
+
...deps,
|
|
635
|
+
];
|
|
636
|
+
return this;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
optionalDependsOn(...deps: string[]): this {
|
|
640
|
+
this.metadata.optional = [...(this.metadata.optional || []), ...deps];
|
|
641
|
+
return this;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// Metadata
|
|
645
|
+
tags(...tags: string[]): this {
|
|
646
|
+
this.metadata.tags = [...(this.metadata.tags || []), ...tags];
|
|
647
|
+
return this;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// Lifecycle
|
|
651
|
+
onInit(initFn: () => Promise<void> | void): this {
|
|
652
|
+
this.metadata.lifecycle = { ...this.metadata.lifecycle, init: initFn };
|
|
653
|
+
return this;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
onDispose(disposeFn: () => Promise<void> | void): this {
|
|
657
|
+
this.metadata.lifecycle = {
|
|
658
|
+
...this.metadata.lifecycle,
|
|
659
|
+
dispose: disposeFn,
|
|
660
|
+
};
|
|
661
|
+
return this;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
healthCheck(healthFn: () => Promise<boolean> | boolean): this {
|
|
665
|
+
this.metadata.lifecycle = {
|
|
666
|
+
...this.metadata.lifecycle,
|
|
667
|
+
healthCheck: healthFn,
|
|
668
|
+
};
|
|
669
|
+
return this;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
fallback(fallbackFn: () => T): this {
|
|
673
|
+
this.metadata.fallback = fallbackFn;
|
|
674
|
+
return this;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
timeout(ms: number): this {
|
|
678
|
+
this.metadata.timeout = ms;
|
|
679
|
+
return this;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// Factory and composition
|
|
683
|
+
factory(factory: ServiceFactory<T>): this {
|
|
684
|
+
this._factory = factory;
|
|
685
|
+
return this;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
compose(
|
|
689
|
+
...compositionFns: Array<(factory: ServiceFactory<T>) => ServiceFactory<T>>
|
|
690
|
+
): this {
|
|
691
|
+
if (!this._factory) {
|
|
692
|
+
throw new Error("Factory must be set before composition");
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
this._factory = compositionFns.reduce((acc, fn) => fn(acc), this._factory);
|
|
696
|
+
return this;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// Interceptors and decorators
|
|
700
|
+
intercept(interceptor: ServiceInterceptor): this {
|
|
701
|
+
this.interceptors.push(interceptor);
|
|
702
|
+
return this;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
decorate(decorator: ServiceDecorator<T>): this {
|
|
706
|
+
this.decorators.push(decorator);
|
|
707
|
+
return this;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
// Build and register
|
|
711
|
+
build(): FunctionalContainer {
|
|
712
|
+
if (!this._factory) {
|
|
713
|
+
throw new Error(`Factory not provided for service '${this.name}'`);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
const definition: ServiceDefinition<T> = {
|
|
717
|
+
factory: this._factory,
|
|
718
|
+
metadata: {
|
|
719
|
+
name: this.name,
|
|
720
|
+
scope: this.metadata.scope!,
|
|
721
|
+
tags: this.metadata.tags!,
|
|
722
|
+
dependencies: this.metadata.dependencies!,
|
|
723
|
+
optional: this.metadata.optional!,
|
|
724
|
+
lifecycle: this.metadata.lifecycle,
|
|
725
|
+
fallback: this.metadata.fallback,
|
|
726
|
+
timeout: this.metadata.timeout,
|
|
727
|
+
},
|
|
728
|
+
interceptors: this.interceptors,
|
|
729
|
+
decorators: this.decorators,
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
return this.container._registerService(this.name, definition);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// Standard Container class
|
|
737
|
+
export class Container {
|
|
738
|
+
private functionalContainer = new FunctionalContainer();
|
|
739
|
+
|
|
740
|
+
register<T>(name: string, factory: () => T, singleton = false): void {
|
|
741
|
+
this.functionalContainer
|
|
742
|
+
.register<T>(name)
|
|
743
|
+
.factory(() => factory())
|
|
744
|
+
[singleton ? "singleton" : "transient"]()
|
|
745
|
+
.build();
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
resolve<T>(name: string): T {
|
|
749
|
+
return this.functionalContainer.resolveSync<T>(name);
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
has(name: string): boolean {
|
|
753
|
+
return this.functionalContainer.has(name);
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// Expose enhanced container for migration
|
|
757
|
+
getEnhanced(): FunctionalContainer {
|
|
758
|
+
return this.functionalContainer;
|
|
759
|
+
}
|
|
760
|
+
}
|