@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
package/src/moro.ts
ADDED
|
@@ -0,0 +1,842 @@
|
|
|
1
|
+
// Moro Framework - Modern TypeScript API Framework
|
|
2
|
+
// Built for developers who demand performance, elegance, and zero compromises
|
|
3
|
+
// Event-driven • Modular • Enterprise-ready • Developer-first
|
|
4
|
+
import { Moro as MoroCore } from "./core/framework";
|
|
5
|
+
import { HttpRequest, HttpResponse, middleware } from "./core/http";
|
|
6
|
+
import { ModuleConfig, InternalRouteDefinition } from "./types/module";
|
|
7
|
+
import { MoroOptions } from "./types/core";
|
|
8
|
+
import { MoroEventBus } from "./core/events";
|
|
9
|
+
import {
|
|
10
|
+
createFrameworkLogger,
|
|
11
|
+
logger as globalLogger,
|
|
12
|
+
applyLoggingConfiguration,
|
|
13
|
+
} from "./core/logger";
|
|
14
|
+
import { IntelligentRoutingManager } from "./core/routing/app-integration";
|
|
15
|
+
import { RouteBuilder, RouteSchema, CompiledRoute } from "./core/routing";
|
|
16
|
+
import { AppDocumentationManager, DocsConfig } from "./core/docs";
|
|
17
|
+
import { readdirSync, statSync } from "fs";
|
|
18
|
+
import { join } from "path";
|
|
19
|
+
import { EventEmitter } from "events";
|
|
20
|
+
// Configuration System Integration
|
|
21
|
+
import {
|
|
22
|
+
initializeConfig,
|
|
23
|
+
getGlobalConfig,
|
|
24
|
+
type AppConfig,
|
|
25
|
+
} from "./core/config";
|
|
26
|
+
// Runtime System Integration
|
|
27
|
+
import {
|
|
28
|
+
RuntimeAdapter,
|
|
29
|
+
RuntimeType,
|
|
30
|
+
createRuntimeAdapter,
|
|
31
|
+
NodeRuntimeAdapter,
|
|
32
|
+
} from "./core/runtime";
|
|
33
|
+
|
|
34
|
+
export class Moro extends EventEmitter {
|
|
35
|
+
private coreFramework: MoroCore;
|
|
36
|
+
private routes: InternalRouteDefinition[] = [];
|
|
37
|
+
private moduleCounter = 0;
|
|
38
|
+
private loadedModules = new Set<string>();
|
|
39
|
+
private routeHandlers: Record<string, Function> = {};
|
|
40
|
+
// Enterprise event system integration
|
|
41
|
+
private eventBus: MoroEventBus;
|
|
42
|
+
// Application logger
|
|
43
|
+
private logger = createFrameworkLogger("App");
|
|
44
|
+
// Intelligent routing system
|
|
45
|
+
private intelligentRouting = new IntelligentRoutingManager();
|
|
46
|
+
// Documentation system
|
|
47
|
+
private documentation = new AppDocumentationManager();
|
|
48
|
+
// Configuration system
|
|
49
|
+
private config: AppConfig;
|
|
50
|
+
// Runtime system
|
|
51
|
+
private runtimeAdapter: RuntimeAdapter;
|
|
52
|
+
private runtimeType: RuntimeType;
|
|
53
|
+
|
|
54
|
+
constructor(options: MoroOptions = {}) {
|
|
55
|
+
super(); // Call EventEmitter constructor
|
|
56
|
+
|
|
57
|
+
// Configure logger from environment variables BEFORE config system initialization
|
|
58
|
+
// This ensures the config loading process respects the log level
|
|
59
|
+
const envLogLevel = process.env.LOG_LEVEL || process.env.MORO_LOG_LEVEL;
|
|
60
|
+
if (envLogLevel) {
|
|
61
|
+
applyLoggingConfiguration({ level: envLogLevel }, undefined);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Initialize configuration system
|
|
65
|
+
this.config = initializeConfig();
|
|
66
|
+
|
|
67
|
+
// Apply additional logging configuration from createApp options (takes precedence)
|
|
68
|
+
if (options.logger !== undefined) {
|
|
69
|
+
applyLoggingConfiguration(undefined, options.logger);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
this.logger.info(
|
|
73
|
+
`Configuration system initialized: ${this.config.server.environment}:${this.config.server.port}`,
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// Initialize runtime system
|
|
77
|
+
this.runtimeType = options.runtime?.type || "node";
|
|
78
|
+
this.runtimeAdapter =
|
|
79
|
+
options.runtime?.adapter || createRuntimeAdapter(this.runtimeType);
|
|
80
|
+
|
|
81
|
+
this.logger.info(
|
|
82
|
+
`Runtime system initialized: ${this.runtimeType}`,
|
|
83
|
+
"Runtime",
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
this.coreFramework = new MoroCore();
|
|
87
|
+
|
|
88
|
+
// Access enterprise event bus from core framework
|
|
89
|
+
this.eventBus = (this.coreFramework as any).eventBus;
|
|
90
|
+
|
|
91
|
+
// Setup default middleware if enabled - use config defaults with options override
|
|
92
|
+
this.setupDefaultMiddleware({
|
|
93
|
+
...this.getDefaultOptionsFromConfig(),
|
|
94
|
+
...options,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Auto-discover modules if enabled
|
|
98
|
+
if (options.autoDiscover !== false) {
|
|
99
|
+
this.autoDiscoverModules(options.modulesPath || "./modules");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Emit initialization event through enterprise event bus
|
|
103
|
+
this.eventBus.emit("framework:initialized", {
|
|
104
|
+
options,
|
|
105
|
+
config: this.config,
|
|
106
|
+
runtime: this.runtimeType,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Get configuration object
|
|
112
|
+
*/
|
|
113
|
+
getConfig(): AppConfig {
|
|
114
|
+
return this.config;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get runtime adapter
|
|
119
|
+
*/
|
|
120
|
+
getRuntime(): RuntimeAdapter {
|
|
121
|
+
return this.runtimeAdapter;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Get runtime type
|
|
126
|
+
*/
|
|
127
|
+
getRuntimeType(): RuntimeType {
|
|
128
|
+
return this.runtimeType;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Extract default options from configuration
|
|
133
|
+
*/
|
|
134
|
+
private getDefaultOptionsFromConfig(): Partial<MoroOptions> {
|
|
135
|
+
return {
|
|
136
|
+
cors: this.config.security.cors.enabled,
|
|
137
|
+
compression: this.config.performance.compression.enabled,
|
|
138
|
+
helmet: this.config.security.helmet.enabled,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Intelligent route methods - chainable with automatic middleware ordering
|
|
143
|
+
// Overloads for better TypeScript inference
|
|
144
|
+
get(path: string): RouteBuilder;
|
|
145
|
+
get(
|
|
146
|
+
path: string,
|
|
147
|
+
handler: (req: HttpRequest, res: HttpResponse) => any,
|
|
148
|
+
options?: any,
|
|
149
|
+
): this;
|
|
150
|
+
get(
|
|
151
|
+
path: string,
|
|
152
|
+
handler?: (req: HttpRequest, res: HttpResponse) => any,
|
|
153
|
+
options?: any,
|
|
154
|
+
): RouteBuilder | this {
|
|
155
|
+
if (handler) {
|
|
156
|
+
// Direct route registration
|
|
157
|
+
return this.addRoute("GET", path, handler, options);
|
|
158
|
+
}
|
|
159
|
+
// Chainable route builder
|
|
160
|
+
return this.intelligentRouting.get(path);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
post(path: string): RouteBuilder;
|
|
164
|
+
post(
|
|
165
|
+
path: string,
|
|
166
|
+
handler: (req: HttpRequest, res: HttpResponse) => any,
|
|
167
|
+
options?: any,
|
|
168
|
+
): this;
|
|
169
|
+
post(
|
|
170
|
+
path: string,
|
|
171
|
+
handler?: (req: HttpRequest, res: HttpResponse) => any,
|
|
172
|
+
options?: any,
|
|
173
|
+
): RouteBuilder | this {
|
|
174
|
+
if (handler) {
|
|
175
|
+
// Direct route registration
|
|
176
|
+
return this.addRoute("POST", path, handler, options);
|
|
177
|
+
}
|
|
178
|
+
// Chainable route builder
|
|
179
|
+
return this.intelligentRouting.post(path);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
put(path: string): RouteBuilder;
|
|
183
|
+
put(
|
|
184
|
+
path: string,
|
|
185
|
+
handler: (req: HttpRequest, res: HttpResponse) => any,
|
|
186
|
+
options?: any,
|
|
187
|
+
): this;
|
|
188
|
+
put(
|
|
189
|
+
path: string,
|
|
190
|
+
handler?: (req: HttpRequest, res: HttpResponse) => any,
|
|
191
|
+
options?: any,
|
|
192
|
+
): RouteBuilder | this {
|
|
193
|
+
if (handler) {
|
|
194
|
+
// Direct route registration
|
|
195
|
+
return this.addRoute("PUT", path, handler, options);
|
|
196
|
+
}
|
|
197
|
+
// Chainable route builder
|
|
198
|
+
return this.intelligentRouting.put(path);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
delete(path: string): RouteBuilder;
|
|
202
|
+
delete(
|
|
203
|
+
path: string,
|
|
204
|
+
handler: (req: HttpRequest, res: HttpResponse) => any,
|
|
205
|
+
options?: any,
|
|
206
|
+
): this;
|
|
207
|
+
delete(
|
|
208
|
+
path: string,
|
|
209
|
+
handler?: (req: HttpRequest, res: HttpResponse) => any,
|
|
210
|
+
options?: any,
|
|
211
|
+
): RouteBuilder | this {
|
|
212
|
+
if (handler) {
|
|
213
|
+
// Direct route registration
|
|
214
|
+
return this.addRoute("DELETE", path, handler, options);
|
|
215
|
+
}
|
|
216
|
+
// Chainable route builder
|
|
217
|
+
return this.intelligentRouting.delete(path);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
patch(path: string): RouteBuilder;
|
|
221
|
+
patch(
|
|
222
|
+
path: string,
|
|
223
|
+
handler: (req: HttpRequest, res: HttpResponse) => any,
|
|
224
|
+
options?: any,
|
|
225
|
+
): this;
|
|
226
|
+
patch(
|
|
227
|
+
path: string,
|
|
228
|
+
handler?: (req: HttpRequest, res: HttpResponse) => any,
|
|
229
|
+
options?: any,
|
|
230
|
+
): RouteBuilder | this {
|
|
231
|
+
if (handler) {
|
|
232
|
+
// Direct route registration
|
|
233
|
+
return this.addRoute("PATCH", path, handler, options);
|
|
234
|
+
}
|
|
235
|
+
// Chainable route builder
|
|
236
|
+
return this.intelligentRouting.patch(path);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Schema-first route method
|
|
240
|
+
route(schema: RouteSchema): CompiledRoute {
|
|
241
|
+
return this.intelligentRouting.route(schema);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Enable automatic API documentation
|
|
245
|
+
enableDocs(config: DocsConfig): void {
|
|
246
|
+
this.documentation.enableDocs(config, this.intelligentRouting);
|
|
247
|
+
|
|
248
|
+
this.logger.info(
|
|
249
|
+
`API Documentation enabled at ${config.basePath || "/docs"}`,
|
|
250
|
+
"Documentation",
|
|
251
|
+
);
|
|
252
|
+
this.eventBus.emit("docs:enabled", { config });
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Get OpenAPI specification
|
|
256
|
+
getOpenAPISpec() {
|
|
257
|
+
return this.documentation.getOpenAPISpec();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Get documentation as JSON
|
|
261
|
+
getDocsJSON(): string {
|
|
262
|
+
return this.documentation.getDocsJSON();
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Get documentation as YAML
|
|
266
|
+
getDocsYAML(): string {
|
|
267
|
+
return this.documentation.getDocsYAML();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Refresh documentation (useful after adding routes dynamically)
|
|
271
|
+
refreshDocs(): void {
|
|
272
|
+
this.documentation.refreshDocs();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Universal middleware system - seamlessly handles standard and advanced middleware
|
|
276
|
+
async use(middlewareOrFunction: any, config?: any) {
|
|
277
|
+
// Standard middleware integration (req, res, next pattern)
|
|
278
|
+
if (
|
|
279
|
+
typeof middlewareOrFunction === "function" &&
|
|
280
|
+
middlewareOrFunction.length >= 3
|
|
281
|
+
) {
|
|
282
|
+
this.coreFramework.addMiddleware(middlewareOrFunction);
|
|
283
|
+
this.eventBus.emit("middleware:registered", {
|
|
284
|
+
type: "standard",
|
|
285
|
+
middleware: middlewareOrFunction,
|
|
286
|
+
});
|
|
287
|
+
return this;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Function-style middleware execution
|
|
291
|
+
if (
|
|
292
|
+
typeof middlewareOrFunction === "function" &&
|
|
293
|
+
middlewareOrFunction.length <= 1
|
|
294
|
+
) {
|
|
295
|
+
await middlewareOrFunction(this);
|
|
296
|
+
this.eventBus.emit("middleware:executed", {
|
|
297
|
+
type: "function",
|
|
298
|
+
middleware: middlewareOrFunction,
|
|
299
|
+
});
|
|
300
|
+
return this;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Advanced middleware pipeline integration
|
|
304
|
+
this.eventBus.emit("middleware:advanced", {
|
|
305
|
+
middleware: middlewareOrFunction,
|
|
306
|
+
config,
|
|
307
|
+
});
|
|
308
|
+
this.logger.debug(
|
|
309
|
+
"Advanced middleware integration - enhanced capabilities loading...",
|
|
310
|
+
"Middleware",
|
|
311
|
+
);
|
|
312
|
+
return this;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Plugin compatibility layer - unified middleware interface
|
|
316
|
+
async plugin(middleware: any, options?: any): Promise<this> {
|
|
317
|
+
return this.use(middleware, options);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Module loading with events
|
|
321
|
+
async loadModule(moduleOrPath: ModuleConfig | string) {
|
|
322
|
+
this.eventBus.emit("module:loading", {
|
|
323
|
+
moduleId:
|
|
324
|
+
typeof moduleOrPath === "string" ? moduleOrPath : moduleOrPath.name,
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
if (typeof moduleOrPath === "string") {
|
|
328
|
+
const module = await this.importModule(moduleOrPath);
|
|
329
|
+
await this.coreFramework.loadModule(module);
|
|
330
|
+
this.loadedModules.add(moduleOrPath);
|
|
331
|
+
this.eventBus.emit("module:loaded", {
|
|
332
|
+
moduleId: module.name,
|
|
333
|
+
version: module.version || "1.0.0",
|
|
334
|
+
});
|
|
335
|
+
} else {
|
|
336
|
+
await this.coreFramework.loadModule(moduleOrPath);
|
|
337
|
+
this.loadedModules.add(moduleOrPath.name);
|
|
338
|
+
this.eventBus.emit("module:loaded", {
|
|
339
|
+
moduleId: moduleOrPath.name,
|
|
340
|
+
version: moduleOrPath.version || "1.0.0",
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
return this;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Database helper with events
|
|
347
|
+
database(adapter: any) {
|
|
348
|
+
this.eventBus.emit("database:connected", {
|
|
349
|
+
adapter: adapter.constructor.name,
|
|
350
|
+
config: "hidden",
|
|
351
|
+
});
|
|
352
|
+
this.coreFramework.registerDatabase(adapter);
|
|
353
|
+
return this;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// WebSocket helper with events
|
|
357
|
+
websocket(namespace: string, handlers: Record<string, Function>) {
|
|
358
|
+
this.emit("websocket:registering", { namespace, handlers });
|
|
359
|
+
|
|
360
|
+
const io = this.coreFramework.getIOServer();
|
|
361
|
+
const ns = io.of(namespace);
|
|
362
|
+
|
|
363
|
+
Object.entries(handlers).forEach(([event, handler]) => {
|
|
364
|
+
ns.on("connection", (socket) => {
|
|
365
|
+
this.emit("websocket:connection", { namespace, event, socket });
|
|
366
|
+
|
|
367
|
+
socket.on(event, (data, callback) => {
|
|
368
|
+
this.emit("websocket:event", { namespace, event, data });
|
|
369
|
+
|
|
370
|
+
Promise.resolve(handler(socket, data))
|
|
371
|
+
.then((result) => {
|
|
372
|
+
this.emit("websocket:response", { namespace, event, result });
|
|
373
|
+
if (callback) callback(result);
|
|
374
|
+
else if (result) socket.emit(`${event}:response`, result);
|
|
375
|
+
})
|
|
376
|
+
.catch((error) => {
|
|
377
|
+
this.emit("websocket:error", { namespace, event, error });
|
|
378
|
+
const errorResponse = { success: false, error: error.message };
|
|
379
|
+
if (callback) callback(errorResponse);
|
|
380
|
+
else socket.emit("error", errorResponse);
|
|
381
|
+
});
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
this.emit("websocket:registered", { namespace, handlers });
|
|
387
|
+
return this;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Start server with events (Node.js only)
|
|
391
|
+
listen(port: number, callback?: () => void): void;
|
|
392
|
+
listen(port: number, host: string, callback?: () => void): void;
|
|
393
|
+
listen(port: number, host?: string | (() => void), callback?: () => void) {
|
|
394
|
+
// Only available for Node.js runtime
|
|
395
|
+
if (this.runtimeType !== "node") {
|
|
396
|
+
throw new Error(
|
|
397
|
+
`listen() is only available for Node.js runtime. Current runtime: ${this.runtimeType}. Use getHandler() for other runtimes.`,
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Handle overloaded parameters (port, callback) or (port, host, callback)
|
|
402
|
+
if (typeof host === "function") {
|
|
403
|
+
callback = host;
|
|
404
|
+
host = undefined;
|
|
405
|
+
}
|
|
406
|
+
this.eventBus.emit("server:starting", { port, runtime: this.runtimeType });
|
|
407
|
+
|
|
408
|
+
// Add documentation middleware first (if enabled)
|
|
409
|
+
try {
|
|
410
|
+
const docsMiddleware = this.documentation.getDocsMiddleware();
|
|
411
|
+
this.coreFramework.addMiddleware(docsMiddleware);
|
|
412
|
+
this.logger.debug("Documentation middleware added", "Documentation");
|
|
413
|
+
} catch (error) {
|
|
414
|
+
// Documentation not enabled, that's fine
|
|
415
|
+
this.logger.debug("Documentation not enabled", "Documentation");
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Add intelligent routing middleware to handle chainable routes
|
|
419
|
+
this.coreFramework.addMiddleware(
|
|
420
|
+
async (req: HttpRequest, res: HttpResponse, next: () => void) => {
|
|
421
|
+
// Try intelligent routing first
|
|
422
|
+
const handled = await this.intelligentRouting.handleIntelligentRoute(
|
|
423
|
+
req,
|
|
424
|
+
res,
|
|
425
|
+
);
|
|
426
|
+
if (!handled) {
|
|
427
|
+
next(); // Fall back to direct routes
|
|
428
|
+
}
|
|
429
|
+
},
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
// Register direct routes with the HTTP server
|
|
433
|
+
if (this.routes.length > 0) {
|
|
434
|
+
this.registerDirectRoutes();
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const actualCallback = () => {
|
|
438
|
+
const displayHost = host || "localhost";
|
|
439
|
+
this.logger.info("Moro Server Started", "Server");
|
|
440
|
+
this.logger.info(`Runtime: ${this.runtimeType}`, "Server");
|
|
441
|
+
this.logger.info(`HTTP API: http://${displayHost}:${port}`, "Server");
|
|
442
|
+
this.logger.info(`WebSocket: ws://${displayHost}:${port}`, "Server");
|
|
443
|
+
this.logger.info(
|
|
444
|
+
"Native Node.js HTTP • Zero Dependencies • Maximum Performance",
|
|
445
|
+
"Server",
|
|
446
|
+
);
|
|
447
|
+
this.logger.info("Learn more at https://morojs.com", "Server");
|
|
448
|
+
|
|
449
|
+
// Log intelligent routes info
|
|
450
|
+
const intelligentRoutes = this.intelligentRouting.getIntelligentRoutes();
|
|
451
|
+
if (intelligentRoutes.length > 0) {
|
|
452
|
+
this.logger.info(
|
|
453
|
+
`Intelligent Routes: ${intelligentRoutes.length} registered`,
|
|
454
|
+
"Server",
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
this.eventBus.emit("server:started", { port, runtime: this.runtimeType });
|
|
459
|
+
if (callback) callback();
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
if (host && typeof host === "string") {
|
|
463
|
+
this.coreFramework.listen(port, host, actualCallback);
|
|
464
|
+
} else {
|
|
465
|
+
this.coreFramework.listen(port, actualCallback);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Get handler for non-Node.js runtimes
|
|
470
|
+
getHandler() {
|
|
471
|
+
// Create a unified request handler that works with the runtime adapter
|
|
472
|
+
const handler = async (req: HttpRequest, res: HttpResponse) => {
|
|
473
|
+
// Add documentation middleware first (if enabled)
|
|
474
|
+
try {
|
|
475
|
+
const docsMiddleware = this.documentation.getDocsMiddleware();
|
|
476
|
+
await docsMiddleware(req, res, () => {});
|
|
477
|
+
if (res.headersSent) return;
|
|
478
|
+
} catch (error) {
|
|
479
|
+
// Documentation not enabled, that's fine
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Try intelligent routing first
|
|
483
|
+
const handled = await this.intelligentRouting.handleIntelligentRoute(
|
|
484
|
+
req,
|
|
485
|
+
res,
|
|
486
|
+
);
|
|
487
|
+
if (handled) return;
|
|
488
|
+
|
|
489
|
+
// Handle direct routes
|
|
490
|
+
if (this.routes.length > 0) {
|
|
491
|
+
await this.handleDirectRoutes(req, res);
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
// Use the runtime adapter to create the appropriate handler
|
|
496
|
+
return this.runtimeAdapter.createServer(handler);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Handle direct routes for runtime adapters
|
|
500
|
+
private async handleDirectRoutes(req: HttpRequest, res: HttpResponse) {
|
|
501
|
+
// Find matching route
|
|
502
|
+
const route = this.findMatchingRoute(req.method!, req.path);
|
|
503
|
+
if (!route) {
|
|
504
|
+
(res as any).status(404).json({ success: false, error: "Not found" });
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
try {
|
|
509
|
+
// Extract path parameters
|
|
510
|
+
const matches = req.path.match(route.pattern);
|
|
511
|
+
if (matches) {
|
|
512
|
+
req.params = {};
|
|
513
|
+
route.paramNames.forEach((name, index) => {
|
|
514
|
+
req.params[name] = matches[index + 1];
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// Get handler function
|
|
519
|
+
const handler = this.routeHandlers[route.handler];
|
|
520
|
+
if (!handler) {
|
|
521
|
+
(res as any)
|
|
522
|
+
.status(500)
|
|
523
|
+
.json({ success: false, error: "Handler not found" });
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Execute validation if present
|
|
528
|
+
if (route.validation) {
|
|
529
|
+
try {
|
|
530
|
+
const validated = route.validation.parse(req.body);
|
|
531
|
+
req.body = validated;
|
|
532
|
+
} catch (error: any) {
|
|
533
|
+
if (error.issues) {
|
|
534
|
+
(res as any).status(400).json({
|
|
535
|
+
success: false,
|
|
536
|
+
error: "Validation failed",
|
|
537
|
+
details: error.issues.map((issue: any) => ({
|
|
538
|
+
field: issue.path.length > 0 ? issue.path.join(".") : "body",
|
|
539
|
+
message: issue.message,
|
|
540
|
+
code: issue.code,
|
|
541
|
+
})),
|
|
542
|
+
});
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
throw error;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// Execute rate limiting if present
|
|
550
|
+
if (route.rateLimit) {
|
|
551
|
+
const clientId = req.ip || "unknown";
|
|
552
|
+
const key = `${route.method}:${route.path}:${clientId}`;
|
|
553
|
+
|
|
554
|
+
if (!this.checkRateLimit(key, route.rateLimit)) {
|
|
555
|
+
(res as any).status(429).json({
|
|
556
|
+
success: false,
|
|
557
|
+
error: "Rate limit exceeded",
|
|
558
|
+
retryAfter: Math.ceil(route.rateLimit.window / 1000),
|
|
559
|
+
});
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// Execute the handler
|
|
565
|
+
const result = await handler(req, res);
|
|
566
|
+
if (result && !(res as any).headersSent) {
|
|
567
|
+
(res as any).json(result);
|
|
568
|
+
}
|
|
569
|
+
} catch (error) {
|
|
570
|
+
if (!(res as any).headersSent) {
|
|
571
|
+
(res as any).status(500).json({
|
|
572
|
+
success: false,
|
|
573
|
+
error:
|
|
574
|
+
error instanceof Error ? error.message : "Internal server error",
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// Find matching route
|
|
581
|
+
private findMatchingRoute(method: string, path: string) {
|
|
582
|
+
for (const route of this.routes) {
|
|
583
|
+
if (route.method === method) {
|
|
584
|
+
const pattern = this.pathToRegex(route.path);
|
|
585
|
+
if (pattern.pattern.test(path)) {
|
|
586
|
+
return {
|
|
587
|
+
...route,
|
|
588
|
+
pattern: pattern.pattern,
|
|
589
|
+
paramNames: pattern.paramNames,
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
return null;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Convert path to regex (simplified version)
|
|
598
|
+
private pathToRegex(path: string): { pattern: RegExp; paramNames: string[] } {
|
|
599
|
+
const paramNames: string[] = [];
|
|
600
|
+
const regexPath = path
|
|
601
|
+
.replace(/\//g, "\\/")
|
|
602
|
+
.replace(/:([^/]+)/g, (match, paramName) => {
|
|
603
|
+
paramNames.push(paramName);
|
|
604
|
+
return "([^/]+)";
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
return {
|
|
608
|
+
pattern: new RegExp(`^${regexPath}$`),
|
|
609
|
+
paramNames,
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// Access enterprise event system for advanced integrations
|
|
614
|
+
get events() {
|
|
615
|
+
return this.eventBus;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// Access to core framework for advanced usage
|
|
619
|
+
get core() {
|
|
620
|
+
return this.coreFramework;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// Private methods
|
|
624
|
+
private addRoute(
|
|
625
|
+
method: string,
|
|
626
|
+
path: string,
|
|
627
|
+
handler: Function,
|
|
628
|
+
options: any = {},
|
|
629
|
+
) {
|
|
630
|
+
const handlerName = `handler_${this.routes.length}`;
|
|
631
|
+
|
|
632
|
+
this.routes.push({
|
|
633
|
+
method: method as any,
|
|
634
|
+
path,
|
|
635
|
+
handler: handlerName,
|
|
636
|
+
validation: options.validation,
|
|
637
|
+
rateLimit: options.rateLimit,
|
|
638
|
+
cache: options.cache,
|
|
639
|
+
middleware: options.middleware,
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
// Store handler for later module creation
|
|
643
|
+
this.routeHandlers[handlerName] = handler;
|
|
644
|
+
|
|
645
|
+
return this;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
private registerDirectRoutes() {
|
|
649
|
+
// Register routes directly with the HTTP server for optimal performance
|
|
650
|
+
// This provides the intuitive developer experience users expect
|
|
651
|
+
for (const route of this.routes) {
|
|
652
|
+
const handler = this.routeHandlers[route.handler];
|
|
653
|
+
|
|
654
|
+
// Get direct access to the HTTP server through the core framework
|
|
655
|
+
const httpServer = (this.coreFramework as any).httpServer;
|
|
656
|
+
|
|
657
|
+
// Create a wrapper handler that handles validation, rate limiting, and return values
|
|
658
|
+
const wrappedHandler = async (req: any, res: any) => {
|
|
659
|
+
try {
|
|
660
|
+
// Enhance request with events property for direct routes
|
|
661
|
+
req.events = this.eventBus;
|
|
662
|
+
|
|
663
|
+
// Validation middleware (Zod-only)
|
|
664
|
+
if (route.validation) {
|
|
665
|
+
try {
|
|
666
|
+
const validated = route.validation.parse(req.body);
|
|
667
|
+
req.body = validated;
|
|
668
|
+
} catch (error: any) {
|
|
669
|
+
if (error.issues) {
|
|
670
|
+
res.status(400).json({
|
|
671
|
+
success: false,
|
|
672
|
+
error: "Validation failed",
|
|
673
|
+
details: error.issues.map((issue: any) => ({
|
|
674
|
+
field:
|
|
675
|
+
issue.path.length > 0 ? issue.path.join(".") : "body",
|
|
676
|
+
message: issue.message,
|
|
677
|
+
code: issue.code,
|
|
678
|
+
})),
|
|
679
|
+
});
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
throw error;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// Rate limiting middleware
|
|
687
|
+
if (route.rateLimit) {
|
|
688
|
+
const clientId =
|
|
689
|
+
req.ip || req.connection.remoteAddress || "unknown";
|
|
690
|
+
const key = `${route.method}:${route.path}:${clientId}`;
|
|
691
|
+
|
|
692
|
+
if (!this.checkRateLimit(key, route.rateLimit)) {
|
|
693
|
+
res.status(429).json({
|
|
694
|
+
success: false,
|
|
695
|
+
error: "Rate limit exceeded",
|
|
696
|
+
retryAfter: Math.ceil(route.rateLimit.window / 1000),
|
|
697
|
+
});
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// Execute the actual handler
|
|
703
|
+
const result = await handler(req, res);
|
|
704
|
+
if (result && !res.headersSent) {
|
|
705
|
+
res.json(result);
|
|
706
|
+
}
|
|
707
|
+
} catch (error) {
|
|
708
|
+
if (!res.headersSent) {
|
|
709
|
+
res.status(500).json({
|
|
710
|
+
success: false,
|
|
711
|
+
error:
|
|
712
|
+
error instanceof Error
|
|
713
|
+
? error.message
|
|
714
|
+
: "Internal server error",
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
// Register with the appropriate HTTP method
|
|
721
|
+
const method = route.method.toLowerCase();
|
|
722
|
+
if (httpServer && httpServer[method]) {
|
|
723
|
+
httpServer[method](route.path, wrappedHandler);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// Simple rate limiting for direct routes
|
|
729
|
+
private rateLimitStore = new Map<
|
|
730
|
+
string,
|
|
731
|
+
{ count: number; resetTime: number }
|
|
732
|
+
>();
|
|
733
|
+
|
|
734
|
+
private checkRateLimit(
|
|
735
|
+
key: string,
|
|
736
|
+
config: { requests: number; window: number },
|
|
737
|
+
): boolean {
|
|
738
|
+
const now = Date.now();
|
|
739
|
+
const bucket = this.rateLimitStore.get(key);
|
|
740
|
+
|
|
741
|
+
if (!bucket || now > bucket.resetTime) {
|
|
742
|
+
// Create new bucket or reset expired bucket
|
|
743
|
+
this.rateLimitStore.set(key, {
|
|
744
|
+
count: 1,
|
|
745
|
+
resetTime: now + config.window,
|
|
746
|
+
});
|
|
747
|
+
return true;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
if (bucket.count >= config.requests) {
|
|
751
|
+
return false; // Rate limit exceeded
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
bucket.count++;
|
|
755
|
+
return true;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
private setupDefaultMiddleware(options: MoroOptions) {
|
|
759
|
+
// CORS
|
|
760
|
+
if (options.cors !== false) {
|
|
761
|
+
const corsOptions = typeof options.cors === "object" ? options.cors : {};
|
|
762
|
+
this.use(middleware.cors(corsOptions));
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// Helmet
|
|
766
|
+
if (options.helmet !== false) {
|
|
767
|
+
this.use(middleware.helmet());
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// Compression
|
|
771
|
+
if (options.compression !== false) {
|
|
772
|
+
const compressionOptions =
|
|
773
|
+
typeof options.compression === "object" ? options.compression : {};
|
|
774
|
+
this.use(middleware.compression(compressionOptions));
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// Body size limiting
|
|
778
|
+
this.use(middleware.bodySize({ limit: "10mb" }));
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
private autoDiscoverModules(modulesPath: string) {
|
|
782
|
+
try {
|
|
783
|
+
if (!statSync(modulesPath).isDirectory()) return;
|
|
784
|
+
|
|
785
|
+
const items = readdirSync(modulesPath);
|
|
786
|
+
items.forEach((item) => {
|
|
787
|
+
const fullPath = join(modulesPath, item);
|
|
788
|
+
if (statSync(fullPath).isDirectory()) {
|
|
789
|
+
const indexPath = join(fullPath, "index.ts");
|
|
790
|
+
try {
|
|
791
|
+
statSync(indexPath);
|
|
792
|
+
// Module directory found, will be loaded later
|
|
793
|
+
this.logger.debug(`Discovered module: ${item}`, "ModuleDiscovery");
|
|
794
|
+
} catch {
|
|
795
|
+
// No index.ts, skip
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
});
|
|
799
|
+
} catch {
|
|
800
|
+
// Modules directory doesn't exist, that's fine
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
private async importModule(modulePath: string): Promise<ModuleConfig> {
|
|
805
|
+
const module = await import(modulePath);
|
|
806
|
+
return module.default || module;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// Export convenience function
|
|
811
|
+
export function createApp(options?: MoroOptions): Moro {
|
|
812
|
+
return new Moro(options);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// Runtime-specific convenience functions
|
|
816
|
+
export function createAppNode(options?: Omit<MoroOptions, "runtime">): Moro {
|
|
817
|
+
return new Moro({
|
|
818
|
+
...options,
|
|
819
|
+
runtime: { type: "node" },
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
export function createAppEdge(options?: Omit<MoroOptions, "runtime">): Moro {
|
|
824
|
+
return new Moro({
|
|
825
|
+
...options,
|
|
826
|
+
runtime: { type: "vercel-edge" },
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
export function createAppLambda(options?: Omit<MoroOptions, "runtime">): Moro {
|
|
831
|
+
return new Moro({
|
|
832
|
+
...options,
|
|
833
|
+
runtime: { type: "aws-lambda" },
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
export function createAppWorker(options?: Omit<MoroOptions, "runtime">): Moro {
|
|
838
|
+
return new Moro({
|
|
839
|
+
...options,
|
|
840
|
+
runtime: { type: "cloudflare-workers" },
|
|
841
|
+
});
|
|
842
|
+
}
|