@webpieces/http-server 0.2.13 → 0.2.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/http-server",
3
- "version": "0.2.13",
3
+ "version": "0.2.15",
4
4
  "description": "WebPieces server with filter chain and dependency injection",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -22,8 +22,9 @@
22
22
  "access": "public"
23
23
  },
24
24
  "dependencies": {
25
- "@webpieces/core-meta": "0.2.13",
26
- "@webpieces/http-routing": "0.2.13",
27
- "@webpieces/http-filters": "0.2.13"
25
+ "@webpieces/core-meta": "0.2.15",
26
+ "@webpieces/core-util": "0.2.15",
27
+ "@webpieces/http-routing": "0.2.15",
28
+ "@webpieces/http-filters": "0.2.15"
28
29
  }
29
30
  }
@@ -1 +1 @@
1
- {"version":3,"file":"FilterMatcher.js","sourceRoot":"","sources":["../../../../../packages/http/http-server/src/FilterMatcher.ts"],"names":[],"mappings":";;;AAGA,yCAAsC;AAOtC;;;;;;;;;;;;GAYG;AACH,MAAa,aAAa;IACxB;;;;;;OAMG;IACH,MAAM,CAAC,mBAAmB,CACxB,kBAAsC,EACtC,UAAmC;QAEnC,MAAM,eAAe,GAAoD,EAAE,CAAC;QAE5E,KAAK,MAAM,UAAU,IAAI,UAAU,EAAE,CAAC;YACpC,MAAM,OAAO,GAAG,UAAU,CAAC,eAAe,CAAC;YAC3C,MAAM,MAAM,GAAG,UAAU,CAAC,MAAoB,CAAC;YAE/C,4DAA4D;YAC5D,IAAI,OAAO,KAAK,GAAG,EAAE,CAAC;gBACpB,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAChE,SAAS;YACX,CAAC;YAED,yDAAyD;YACzD,IAAI,CAAC,kBAAkB,EAAE,CAAC;gBACxB,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;oBACvB,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAClE,CAAC;gBACD,SAAS;YACX,CAAC;YAED,6CAA6C;YAC7C,MAAM,cAAc,GAAG,aAAa,CAAC,iBAAiB,CAAC,kBAAkB,CAAC,CAAC;YAE3E,wBAAwB;YACxB,IAAI,IAAA,qBAAS,EAAC,cAAc,EAAE,OAAO,CAAC,EAAE,CAAC;gBACvC,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC;YAClE,CAAC;QACH,CAAC;QAED,mCAAmC;QACnC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;QAExD,OAAO,eAAe,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACpD,CAAC;IAED;;;;;;;OAOG;IACH,MAAM,CAAC,iBAAiB,CAAC,QAAgB;QACvC,OAAO,QAAQ;aACZ,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,yCAAyC;aAC7D,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,sBAAsB;IACjD,CAAC;CACF;AA5DD,sCA4DC","sourcesContent":["import { Filter, WpResponse } from '@webpieces/http-filters';\nimport { MethodMeta } from './MethodMeta';\nimport { FilterDefinition } from '@webpieces/core-meta';\nimport { minimatch } from 'minimatch';\n\n/**\n * Type alias for HTTP filters that work with MethodMeta and ResponseWrapper.\n */\nexport type HttpFilter = Filter<MethodMeta, WpResponse<unknown>>;\n\n/**\n * FilterMatcher - Matches filters to routes based on filepath patterns.\n * Similar to Java SharedMatchUtil.findMatchingFilters().\n *\n * Responsibilities:\n * 1. Filter based on filepath glob pattern matching\n * 2. Sort matching filters by priority (higher first)\n *\n * Differences from Java:\n * - Uses glob patterns instead of regex\n * - Only matches filepaths (no URL path or HTTPS filtering yet)\n * - Simpler API focused on one responsibility\n */\nexport class FilterMatcher {\n /**\n * Find filters that match the given controller filepath.\n *\n * @param controllerFilepath - The filepath of the controller source file\n * @param allFilters - All registered filters with their definitions\n * @returns Array of matching filters, sorted by priority (highest first)\n */\n static findMatchingFilters(\n controllerFilepath: string | undefined,\n allFilters: Array<FilterDefinition>\n ): HttpFilter[] {\n const matchingFilters: Array<{ filter: HttpFilter; priority: number }> = [];\n\n for (const definition of allFilters) {\n const pattern = definition.filepathPattern;\n const filter = definition.filter as HttpFilter;\n\n // Special case: '*' matches all controllers (global filter)\n if (pattern === '*') {\n matchingFilters.push({ filter, priority: definition.priority });\n continue;\n }\n\n // If no filepath available, only match wildcard patterns\n if (!controllerFilepath) {\n if (pattern === '**/*') {\n matchingFilters.push({ filter, priority: definition.priority });\n }\n continue;\n }\n\n // Normalize filepath for consistent matching\n const normalizedPath = FilterMatcher.normalizeFilepath(controllerFilepath);\n\n // Match using minimatch\n if (minimatch(normalizedPath, pattern)) {\n matchingFilters.push({ filter, priority: definition.priority });\n }\n }\n\n // Sort by priority (highest first)\n matchingFilters.sort((a, b) => b.priority - a.priority);\n\n return matchingFilters.map((item) => item.filter);\n }\n\n /**\n * Normalize a controller filepath for consistent matching.\n * - Converts backslashes to forward slashes (Windows compatibility)\n * - Removes leading './'\n *\n * @param filepath - Raw filepath\n * @returns Normalized filepath\n */\n static normalizeFilepath(filepath: string): string {\n return filepath\n .replace(/\\\\/g, '/') // Windows backslashes to forward slashes\n .replace(/^\\.\\//, ''); // Remove leading './'\n }\n}\n"]}
1
+ {"version":3,"file":"FilterMatcher.js","sourceRoot":"","sources":["../../../../../packages/http/http-server/src/FilterMatcher.ts"],"names":[],"mappings":";;;AAGA,yCAAsC;AAOtC;;;;;;;;;;;;GAYG;AACH,MAAa,aAAa;IACtB;;;;;;OAMG;IACH,MAAM,CAAC,mBAAmB,CACtB,kBAAsC,EACtC,UAAmC;QAEnC,MAAM,eAAe,GAAoD,EAAE,CAAC;QAE5E,KAAK,MAAM,UAAU,IAAI,UAAU,EAAE,CAAC;YAClC,MAAM,OAAO,GAAG,UAAU,CAAC,eAAe,CAAC;YAC3C,MAAM,MAAM,GAAG,UAAU,CAAC,MAAoB,CAAC;YAE/C,4DAA4D;YAC5D,IAAI,OAAO,KAAK,GAAG,EAAE,CAAC;gBAClB,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAChE,SAAS;YACb,CAAC;YAED,yDAAyD;YACzD,IAAI,CAAC,kBAAkB,EAAE,CAAC;gBACtB,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;oBACrB,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC;gBACpE,CAAC;gBACD,SAAS;YACb,CAAC;YAED,6CAA6C;YAC7C,MAAM,cAAc,GAAG,aAAa,CAAC,iBAAiB,CAAC,kBAAkB,CAAC,CAAC;YAE3E,wBAAwB;YACxB,IAAI,IAAA,qBAAS,EAAC,cAAc,EAAE,OAAO,CAAC,EAAE,CAAC;gBACrC,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC;YACpE,CAAC;QACL,CAAC;QAED,mCAAmC;QACnC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;QAExD,OAAO,eAAe,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACtD,CAAC;IAED;;;;;;;OAOG;IACH,MAAM,CAAC,iBAAiB,CAAC,QAAgB;QACrC,OAAO,QAAQ;aACV,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,yCAAyC;aAC7D,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,sBAAsB;IACrD,CAAC;CACJ;AA5DD,sCA4DC","sourcesContent":["import { Filter, WpResponse } from '@webpieces/http-filters';\nimport { MethodMeta } from './MethodMeta';\nimport { FilterDefinition } from '@webpieces/core-meta';\nimport { minimatch } from 'minimatch';\n\n/**\n * Type alias for HTTP filters that work with MethodMeta and ResponseWrapper.\n */\nexport type HttpFilter = Filter<MethodMeta, WpResponse<unknown>>;\n\n/**\n * FilterMatcher - Matches filters to routes based on filepath patterns.\n * Similar to Java SharedMatchUtil.findMatchingFilters().\n *\n * Responsibilities:\n * 1. Filter based on filepath glob pattern matching\n * 2. Sort matching filters by priority (higher first)\n *\n * Differences from Java:\n * - Uses glob patterns instead of regex\n * - Only matches filepaths (no URL path or HTTPS filtering yet)\n * - Simpler API focused on one responsibility\n */\nexport class FilterMatcher {\n /**\n * Find filters that match the given controller filepath.\n *\n * @param controllerFilepath - The filepath of the controller source file\n * @param allFilters - All registered filters with their definitions\n * @returns Array of matching filters, sorted by priority (highest first)\n */\n static findMatchingFilters(\n controllerFilepath: string | undefined,\n allFilters: Array<FilterDefinition>,\n ): HttpFilter[] {\n const matchingFilters: Array<{ filter: HttpFilter; priority: number }> = [];\n\n for (const definition of allFilters) {\n const pattern = definition.filepathPattern;\n const filter = definition.filter as HttpFilter;\n\n // Special case: '*' matches all controllers (global filter)\n if (pattern === '*') {\n matchingFilters.push({ filter, priority: definition.priority });\n continue;\n }\n\n // If no filepath available, only match wildcard patterns\n if (!controllerFilepath) {\n if (pattern === '**/*') {\n matchingFilters.push({ filter, priority: definition.priority });\n }\n continue;\n }\n\n // Normalize filepath for consistent matching\n const normalizedPath = FilterMatcher.normalizeFilepath(controllerFilepath);\n\n // Match using minimatch\n if (minimatch(normalizedPath, pattern)) {\n matchingFilters.push({ filter, priority: definition.priority });\n }\n }\n\n // Sort by priority (highest first)\n matchingFilters.sort((a, b) => b.priority - a.priority);\n\n return matchingFilters.map((item) => item.filter);\n }\n\n /**\n * Normalize a controller filepath for consistent matching.\n * - Converts backslashes to forward slashes (Windows compatibility)\n * - Removes leading './'\n *\n * @param filepath - Raw filepath\n * @returns Normalized filepath\n */\n static normalizeFilepath(filepath: string): string {\n return filepath\n .replace(/\\\\/g, '/') // Windows backslashes to forward slashes\n .replace(/^\\.\\//, ''); // Remove leading './'\n }\n}\n"]}
@@ -3,7 +3,7 @@ import { RouteMetadata2, RouteRequest } from '@webpieces/core-meta';
3
3
  * Metadata about the method being invoked.
4
4
  * Passed to filters and contains request information.
5
5
  *
6
- * MethodMeta is created by WebpiecesCoreServer when handling a request:
6
+ * MethodMeta is created by WebpiecesServerImpl when handling a request:
7
7
  * - routeMeta: Static route information (httpMethod, path, methodName)
8
8
  * - routeRequest: Express Request/Response objects
9
9
  * - requestDto: Set by JsonFilter after deserializing the request body
package/src/MethodMeta.js CHANGED
@@ -5,7 +5,7 @@ exports.MethodMeta = void 0;
5
5
  * Metadata about the method being invoked.
6
6
  * Passed to filters and contains request information.
7
7
  *
8
- * MethodMeta is created by WebpiecesCoreServer when handling a request:
8
+ * MethodMeta is created by WebpiecesServerImpl when handling a request:
9
9
  * - routeMeta: Static route information (httpMethod, path, methodName)
10
10
  * - routeRequest: Express Request/Response objects
11
11
  * - requestDto: Set by JsonFilter after deserializing the request body
@@ -1 +1 @@
1
- {"version":3,"file":"MethodMeta.js","sourceRoot":"","sources":["../../../../../packages/http/http-server/src/MethodMeta.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;GAQG;AACH,MAAa,UAAU;IAsBrB,YACE,SAAyB,EACzB,YAA0B,EAC1B,UAAoB,EACpB,QAA+B;QAE/B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,QAAQ,GAAG,QAAQ,IAAI,IAAI,GAAG,EAAE,CAAC;IACxC,CAAC;IAED;;OAEG;IACH,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC;IACnC,CAAC;IAED;;OAEG;IACH,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;IAC7B,CAAC;IAED;;OAEG;IACH,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC;IACnC,CAAC;CACF;AAtDD,gCAsDC","sourcesContent":["import { RouteMetadata2, RouteRequest } from '@webpieces/core-meta';\n\n/**\n * Metadata about the method being invoked.\n * Passed to filters and contains request information.\n *\n * MethodMeta is created by WebpiecesCoreServer when handling a request:\n * - routeMeta: Static route information (httpMethod, path, methodName)\n * - routeRequest: Express Request/Response objects\n * - requestDto: Set by JsonFilter after deserializing the request body\n */\nexport class MethodMeta {\n /**\n * Route metadata (httpMethod, path, methodName, parameterTypes)\n */\n routeMeta: RouteMetadata2;\n\n /**\n * Express Request and Response objects\n */\n routeRequest: RouteRequest;\n\n /**\n * The deserialized request DTO.\n * Set by JsonFilter after deserializing the request body.\n */\n requestDto?: unknown;\n\n /**\n * Additional metadata for storing request-scoped data.\n */\n metadata: Map<string, unknown>;\n\n constructor(\n routeMeta: RouteMetadata2,\n routeRequest: RouteRequest,\n requestDto?: unknown,\n metadata?: Map<string, unknown>\n ) {\n this.routeMeta = routeMeta;\n this.routeRequest = routeRequest;\n this.requestDto = requestDto;\n this.metadata = metadata ?? new Map();\n }\n\n /**\n * Get the HTTP method (convenience accessor).\n */\n get httpMethod(): string {\n return this.routeMeta.httpMethod;\n }\n\n /**\n * Get the request path (convenience accessor).\n */\n get path(): string {\n return this.routeMeta.path;\n }\n\n /**\n * Get the method name (convenience accessor).\n */\n get methodName(): string {\n return this.routeMeta.methodName;\n }\n}\n"]}
1
+ {"version":3,"file":"MethodMeta.js","sourceRoot":"","sources":["../../../../../packages/http/http-server/src/MethodMeta.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;GAQG;AACH,MAAa,UAAU;IAsBnB,YACI,SAAyB,EACzB,YAA0B,EAC1B,UAAoB,EACpB,QAA+B;QAE/B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,QAAQ,GAAG,QAAQ,IAAI,IAAI,GAAG,EAAE,CAAC;IAC1C,CAAC;IAED;;OAEG;IACH,IAAI,UAAU;QACV,OAAO,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC;IACrC,CAAC;IAED;;OAEG;IACH,IAAI,IAAI;QACJ,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;IAC/B,CAAC;IAED;;OAEG;IACH,IAAI,UAAU;QACV,OAAO,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC;IACrC,CAAC;CACJ;AAtDD,gCAsDC","sourcesContent":["import { RouteMetadata2, RouteRequest } from '@webpieces/core-meta';\n\n/**\n * Metadata about the method being invoked.\n * Passed to filters and contains request information.\n *\n * MethodMeta is created by WebpiecesServerImpl when handling a request:\n * - routeMeta: Static route information (httpMethod, path, methodName)\n * - routeRequest: Express Request/Response objects\n * - requestDto: Set by JsonFilter after deserializing the request body\n */\nexport class MethodMeta {\n /**\n * Route metadata (httpMethod, path, methodName, parameterTypes)\n */\n routeMeta: RouteMetadata2;\n\n /**\n * Express Request and Response objects\n */\n routeRequest: RouteRequest;\n\n /**\n * The deserialized request DTO.\n * Set by JsonFilter after deserializing the request body.\n */\n requestDto?: unknown;\n\n /**\n * Additional metadata for storing request-scoped data.\n */\n metadata: Map<string, unknown>;\n\n constructor(\n routeMeta: RouteMetadata2,\n routeRequest: RouteRequest,\n requestDto?: unknown,\n metadata?: Map<string, unknown>,\n ) {\n this.routeMeta = routeMeta;\n this.routeRequest = routeRequest;\n this.requestDto = requestDto;\n this.metadata = metadata ?? new Map();\n }\n\n /**\n * Get the HTTP method (convenience accessor).\n */\n get httpMethod(): string {\n return this.routeMeta.httpMethod;\n }\n\n /**\n * Get the request path (convenience accessor).\n */\n get path(): string {\n return this.routeMeta.path;\n }\n\n /**\n * Get the method name (convenience accessor).\n */\n get methodName(): string {\n return this.routeMeta.methodName;\n }\n}\n"]}
@@ -85,7 +85,7 @@ let RouteBuilderImpl = class RouteBuilderImpl {
85
85
  throw new Error(`Method ${routeMeta.methodName} not found on controller ${controllerName}`);
86
86
  }
87
87
  // Create handler that uses the resolved controller instance
88
- const handler = new class extends RouteHandler_1.RouteHandler {
88
+ const handler = new (class extends RouteHandler_1.RouteHandler {
89
89
  async execute(meta) {
90
90
  // Invoke the method with requestDto from meta
91
91
  // The controller is already resolved - no DI lookup on every request!
@@ -93,7 +93,7 @@ let RouteBuilderImpl = class RouteBuilderImpl {
93
93
  const result = await method.call(controller, meta.requestDto);
94
94
  return result;
95
95
  }
96
- };
96
+ })();
97
97
  // Store handler with route definition
98
98
  const routeWithMeta = new RouteHandlerWithMeta(handler, route);
99
99
  this.routes.set(key, routeWithMeta);
@@ -1 +1 @@
1
- {"version":3,"file":"RouteBuilderImpl.js","sourceRoot":"","sources":["../../../../../packages/http/http-server/src/RouteBuilderImpl.ts"],"names":[],"mappings":"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","sourcesContent":["import { Container, injectable } from 'inversify';\nimport { RouteBuilder, RouteDefinition, FilterDefinition } from '@webpieces/core-meta';\nimport { Filter, WpResponse } from '@webpieces/http-filters';\nimport { provideSingleton } from '@webpieces/http-routing';\nimport { RouteHandler } from './RouteHandler';\nimport { MethodMeta } from './MethodMeta';\n\n/**\n * Type alias for HTTP filters that work with MethodMeta and ResponseWrapper.\n */\nexport type HttpFilter = Filter<MethodMeta, WpResponse<unknown>>;\n\n/**\n * FilterWithMeta - Pairs a resolved filter instance with its definition.\n * Stores both the DI-resolved filter and the metadata needed for matching.\n */\nexport class FilterWithMeta {\n constructor(\n public filter: HttpFilter,\n public definition: FilterDefinition\n ) {}\n}\n\n/**\n * RouteHandlerWithMeta - Pairs a route handler with its definition.\n * Stores both the handler (which wraps the DI-resolved controller) and the route metadata.\n *\n * We use unknown for the generic type since we store different TResult types in the same Map.\n * Type safety is maintained through the generic on RouteDefinition at registration time.\n */\nexport class RouteHandlerWithMeta {\n constructor(\n public handler: RouteHandler<unknown>,\n public definition: RouteDefinition<unknown>\n ) {}\n}\n\n/**\n * RouteBuilderImpl - Concrete implementation of RouteBuilder interface.\n *\n * Similar to Java WebPieces RouteBuilder, this class is responsible for:\n * 1. Registering routes with their handlers\n * 2. Registering filters with priority\n *\n * This class is explicit (not anonymous) to:\n * - Improve traceability and debugging\n * - Make the code easier to understand\n * - Enable better IDE navigation (Cmd+Click on addRoute works!)\n *\n * DI Pattern: This class is registered in webpiecesContainer via @provideSingleton()\n * but needs appContainer to resolve filters/controllers. The container is set via\n * setContainer() after appContainer is created (late binding pattern).\n */\n@provideSingleton()\n@injectable()\nexport class RouteBuilderImpl implements RouteBuilder {\n private routes: Map<string, RouteHandlerWithMeta> = new Map();\n private filterRegistry: Array<FilterWithMeta> = [];\n private container?: Container;\n\n /**\n * Set the DI container used for resolving filters and controllers.\n * Called by WebpiecesCoreServer after appContainer is created.\n *\n * @param container - The application DI container (appContainer)\n */\n setContainer(container: Container): void {\n this.container = container;\n }\n\n /**\n * Register a route with the router.\n *\n * Resolves the controller from DI container ONCE and creates a handler that uses\n * the resolved controller instance. This is more efficient than resolving on every request.\n *\n * The route is stored with a key of \"METHOD:path\" (e.g., \"POST:/search/item\").\n *\n * @param route - Route definition with controller class and method name\n */\n addRoute<TResult = unknown>(route: RouteDefinition<TResult>): void {\n if (!this.container) {\n throw new Error('Container not set. Call setContainer() before registering routes.');\n }\n\n const routeMeta = route.routeMeta;\n\n const key = `${routeMeta.httpMethod}:${routeMeta.path}`;\n\n // Resolve controller instance from DI container ONCE (not on every request!)\n const controller = this.container.get(route.controllerClass);\n\n // Get the controller method\n const method = (controller as Record<string, unknown>)[routeMeta.methodName];\n if (typeof method !== 'function') {\n const controllerName = (route.controllerClass as { name?: string }).name || 'Unknown';\n throw new Error(\n `Method ${routeMeta.methodName} not found on controller ${controllerName}`\n );\n }\n\n // Create handler that uses the resolved controller instance\n const handler = new class extends RouteHandler<TResult> {\n async execute(meta: MethodMeta): Promise<TResult> {\n // Invoke the method with requestDto from meta\n // The controller is already resolved - no DI lookup on every request!\n // Pass requestDto as the single argument to the controller method\n const result: TResult = await method.call(controller, meta.requestDto);\n return result;\n }\n };\n\n // Store handler with route definition\n const routeWithMeta = new RouteHandlerWithMeta(\n handler as RouteHandler<unknown>,\n route as RouteDefinition<unknown>\n );\n\n this.routes.set(key, routeWithMeta);\n }\n\n /**\n * Register a filter with the filter chain.\n *\n * Resolves the filter from DI container and pairs it with the filter definition.\n * The definition includes pattern information used for route-specific filtering.\n *\n * @param filterDef - Filter definition with priority, filter class, and optional filepath pattern\n */\n addFilter(filterDef: FilterDefinition): void {\n if (!this.container) {\n throw new Error('Container not set. Call setContainer() before registering filters.');\n }\n\n // Resolve filter instance from DI container\n const filter = this.container.get<HttpFilter>(filterDef.filterClass);\n\n // Store filter with its definition\n const filterWithMeta = new FilterWithMeta(filter, filterDef);\n this.filterRegistry.push(filterWithMeta);\n }\n\n /**\n * Get all registered routes.\n *\n * @returns Map of routes with handlers and definitions, keyed by \"METHOD:path\"\n */\n getRoutes(): Map<string, RouteHandlerWithMeta> {\n return this.routes;\n }\n\n /**\n * Get all filters sorted by priority (highest priority first).\n *\n * @returns Array of FilterWithMeta sorted by priority\n */\n getSortedFilters(): Array<FilterWithMeta> {\n return [...this.filterRegistry].sort((a, b) => b.definition.priority - a.definition.priority);\n }\n}\n"]}
1
+ {"version":3,"file":"RouteBuilderImpl.js","sourceRoot":"","sources":["../../../../../packages/http/http-server/src/RouteBuilderImpl.ts"],"names":[],"mappings":";;;;AAAA,yCAAkD;AAGlD,0DAA2D;AAC3D,iDAA8C;AAQ9C;;;GAGG;AACH,MAAa,cAAc;IACvB,YACW,MAAkB,EAClB,UAA4B;QAD5B,WAAM,GAAN,MAAM,CAAY;QAClB,eAAU,GAAV,UAAU,CAAkB;IACpC,CAAC;CACP;AALD,wCAKC;AAED;;;;;;GAMG;AACH,MAAa,oBAAoB;IAC7B,YACW,OAA8B,EAC9B,UAAoC;QADpC,YAAO,GAAP,OAAO,CAAuB;QAC9B,eAAU,GAAV,UAAU,CAA0B;IAC5C,CAAC;CACP;AALD,oDAKC;AAED;;;;;;;;;;;;;;;GAeG;AAGI,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IAAtB;QACK,WAAM,GAAsC,IAAI,GAAG,EAAE,CAAC;QACtD,mBAAc,GAA0B,EAAE,CAAC;IAwGvD,CAAC;IArGG;;;;;OAKG;IACH,YAAY,CAAC,SAAoB;QAC7B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC/B,CAAC;IAED;;;;;;;;;OASG;IACH,QAAQ,CAAoB,KAA+B;QACvD,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,mEAAmE,CAAC,CAAC;QACzF,CAAC;QAED,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;QAElC,MAAM,GAAG,GAAG,GAAG,SAAS,CAAC,UAAU,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC;QAExD,6EAA6E;QAC7E,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QAE7D,4BAA4B;QAC5B,MAAM,MAAM,GAAI,UAAsC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QAC7E,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE,CAAC;YAC/B,MAAM,cAAc,GAAI,KAAK,CAAC,eAAqC,CAAC,IAAI,IAAI,SAAS,CAAC;YACtF,MAAM,IAAI,KAAK,CACX,UAAU,SAAS,CAAC,UAAU,4BAA4B,cAAc,EAAE,CAC7E,CAAC;QACN,CAAC;QAED,4DAA4D;QAC5D,MAAM,OAAO,GAAG,IAAI,CAAC,KAAM,SAAQ,2BAAqB;YACpD,KAAK,CAAC,OAAO,CAAC,IAAgB;gBAC1B,8CAA8C;gBAC9C,sEAAsE;gBACtE,kEAAkE;gBAClE,MAAM,MAAM,GAAY,MAAM,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;gBACvE,OAAO,MAAM,CAAC;YAClB,CAAC;SACJ,CAAC,EAAE,CAAC;QAEL,sCAAsC;QACtC,MAAM,aAAa,GAAG,IAAI,oBAAoB,CAC1C,OAAgC,EAChC,KAAiC,CACpC,CAAC;QAEF,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IACxC,CAAC;IAED;;;;;;;OAOG;IACH,SAAS,CAAC,SAA2B;QACjC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAC;QAC1F,CAAC;QAED,4CAA4C;QAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAa,SAAS,CAAC,WAAW,CAAC,CAAC;QAErE,mCAAmC;QACnC,MAAM,cAAc,GAAG,IAAI,cAAc,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAC7D,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC7C,CAAC;IAED;;;;OAIG;IACH,SAAS;QACL,OAAO,IAAI,CAAC,MAAM,CAAC;IACvB,CAAC;IAED;;;;OAIG;IACH,gBAAgB;QACZ,OAAO,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC,IAAI,CAChC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,GAAG,CAAC,CAAC,UAAU,CAAC,QAAQ,CAC1D,CAAC;IACN,CAAC;CACJ,CAAA;AA1GY,4CAAgB;2BAAhB,gBAAgB;IAF5B,IAAA,+BAAgB,GAAE;IAClB,IAAA,sBAAU,GAAE;GACA,gBAAgB,CA0G5B","sourcesContent":["import { Container, injectable } from 'inversify';\nimport { RouteBuilder, RouteDefinition, FilterDefinition } from '@webpieces/core-meta';\nimport { Filter, WpResponse } from '@webpieces/http-filters';\nimport { provideSingleton } from '@webpieces/http-routing';\nimport { RouteHandler } from './RouteHandler';\nimport { MethodMeta } from './MethodMeta';\n\n/**\n * Type alias for HTTP filters that work with MethodMeta and ResponseWrapper.\n */\nexport type HttpFilter = Filter<MethodMeta, WpResponse<unknown>>;\n\n/**\n * FilterWithMeta - Pairs a resolved filter instance with its definition.\n * Stores both the DI-resolved filter and the metadata needed for matching.\n */\nexport class FilterWithMeta {\n constructor(\n public filter: HttpFilter,\n public definition: FilterDefinition,\n ) {}\n}\n\n/**\n * RouteHandlerWithMeta - Pairs a route handler with its definition.\n * Stores both the handler (which wraps the DI-resolved controller) and the route metadata.\n *\n * We use unknown for the generic type since we store different TResult types in the same Map.\n * Type safety is maintained through the generic on RouteDefinition at registration time.\n */\nexport class RouteHandlerWithMeta {\n constructor(\n public handler: RouteHandler<unknown>,\n public definition: RouteDefinition<unknown>,\n ) {}\n}\n\n/**\n * RouteBuilderImpl - Concrete implementation of RouteBuilder interface.\n *\n * Similar to Java WebPieces RouteBuilder, this class is responsible for:\n * 1. Registering routes with their handlers\n * 2. Registering filters with priority\n *\n * This class is explicit (not anonymous) to:\n * - Improve traceability and debugging\n * - Make the code easier to understand\n * - Enable better IDE navigation (Cmd+Click on addRoute works!)\n *\n * DI Pattern: This class is registered in webpiecesContainer via @provideSingleton()\n * but needs appContainer to resolve filters/controllers. The container is set via\n * setContainer() after appContainer is created (late binding pattern).\n */\n@provideSingleton()\n@injectable()\nexport class RouteBuilderImpl implements RouteBuilder {\n private routes: Map<string, RouteHandlerWithMeta> = new Map();\n private filterRegistry: Array<FilterWithMeta> = [];\n private container?: Container;\n\n /**\n * Set the DI container used for resolving filters and controllers.\n * Called by WebpiecesCoreServer after appContainer is created.\n *\n * @param container - The application DI container (appContainer)\n */\n setContainer(container: Container): void {\n this.container = container;\n }\n\n /**\n * Register a route with the router.\n *\n * Resolves the controller from DI container ONCE and creates a handler that uses\n * the resolved controller instance. This is more efficient than resolving on every request.\n *\n * The route is stored with a key of \"METHOD:path\" (e.g., \"POST:/search/item\").\n *\n * @param route - Route definition with controller class and method name\n */\n addRoute<TResult = unknown>(route: RouteDefinition<TResult>): void {\n if (!this.container) {\n throw new Error('Container not set. Call setContainer() before registering routes.');\n }\n\n const routeMeta = route.routeMeta;\n\n const key = `${routeMeta.httpMethod}:${routeMeta.path}`;\n\n // Resolve controller instance from DI container ONCE (not on every request!)\n const controller = this.container.get(route.controllerClass);\n\n // Get the controller method\n const method = (controller as Record<string, unknown>)[routeMeta.methodName];\n if (typeof method !== 'function') {\n const controllerName = (route.controllerClass as { name?: string }).name || 'Unknown';\n throw new Error(\n `Method ${routeMeta.methodName} not found on controller ${controllerName}`,\n );\n }\n\n // Create handler that uses the resolved controller instance\n const handler = new (class extends RouteHandler<TResult> {\n async execute(meta: MethodMeta): Promise<TResult> {\n // Invoke the method with requestDto from meta\n // The controller is already resolved - no DI lookup on every request!\n // Pass requestDto as the single argument to the controller method\n const result: TResult = await method.call(controller, meta.requestDto);\n return result;\n }\n })();\n\n // Store handler with route definition\n const routeWithMeta = new RouteHandlerWithMeta(\n handler as RouteHandler<unknown>,\n route as RouteDefinition<unknown>,\n );\n\n this.routes.set(key, routeWithMeta);\n }\n\n /**\n * Register a filter with the filter chain.\n *\n * Resolves the filter from DI container and pairs it with the filter definition.\n * The definition includes pattern information used for route-specific filtering.\n *\n * @param filterDef - Filter definition with priority, filter class, and optional filepath pattern\n */\n addFilter(filterDef: FilterDefinition): void {\n if (!this.container) {\n throw new Error('Container not set. Call setContainer() before registering filters.');\n }\n\n // Resolve filter instance from DI container\n const filter = this.container.get<HttpFilter>(filterDef.filterClass);\n\n // Store filter with its definition\n const filterWithMeta = new FilterWithMeta(filter, filterDef);\n this.filterRegistry.push(filterWithMeta);\n }\n\n /**\n * Get all registered routes.\n *\n * @returns Map of routes with handlers and definitions, keyed by \"METHOD:path\"\n */\n getRoutes(): Map<string, RouteHandlerWithMeta> {\n return this.routes;\n }\n\n /**\n * Get all filters sorted by priority (highest priority first).\n *\n * @returns Array of FilterWithMeta sorted by priority\n */\n getSortedFilters(): Array<FilterWithMeta> {\n return [...this.filterRegistry].sort(\n (a, b) => b.definition.priority - a.definition.priority,\n );\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"RouteHandler.js","sourceRoot":"","sources":["../../../../../packages/http/http-server/src/RouteHandler.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;GAYG;AACH,MAAsB,YAAY;CAOjC;AAPD,oCAOC","sourcesContent":["import { MethodMeta } from './MethodMeta';\n\n/**\n * Handler class for routes.\n * Takes a MethodMeta and returns the controller method result.\n *\n * Generic type parameter TResult represents the return type of the controller method.\n * Example: RouteHandler<SaveResponse> for a method that returns Promise<SaveResponse>\n *\n * Using unknown as default instead of any forces type safety - consumers must\n * handle the result appropriately rather than assuming any type.\n *\n * This is a class instead of a function type to make it easier to trace\n * who is calling what in the debugger/IDE.\n */\nexport abstract class RouteHandler<TResult = unknown> {\n /**\n * Execute the route handler.\n * @param meta - The method metadata containing request info and params\n * @returns Promise of the controller method result\n */\n abstract execute(meta: MethodMeta): Promise<TResult>;\n}\n"]}
1
+ {"version":3,"file":"RouteHandler.js","sourceRoot":"","sources":["../../../../../packages/http/http-server/src/RouteHandler.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;;GAYG;AACH,MAAsB,YAAY;CAOjC;AAPD,oCAOC","sourcesContent":["import { MethodMeta } from './MethodMeta';\n\n/**\n * Handler class for routes.\n * Takes a MethodMeta and returns the controller method result.\n *\n * Generic type parameter TResult represents the return type of the controller method.\n * Example: RouteHandler<SaveResponse> for a method that returns Promise<SaveResponse>\n *\n * Using unknown as default instead of any forces type safety - consumers must\n * handle the result appropriately rather than assuming any type.\n *\n * This is a class instead of a function type to make it easier to trace\n * who is calling what in the debugger/IDE.\n */\nexport abstract class RouteHandler<TResult = unknown> {\n /**\n * Execute the route handler.\n * @param meta - The method metadata containing request info and params\n * @returns Promise of the controller method result\n */\n abstract execute(meta: MethodMeta): Promise<TResult>;\n}\n"]}
@@ -0,0 +1,44 @@
1
+ import { WebAppMeta } from '@webpieces/core-meta';
2
+ import { WebpiecesServer } from './WebpiecesServer';
3
+ /**
4
+ * WebpiecesFactory - Factory for creating WebPieces server instances.
5
+ *
6
+ * This factory encapsulates the server creation and initialization logic:
7
+ * 1. Creates the WebPieces DI container
8
+ * 2. Loads the provider module for @provideSingleton decorators
9
+ * 3. Resolves WebpiecesServerImpl from DI
10
+ * 4. Calls initialize() with the container and meta
11
+ * 5. Returns the server as the WebpiecesServer interface
12
+ *
13
+ * The returned WebpiecesServer interface only exposes start() and stop(),
14
+ * hiding the internal initialize() method from consumers.
15
+ *
16
+ * Usage:
17
+ * ```typescript
18
+ * const server = WebpiecesFactory.create(new ProdServerMeta());
19
+ * server.start(8080);
20
+ * // ... later
21
+ * server.stop();
22
+ * ```
23
+ *
24
+ * This pattern:
25
+ * - Enforces proper initialization order
26
+ * - Hides implementation details from consumers
27
+ * - Makes the API simpler and harder to misuse
28
+ * - Follows the principle of least privilege
29
+ */
30
+ export declare class WebpiecesFactory {
31
+ /**
32
+ * Create a new WebPieces server instance.
33
+ *
34
+ * This method:
35
+ * 1. Creates the WebPieces framework DI container
36
+ * 2. Loads framework bindings via buildProviderModule()
37
+ * 3. Resolves the server implementation from DI
38
+ * 4. Initializes the server with the container and meta
39
+ *
40
+ * @param meta - User-provided WebAppMeta with DI modules and routes
41
+ * @returns A fully initialized WebpiecesServer ready to start()
42
+ */
43
+ static create(meta: WebAppMeta): WebpiecesServer;
44
+ }
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WebpiecesFactory = void 0;
4
+ const inversify_1 = require("inversify");
5
+ const binding_decorators_1 = require("@inversifyjs/binding-decorators");
6
+ const WebpiecesServerImpl_1 = require("./WebpiecesServerImpl");
7
+ /**
8
+ * WebpiecesFactory - Factory for creating WebPieces server instances.
9
+ *
10
+ * This factory encapsulates the server creation and initialization logic:
11
+ * 1. Creates the WebPieces DI container
12
+ * 2. Loads the provider module for @provideSingleton decorators
13
+ * 3. Resolves WebpiecesServerImpl from DI
14
+ * 4. Calls initialize() with the container and meta
15
+ * 5. Returns the server as the WebpiecesServer interface
16
+ *
17
+ * The returned WebpiecesServer interface only exposes start() and stop(),
18
+ * hiding the internal initialize() method from consumers.
19
+ *
20
+ * Usage:
21
+ * ```typescript
22
+ * const server = WebpiecesFactory.create(new ProdServerMeta());
23
+ * server.start(8080);
24
+ * // ... later
25
+ * server.stop();
26
+ * ```
27
+ *
28
+ * This pattern:
29
+ * - Enforces proper initialization order
30
+ * - Hides implementation details from consumers
31
+ * - Makes the API simpler and harder to misuse
32
+ * - Follows the principle of least privilege
33
+ */
34
+ class WebpiecesFactory {
35
+ /**
36
+ * Create a new WebPieces server instance.
37
+ *
38
+ * This method:
39
+ * 1. Creates the WebPieces framework DI container
40
+ * 2. Loads framework bindings via buildProviderModule()
41
+ * 3. Resolves the server implementation from DI
42
+ * 4. Initializes the server with the container and meta
43
+ *
44
+ * @param meta - User-provided WebAppMeta with DI modules and routes
45
+ * @returns A fully initialized WebpiecesServer ready to start()
46
+ */
47
+ static create(meta) {
48
+ // Create WebPieces container for framework-level bindings
49
+ const webpiecesContainer = new inversify_1.Container();
50
+ // Load buildProviderModule to auto-scan for @provideSingleton decorators
51
+ // This registers framework classes (WebpiecesServerImpl, RouteBuilderImpl)
52
+ webpiecesContainer.load((0, binding_decorators_1.buildProviderModule)());
53
+ // Resolve WebpiecesServerImpl from DI container (proper DI - no 'new'!)
54
+ const serverImpl = webpiecesContainer.get(WebpiecesServerImpl_1.WebpiecesServerImpl);
55
+ // Initialize the server (loads app DI modules, registers routes)
56
+ serverImpl.initialize(webpiecesContainer, meta);
57
+ // Return as interface to hide initialize() from consumers
58
+ return serverImpl;
59
+ }
60
+ }
61
+ exports.WebpiecesFactory = WebpiecesFactory;
62
+ //# sourceMappingURL=WebpiecesFactory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebpiecesFactory.js","sourceRoot":"","sources":["../../../../../packages/http/http-server/src/WebpiecesFactory.ts"],"names":[],"mappings":";;;AAAA,yCAAsC;AACtC,wEAAsE;AAGtE,+DAA4D;AAE5D;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAa,gBAAgB;IACzB;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,MAAM,CAAC,IAAgB;QAC1B,0DAA0D;QAC1D,MAAM,kBAAkB,GAAG,IAAI,qBAAS,EAAE,CAAC;QAE3C,yEAAyE;QACzE,2EAA2E;QAC3E,kBAAkB,CAAC,IAAI,CAAC,IAAA,wCAAmB,GAAE,CAAC,CAAC;QAE/C,wEAAwE;QACxE,MAAM,UAAU,GAAG,kBAAkB,CAAC,GAAG,CAAC,yCAAmB,CAAC,CAAC;QAE/D,iEAAiE;QACjE,UAAU,CAAC,UAAU,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC;QAEhD,0DAA0D;QAC1D,OAAO,UAAU,CAAC;IACtB,CAAC;CACJ;AA9BD,4CA8BC","sourcesContent":["import { Container } from 'inversify';\nimport { buildProviderModule } from '@inversifyjs/binding-decorators';\nimport { WebAppMeta } from '@webpieces/core-meta';\nimport { WebpiecesServer } from './WebpiecesServer';\nimport { WebpiecesServerImpl } from './WebpiecesServerImpl';\n\n/**\n * WebpiecesFactory - Factory for creating WebPieces server instances.\n *\n * This factory encapsulates the server creation and initialization logic:\n * 1. Creates the WebPieces DI container\n * 2. Loads the provider module for @provideSingleton decorators\n * 3. Resolves WebpiecesServerImpl from DI\n * 4. Calls initialize() with the container and meta\n * 5. Returns the server as the WebpiecesServer interface\n *\n * The returned WebpiecesServer interface only exposes start() and stop(),\n * hiding the internal initialize() method from consumers.\n *\n * Usage:\n * ```typescript\n * const server = WebpiecesFactory.create(new ProdServerMeta());\n * server.start(8080);\n * // ... later\n * server.stop();\n * ```\n *\n * This pattern:\n * - Enforces proper initialization order\n * - Hides implementation details from consumers\n * - Makes the API simpler and harder to misuse\n * - Follows the principle of least privilege\n */\nexport class WebpiecesFactory {\n /**\n * Create a new WebPieces server instance.\n *\n * This method:\n * 1. Creates the WebPieces framework DI container\n * 2. Loads framework bindings via buildProviderModule()\n * 3. Resolves the server implementation from DI\n * 4. Initializes the server with the container and meta\n *\n * @param meta - User-provided WebAppMeta with DI modules and routes\n * @returns A fully initialized WebpiecesServer ready to start()\n */\n static create(meta: WebAppMeta): WebpiecesServer {\n // Create WebPieces container for framework-level bindings\n const webpiecesContainer = new Container();\n\n // Load buildProviderModule to auto-scan for @provideSingleton decorators\n // This registers framework classes (WebpiecesServerImpl, RouteBuilderImpl)\n webpiecesContainer.load(buildProviderModule());\n\n // Resolve WebpiecesServerImpl from DI container (proper DI - no 'new'!)\n const serverImpl = webpiecesContainer.get(WebpiecesServerImpl);\n\n // Initialize the server (loads app DI modules, registers routes)\n serverImpl.initialize(webpiecesContainer, meta);\n\n // Return as interface to hide initialize() from consumers\n return serverImpl;\n }\n}\n"]}
@@ -1,55 +1,32 @@
1
- import { WebAppMeta } from '@webpieces/core-meta';
2
1
  /**
3
- * WebpiecesServer - Main bootstrap class for WebPieces applications.
2
+ * WebpiecesServer - Public interface for WebPieces server.
4
3
  *
5
- * This class uses a two-container pattern similar to Java WebPieces:
6
- * 1. webpiecesContainer: Core WebPieces framework bindings
7
- * 2. appContainer: User's application bindings (child of webpiecesContainer)
4
+ * This interface exposes only the methods needed by application code:
5
+ * - start(): Start the HTTP server
6
+ * - stop(): Stop the HTTP server
8
7
  *
9
- * This separation allows:
10
- * - Clean separation of concerns
11
- * - Better testability
12
- * - Ability to override framework bindings in tests
8
+ * The initialization logic is hidden inside WebpiecesFactory.create().
9
+ * This provides a clean API and prevents accidental re-initialization.
13
10
  *
14
- * The server:
15
- * 1. Initializes both DI containers from WebAppMeta.getDIModules()
16
- * 2. Registers routes using explicit RouteBuilderImpl
17
- * 3. Creates filter chains
18
- * 4. Supports both HTTP server mode and testing mode (no HTTP)
19
- *
20
- * Usage for testing (no HTTP):
21
- * ```typescript
22
- * const server = new WebpiecesServer(new ProdServerMeta());
23
- * server.initialize();
24
- * const saveApi = server.createApiClient<SaveApi>(SaveApiPrototype);
25
- * const response = await saveApi.save(request);
26
- * ```
27
- *
28
- * Usage for production (HTTP server):
11
+ * Usage:
29
12
  * ```typescript
30
- * const server = new WebpiecesServer(new ProdServerMeta());
31
- * server.start(); // Starts Express server
13
+ * const server = WebpiecesFactory.create(new ProdServerMeta());
14
+ * await server.start(8080);
15
+ * console.log('Server is now listening!');
16
+ * // ... later
17
+ * server.stop();
32
18
  * ```
33
19
  */
34
- export declare class WebpiecesServer {
35
- private meta;
36
- /**
37
- * WebPieces container: Core WebPieces framework bindings.
38
- * This includes framework-level services like filters, routing infrastructure,
39
- * logging, metrics, etc. Similar to Java WebPieces platform container.
40
- */
41
- private webpiecesContainer;
42
- private coreService;
43
- constructor(meta: WebAppMeta);
44
- /**
45
- * Initialize the server (DI container, routes, filters).
46
- * This is called automatically by start() or can be called manually for testing.
47
- */
48
- initialize(): void;
20
+ export interface WebpiecesServer {
49
21
  /**
50
22
  * Start the HTTP server with Express.
23
+ * Returns a Promise that resolves when the server is listening,
24
+ * or rejects if the server fails to start.
25
+ *
26
+ * @param port - The port to listen on (default: 8080)
27
+ * @returns Promise that resolves when server is ready
51
28
  */
52
- start(port?: number): void;
29
+ start(port?: number): Promise<void>;
53
30
  /**
54
31
  * Stop the HTTP server.
55
32
  */
@@ -1,72 +1,3 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WebpiecesServer = void 0;
4
- const inversify_1 = require("inversify");
5
- const binding_decorators_1 = require("@inversifyjs/binding-decorators");
6
- const WebpiecesCoreServer_1 = require("./WebpiecesCoreServer");
7
- /**
8
- * WebpiecesServer - Main bootstrap class for WebPieces applications.
9
- *
10
- * This class uses a two-container pattern similar to Java WebPieces:
11
- * 1. webpiecesContainer: Core WebPieces framework bindings
12
- * 2. appContainer: User's application bindings (child of webpiecesContainer)
13
- *
14
- * This separation allows:
15
- * - Clean separation of concerns
16
- * - Better testability
17
- * - Ability to override framework bindings in tests
18
- *
19
- * The server:
20
- * 1. Initializes both DI containers from WebAppMeta.getDIModules()
21
- * 2. Registers routes using explicit RouteBuilderImpl
22
- * 3. Creates filter chains
23
- * 4. Supports both HTTP server mode and testing mode (no HTTP)
24
- *
25
- * Usage for testing (no HTTP):
26
- * ```typescript
27
- * const server = new WebpiecesServer(new ProdServerMeta());
28
- * server.initialize();
29
- * const saveApi = server.createApiClient<SaveApi>(SaveApiPrototype);
30
- * const response = await saveApi.save(request);
31
- * ```
32
- *
33
- * Usage for production (HTTP server):
34
- * ```typescript
35
- * const server = new WebpiecesServer(new ProdServerMeta());
36
- * server.start(); // Starts Express server
37
- * ```
38
- */
39
- class WebpiecesServer {
40
- constructor(meta) {
41
- this.meta = meta;
42
- // Create WebPieces container for framework-level bindings
43
- this.webpiecesContainer = new inversify_1.Container();
44
- // Load buildProviderModule to auto-scan for @provideSingleton decorators
45
- // This registers framework classes (WebpiecesCoreServer, RouteBuilderImpl)
46
- this.webpiecesContainer.load((0, binding_decorators_1.buildProviderModule)());
47
- // Resolve WebpiecesCoreServer from DI container (proper DI - no 'new'!)
48
- this.coreService = this.webpiecesContainer.get(WebpiecesCoreServer_1.WebpiecesCoreServer);
49
- }
50
- /**
51
- * Initialize the server (DI container, routes, filters).
52
- * This is called automatically by start() or can be called manually for testing.
53
- */
54
- initialize() {
55
- this.coreService.initialize(this.webpiecesContainer, this.meta);
56
- }
57
- /**
58
- * Start the HTTP server with Express.
59
- */
60
- start(port = 8080) {
61
- this.initialize();
62
- this.coreService.start(port);
63
- }
64
- /**
65
- * Stop the HTTP server.
66
- */
67
- stop() {
68
- this.coreService.stop();
69
- }
70
- }
71
- exports.WebpiecesServer = WebpiecesServer;
72
3
  //# sourceMappingURL=WebpiecesServer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"WebpiecesServer.js","sourceRoot":"","sources":["../../../../../packages/http/http-server/src/WebpiecesServer.ts"],"names":[],"mappings":";;;AAAA,yCAAsC;AACtC,wEAAsE;AAEtE,+DAA4D;AAE5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAa,eAAe;IAY1B,YAAY,IAAgB;QAC1B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QAEjB,0DAA0D;QAC1D,IAAI,CAAC,kBAAkB,GAAG,IAAI,qBAAS,EAAE,CAAC;QAE1C,yEAAyE;QACzE,2EAA2E;QAC3E,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAA,wCAAmB,GAAE,CAAC,CAAC;QAEpD,wEAAwE;QACxE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,yCAAmB,CAAC,CAAC;IACtE,CAAC;IAED;;;OAGG;IACH,UAAU;QACR,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,kBAAkB,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAClE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAe,IAAI;QACvB,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED;;OAEG;IACH,IAAI;QACF,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;IAC1B,CAAC;CAEF;AAjDD,0CAiDC","sourcesContent":["import { Container } from 'inversify';\nimport { buildProviderModule } from '@inversifyjs/binding-decorators';\nimport { WebAppMeta } from '@webpieces/core-meta';\nimport { WebpiecesCoreServer } from \"./WebpiecesCoreServer\";\n\n/**\n * WebpiecesServer - Main bootstrap class for WebPieces applications.\n *\n * This class uses a two-container pattern similar to Java WebPieces:\n * 1. webpiecesContainer: Core WebPieces framework bindings\n * 2. appContainer: User's application bindings (child of webpiecesContainer)\n *\n * This separation allows:\n * - Clean separation of concerns\n * - Better testability\n * - Ability to override framework bindings in tests\n *\n * The server:\n * 1. Initializes both DI containers from WebAppMeta.getDIModules()\n * 2. Registers routes using explicit RouteBuilderImpl\n * 3. Creates filter chains\n * 4. Supports both HTTP server mode and testing mode (no HTTP)\n *\n * Usage for testing (no HTTP):\n * ```typescript\n * const server = new WebpiecesServer(new ProdServerMeta());\n * server.initialize();\n * const saveApi = server.createApiClient<SaveApi>(SaveApiPrototype);\n * const response = await saveApi.save(request);\n * ```\n *\n * Usage for production (HTTP server):\n * ```typescript\n * const server = new WebpiecesServer(new ProdServerMeta());\n * server.start(); // Starts Express server\n * ```\n */\nexport class WebpiecesServer {\n private meta: WebAppMeta;\n\n /**\n * WebPieces container: Core WebPieces framework bindings.\n * This includes framework-level services like filters, routing infrastructure,\n * logging, metrics, etc. Similar to Java WebPieces platform container.\n */\n private webpiecesContainer: Container;\n\n private coreService: WebpiecesCoreServer;\n\n constructor(meta: WebAppMeta) {\n this.meta = meta;\n\n // Create WebPieces container for framework-level bindings\n this.webpiecesContainer = new Container();\n\n // Load buildProviderModule to auto-scan for @provideSingleton decorators\n // This registers framework classes (WebpiecesCoreServer, RouteBuilderImpl)\n this.webpiecesContainer.load(buildProviderModule());\n\n // Resolve WebpiecesCoreServer from DI container (proper DI - no 'new'!)\n this.coreService = this.webpiecesContainer.get(WebpiecesCoreServer);\n }\n\n /**\n * Initialize the server (DI container, routes, filters).\n * This is called automatically by start() or can be called manually for testing.\n */\n initialize(): void {\n this.coreService.initialize(this.webpiecesContainer, this.meta);\n }\n\n /**\n * Start the HTTP server with Express.\n */\n start(port: number = 8080): void {\n this.initialize();\n this.coreService.start(port);\n }\n\n /**\n * Stop the HTTP server.\n */\n stop(): void {\n this.coreService.stop();\n }\n\n}\n"]}
1
+ {"version":3,"file":"WebpiecesServer.js","sourceRoot":"","sources":["../../../../../packages/http/http-server/src/WebpiecesServer.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * WebpiecesServer - Public interface for WebPieces server.\n *\n * This interface exposes only the methods needed by application code:\n * - start(): Start the HTTP server\n * - stop(): Stop the HTTP server\n *\n * The initialization logic is hidden inside WebpiecesFactory.create().\n * This provides a clean API and prevents accidental re-initialization.\n *\n * Usage:\n * ```typescript\n * const server = WebpiecesFactory.create(new ProdServerMeta());\n * await server.start(8080);\n * console.log('Server is now listening!');\n * // ... later\n * server.stop();\n * ```\n */\nexport interface WebpiecesServer {\n /**\n * Start the HTTP server with Express.\n * Returns a Promise that resolves when the server is listening,\n * or rejects if the server fails to start.\n *\n * @param port - The port to listen on (default: 8080)\n * @returns Promise that resolves when server is ready\n */\n start(port?: number): Promise<void>;\n\n /**\n * Stop the HTTP server.\n */\n stop(): void;\n}\n"]}
@@ -1,8 +1,12 @@
1
1
  import { Container } from 'inversify';
2
2
  import { WebAppMeta } from '@webpieces/core-meta';
3
3
  import { RouteBuilderImpl } from './RouteBuilderImpl';
4
+ import { WebpiecesServer } from './WebpiecesServer';
4
5
  /**
5
- * WebpiecesCoreServer - Core server implementation with DI.
6
+ * WebpiecesServerImpl - Internal server implementation.
7
+ *
8
+ * This class implements the WebpiecesServer interface and contains
9
+ * all the actual server logic. It is created by WebpiecesFactory.create().
6
10
  *
7
11
  * This class uses a two-container pattern similar to Java WebPieces:
8
12
  * 1. webpiecesContainer: Core WebPieces framework bindings
@@ -20,9 +24,9 @@ import { RouteBuilderImpl } from './RouteBuilderImpl';
20
24
  * 4. Supports both HTTP server mode and testing mode (no HTTP)
21
25
  *
22
26
  * DI Pattern: This class is registered in webpiecesContainer via @provideSingleton()
23
- * and resolved by WebpiecesServer. It receives RouteBuilder via constructor injection.
27
+ * and resolved by WebpiecesFactory. It receives RouteBuilder via constructor injection.
24
28
  */
25
- export declare class WebpiecesCoreServer {
29
+ export declare class WebpiecesServerImpl implements WebpiecesServer {
26
30
  private routeBuilder;
27
31
  private meta;
28
32
  private webpiecesContainer;
@@ -39,7 +43,8 @@ export declare class WebpiecesCoreServer {
39
43
  constructor(routeBuilder: RouteBuilderImpl);
40
44
  /**
41
45
  * Initialize the server (DI container, routes, filters).
42
- * This is called by WebpiecesServer after resolving this class from DI.
46
+ * This is called by WebpiecesFactory.create() after resolving this class from DI.
47
+ * This method is internal and not exposed on the WebpiecesServer interface.
43
48
  *
44
49
  * @param webpiecesContainer - The framework container
45
50
  * @param meta - User-provided WebAppMeta with DI modules and routes
@@ -81,9 +86,13 @@ export declare class WebpiecesCoreServer {
81
86
  private logNextLayer;
82
87
  /**
83
88
  * Start the HTTP server with Express.
84
- * Assumes initialize() has already been called by WebpiecesServer.
89
+ * Returns a Promise that resolves when the server is listening,
90
+ * or rejects if the server fails to start.
91
+ *
92
+ * @param port - The port to listen on (default: 8080)
93
+ * @returns Promise that resolves when server is ready
85
94
  */
86
- start(port?: number): void;
95
+ start(port?: number): Promise<void>;
87
96
  /**
88
97
  * Register all routes with Express.
89
98
  */
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WebpiecesCoreServer = void 0;
3
+ exports.WebpiecesServerImpl = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const express_1 = tslib_1.__importDefault(require("express"));
6
6
  const inversify_1 = require("inversify");
@@ -13,7 +13,10 @@ const FilterMatcher_1 = require("./FilterMatcher");
13
13
  const core_util_1 = require("@webpieces/core-util");
14
14
  const MethodMeta_1 = require("./MethodMeta");
15
15
  /**
16
- * WebpiecesCoreServer - Core server implementation with DI.
16
+ * WebpiecesServerImpl - Internal server implementation.
17
+ *
18
+ * This class implements the WebpiecesServer interface and contains
19
+ * all the actual server logic. It is created by WebpiecesFactory.create().
17
20
  *
18
21
  * This class uses a two-container pattern similar to Java WebPieces:
19
22
  * 1. webpiecesContainer: Core WebPieces framework bindings
@@ -31,9 +34,9 @@ const MethodMeta_1 = require("./MethodMeta");
31
34
  * 4. Supports both HTTP server mode and testing mode (no HTTP)
32
35
  *
33
36
  * DI Pattern: This class is registered in webpiecesContainer via @provideSingleton()
34
- * and resolved by WebpiecesServer. It receives RouteBuilder via constructor injection.
37
+ * and resolved by WebpiecesFactory. It receives RouteBuilder via constructor injection.
35
38
  */
36
- let WebpiecesCoreServer = class WebpiecesCoreServer {
39
+ let WebpiecesServerImpl = class WebpiecesServerImpl {
37
40
  constructor(routeBuilder) {
38
41
  this.routeBuilder = routeBuilder;
39
42
  this.initialized = false;
@@ -41,7 +44,8 @@ let WebpiecesCoreServer = class WebpiecesCoreServer {
41
44
  }
42
45
  /**
43
46
  * Initialize the server (DI container, routes, filters).
44
- * This is called by WebpiecesServer after resolving this class from DI.
47
+ * This is called by WebpiecesFactory.create() after resolving this class from DI.
48
+ * This method is internal and not exposed on the WebpiecesServer interface.
45
49
  *
46
50
  * @param webpiecesContainer - The framework container
47
51
  * @param meta - User-provided WebAppMeta with DI modules and routes
@@ -145,11 +149,15 @@ let WebpiecesCoreServer = class WebpiecesCoreServer {
145
149
  }
146
150
  /**
147
151
  * Start the HTTP server with Express.
148
- * Assumes initialize() has already been called by WebpiecesServer.
152
+ * Returns a Promise that resolves when the server is listening,
153
+ * or rejects if the server fails to start.
154
+ *
155
+ * @param port - The port to listen on (default: 8080)
156
+ * @returns Promise that resolves when server is ready
149
157
  */
150
158
  start(port = 8080) {
151
159
  if (!this.initialized) {
152
- throw new Error('Server not initialized. Call initialize() before start().');
160
+ return Promise.reject(new Error('Server not initialized. Call initialize() before start().'));
153
161
  }
154
162
  this.port = port;
155
163
  // Create Express app
@@ -164,10 +172,18 @@ let WebpiecesCoreServer = class WebpiecesCoreServer {
164
172
  // Register routes (these become the innermost handlers)
165
173
  this.registerExpressRoutes();
166
174
  const routes = this.routeBuilder.getRoutes();
167
- // Start listening
168
- this.server = this.app.listen(this.port, () => {
169
- console.log(`[WebpiecesServer] Server listening on http://localhost:${this.port}`);
170
- console.log(`[WebpiecesServer] Registered ${routes.size} routes`);
175
+ // Start listening - wrap in Promise
176
+ return new Promise((resolve, reject) => {
177
+ this.server = this.app.listen(this.port, (error) => {
178
+ if (error) {
179
+ console.error(`[WebpiecesServer] Failed to start server:`, error);
180
+ reject(error);
181
+ return;
182
+ }
183
+ console.log(`[WebpiecesServer] Server listening on http://localhost:${this.port}`);
184
+ console.log(`[WebpiecesServer] Registered ${routes.size} routes`);
185
+ resolve();
186
+ });
171
187
  });
172
188
  }
173
189
  /**
@@ -202,7 +218,7 @@ let WebpiecesCoreServer = class WebpiecesCoreServer {
202
218
  console.log(`[WebpiecesServer] Registering route: ${method.toUpperCase()} ${path}`);
203
219
  // Find matching filters for this route - FilterMatcher returns Filter[] not FilterWithMeta[]
204
220
  // So we need to convert our FilterWithMeta[] to what FilterMatcher expects
205
- const filterDefinitions = filtersWithMeta.map(fwm => {
221
+ const filterDefinitions = filtersWithMeta.map((fwm) => {
206
222
  // Set the filter instance on the definition for FilterMatcher
207
223
  const def = fwm.definition;
208
224
  def.filter = fwm.filter;
@@ -216,7 +232,7 @@ let WebpiecesCoreServer = class WebpiecesCoreServer {
216
232
  const result = await routeWithMeta.handler.execute(meta);
217
233
  const responseWrapper = new http_filters_1.WpResponse(result);
218
234
  return responseWrapper;
219
- }
235
+ },
220
236
  };
221
237
  // Chain filters with the controller service (reverse order for correct execution)
222
238
  // IMPORTANT: MUST USE Filter.chain(filter) and Filter.chainService(svc);
@@ -266,11 +282,11 @@ let WebpiecesCoreServer = class WebpiecesCoreServer {
266
282
  }
267
283
  }
268
284
  };
269
- exports.WebpiecesCoreServer = WebpiecesCoreServer;
270
- exports.WebpiecesCoreServer = WebpiecesCoreServer = tslib_1.__decorate([
285
+ exports.WebpiecesServerImpl = WebpiecesServerImpl;
286
+ exports.WebpiecesServerImpl = WebpiecesServerImpl = tslib_1.__decorate([
271
287
  (0, http_routing_1.provideSingleton)(),
272
288
  (0, inversify_1.injectable)(),
273
289
  tslib_1.__param(0, (0, inversify_1.inject)(RouteBuilderImpl_1.RouteBuilderImpl)),
274
290
  tslib_1.__metadata("design:paramtypes", [RouteBuilderImpl_1.RouteBuilderImpl])
275
- ], WebpiecesCoreServer);
276
- //# sourceMappingURL=WebpiecesCoreServer.js.map
291
+ ], WebpiecesServerImpl);
292
+ //# sourceMappingURL=WebpiecesServerImpl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebpiecesServerImpl.js","sourceRoot":"","sources":["../../../../../packages/http/http-server/src/WebpiecesServerImpl.ts"],"names":[],"mappings":"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","sourcesContent":["import express, { Express, NextFunction, Request, Response } from 'express';\nimport { Container, inject, injectable } from 'inversify';\nimport { buildProviderModule } from '@inversifyjs/binding-decorators';\nimport { RouteRequest, WebAppMeta } from '@webpieces/core-meta';\nimport { WpResponse, Service } from '@webpieces/http-filters';\nimport { provideSingleton } from '@webpieces/http-routing';\nimport { RouteBuilderImpl, RouteHandlerWithMeta, FilterWithMeta } from './RouteBuilderImpl';\nimport { FilterMatcher } from './FilterMatcher';\nimport { toError } from '@webpieces/core-util';\nimport { MethodMeta } from './MethodMeta';\nimport { WebpiecesServer } from './WebpiecesServer';\n\n/**\n * WebpiecesServerImpl - Internal server implementation.\n *\n * This class implements the WebpiecesServer interface and contains\n * all the actual server logic. It is created by WebpiecesFactory.create().\n *\n * This class uses a two-container pattern similar to Java WebPieces:\n * 1. webpiecesContainer: Core WebPieces framework bindings\n * 2. appContainer: User's application bindings (child of webpiecesContainer)\n *\n * This separation allows:\n * - Clean separation of concerns\n * - Better testability\n * - Ability to override framework bindings in tests\n *\n * The server:\n * 1. Initializes both DI containers from WebAppMeta.getDIModules()\n * 2. Registers routes using explicit RouteBuilderImpl\n * 3. Creates filter chains\n * 4. Supports both HTTP server mode and testing mode (no HTTP)\n *\n * DI Pattern: This class is registered in webpiecesContainer via @provideSingleton()\n * and resolved by WebpiecesFactory. It receives RouteBuilder via constructor injection.\n */\n@provideSingleton()\n@injectable()\nexport class WebpiecesServerImpl implements WebpiecesServer {\n private meta!: WebAppMeta;\n private webpiecesContainer!: Container;\n\n /**\n * Application container: User's application bindings.\n * This is a child container of webpiecesContainer, so it can access\n * framework bindings while keeping app bindings separate.\n */\n private appContainer!: Container;\n\n private initialized = false;\n private app?: Express;\n private server?: ReturnType<Express['listen']>;\n private port: number = 8080;\n\n constructor(@inject(RouteBuilderImpl) private routeBuilder: RouteBuilderImpl) {}\n\n /**\n * Initialize the server (DI container, routes, filters).\n * This is called by WebpiecesFactory.create() after resolving this class from DI.\n * This method is internal and not exposed on the WebpiecesServer interface.\n *\n * @param webpiecesContainer - The framework container\n * @param meta - User-provided WebAppMeta with DI modules and routes\n */\n initialize(webpiecesContainer: Container, meta: WebAppMeta): void {\n if (this.initialized) {\n return;\n }\n\n this.webpiecesContainer = webpiecesContainer;\n this.meta = meta;\n\n // Create application container as child of framework container\n this.appContainer = new Container({ parent: this.webpiecesContainer });\n\n // Set container on RouteBuilder (late binding - appContainer didn't exist in constructor)\n this.routeBuilder.setContainer(this.appContainer);\n\n // 1. Load DI modules\n this.loadDIModules();\n\n // 2. Register routes and filters\n this.registerRoutes();\n\n this.initialized = true;\n }\n\n /**\n * Load DI modules from WebAppMeta.\n *\n * Currently, all user modules are loaded into the application container.\n * In the future, we could separate:\n * - WebPieces framework modules -> webpiecesContainer\n * - Application modules -> appContainer\n *\n * For now, everything goes into appContainer which has access to webpiecesContainer.\n */\n private loadDIModules(): void {\n const modules = this.meta.getDIModules();\n\n // Load buildProviderModule to auto-scan for @provideSingleton decorators\n this.appContainer.load(buildProviderModule());\n\n // Load all modules into application container\n // (webpiecesContainer is currently empty, reserved for future framework bindings)\n for (const module of modules) {\n this.appContainer.load(module);\n }\n }\n\n /**\n * Register routes from WebAppMeta.\n *\n * Creates an explicit RouteBuilderImpl instead of an anonymous object.\n * This improves:\n * - Traceability: Can Cmd+Click on addRoute to see implementation\n * - Debugging: Explicit class shows up in stack traces\n * - Understanding: Clear class name vs anonymous object\n */\n private registerRoutes(): void {\n const routeConfigs = this.meta.getRoutes();\n\n // Configure routes using the explicit RouteBuilder\n for (const routeConfig of routeConfigs) {\n routeConfig.configure(this.routeBuilder);\n }\n }\n\n /**\n * Global error handler middleware - catches ALL unhandled errors.\n * Returns HTML 500 error page for any errors that escape the filter chain.\n *\n * This is the outermost safety net - JsonFilter catches JSON API errors,\n * this catches everything else.\n */\n private async globalErrorHandler(\n req: Request,\n res: Response,\n next: NextFunction,\n ): Promise<void> {\n console.log('🔴 [Layer 1: GlobalErrorHandler] Request START:', req.method, req.path);\n\n try {\n // await next() catches BOTH:\n // 1. Synchronous throws from next() itself\n // 2. Rejected promises from downstream async middleware\n await next();\n console.log(\n '🔴 [Layer 1: GlobalErrorHandler] Request END (success):',\n req.method,\n req.path,\n );\n } catch (err: unknown) {\n const error = toError(err);\n console.error('🔴 [Layer 1: GlobalErrorHandler] Caught unhandled error:', error);\n if (!res.headersSent) {\n // Return HTML error page (not JSON - JsonFilter handles JSON errors)\n res.status(500).send(`\n <!DOCTYPE html>\n <html>\n <head><title>Server Error</title></head>\n <body>\n <h1>You hit a server error</h1>\n <p>An unexpected error occurred while processing your request.</p>\n <pre>${error.message}</pre>\n </body>\n </html>\n `);\n }\n console.log(\n '🔴 [Layer 1: GlobalErrorHandler] Request END (error):',\n req.method,\n req.path,\n );\n }\n }\n\n /**\n * Logging middleware - logs request/response flow.\n * Demonstrates middleware execution order.\n */\n private logNextLayer(req: Request, res: Response, next: NextFunction): void {\n console.log('🟡 [Layer 2: LogNextLayer] Before next() -', req.method, req.path);\n next();\n console.log('🟡 [Layer 2: LogNextLayer] After next() -', req.method, req.path);\n }\n\n /**\n * Start the HTTP server with Express.\n * Returns a Promise that resolves when the server is listening,\n * or rejects if the server fails to start.\n *\n * @param port - The port to listen on (default: 8080)\n * @returns Promise that resolves when server is ready\n */\n start(port: number = 8080): Promise<void> {\n if (!this.initialized) {\n return Promise.reject(\n new Error('Server not initialized. Call initialize() before start().'),\n );\n }\n\n this.port = port;\n\n // Create Express app\n this.app = express();\n\n // Parse JSON request bodies\n this.app.use(express.json());\n\n // Layer 1: Global Error Handler (OUTERMOST - runs FIRST)\n // Catches all unhandled errors and returns HTML 500 page\n this.app.use(this.globalErrorHandler.bind(this));\n\n // Layer 2: Request/Response Logging\n this.app.use(this.logNextLayer.bind(this));\n\n // Register routes (these become the innermost handlers)\n this.registerExpressRoutes();\n\n const routes = this.routeBuilder.getRoutes();\n\n // Start listening - wrap in Promise\n return new Promise((resolve, reject) => {\n this.server = this.app!.listen(this.port, (error?: Error) => {\n if (error) {\n console.error(`[WebpiecesServer] Failed to start server:`, error);\n reject(error);\n return;\n }\n console.log(`[WebpiecesServer] Server listening on http://localhost:${this.port}`);\n console.log(`[WebpiecesServer] Registered ${routes.size} routes`);\n resolve();\n });\n });\n }\n\n /**\n * Register all routes with Express.\n */\n private registerExpressRoutes(): void {\n if (!this.app) {\n throw new Error('Express app not initialized');\n }\n\n const routes = this.routeBuilder.getRoutes();\n const sortedFilters = this.routeBuilder.getSortedFilters();\n for (const [key, routeWithMeta] of routes.entries()) {\n this.setupRoute(key, routeWithMeta, sortedFilters);\n }\n }\n\n /**\n * Setup a single route with Express.\n * Finds matching filters, creates handler, and registers with Express.\n *\n * @param key - Route key (method:path)\n * @param routeWithMeta - The route handler paired with its definition\n * @param filtersWithMeta - All filters with their definitions\n */\n private setupRoute(\n key: string,\n routeWithMeta: RouteHandlerWithMeta,\n filtersWithMeta: Array<FilterWithMeta>,\n ): void {\n if (!this.app) {\n throw new Error('Express app not initialized');\n }\n\n const route = routeWithMeta.definition;\n const routeMeta = route.routeMeta;\n const method = routeMeta.httpMethod.toLowerCase();\n const path = routeMeta.path;\n\n console.log(`[WebpiecesServer] Registering route: ${method.toUpperCase()} ${path}`);\n\n // Find matching filters for this route - FilterMatcher returns Filter[] not FilterWithMeta[]\n // So we need to convert our FilterWithMeta[] to what FilterMatcher expects\n const filterDefinitions = filtersWithMeta.map((fwm) => {\n // Set the filter instance on the definition for FilterMatcher\n const def = fwm.definition;\n def.filter = fwm.filter;\n return def;\n });\n\n const matchingFilters = FilterMatcher.findMatchingFilters(\n route.controllerFilepath,\n filterDefinitions,\n );\n\n // Create service that wraps the controller execution\n const controllerService: Service<MethodMeta, WpResponse> = {\n invoke: async (meta: MethodMeta): Promise<WpResponse> => {\n // Invoke the controller method via route handler\n const result = await routeWithMeta.handler.execute(meta);\n const responseWrapper = new WpResponse(result);\n return responseWrapper;\n },\n };\n\n // Chain filters with the controller service (reverse order for correct execution)\n // IMPORTANT: MUST USE Filter.chain(filter) and Filter.chainService(svc);\n let filterChain = matchingFilters[matchingFilters.length - 1];\n for (let i = matchingFilters.length - 2; i >= 0; i--) {\n filterChain = filterChain.chain(matchingFilters[i]);\n }\n const svc = filterChain.chainService(controllerService);\n\n // Create Express route handler - delegates to filter chain\n const handler = async (req: Request, res: Response, next: NextFunction) => {\n // Create RouteRequest with Express Request/Response\n const routeRequest = new RouteRequest(req, res);\n\n // Create MethodMeta with route info and Express Request/Response\n const meta = new MethodMeta(routeMeta, routeRequest);\n\n // Response is written by JsonFilter - we just await completion\n await svc.invoke(meta);\n };\n\n // Register with Express\n switch (method) {\n case 'get':\n this.app.get(path, handler);\n break;\n case 'post':\n this.app.post(path, handler);\n break;\n case 'put':\n this.app.put(path, handler);\n break;\n case 'delete':\n this.app.delete(path, handler);\n break;\n case 'patch':\n this.app.patch(path, handler);\n break;\n default:\n console.warn(`[WebpiecesServer] Unknown HTTP method: ${method}`);\n }\n }\n\n /**\n * Stop the HTTP server.\n */\n stop(): void {\n if (this.server) {\n this.server.close(() => {\n console.log('[WebpiecesServer] Server stopped');\n });\n }\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"ContextFilter.js","sourceRoot":"","sources":["../../../../../../packages/http/http-server/src/filters/ContextFilter.ts"],"names":[],"mappings":";;;;AAAA,yCAAuC;AACvC,0DAA2D;AAC3D,0DAAyD;AACzD,0DAAsE;AAGtE;;;;;;GAMG;AAGI,IAAM,aAAa,GAAnB,MAAM,aAAc,SAAQ,qBAAuC;IAExE,KAAK,CAAC,MAAM,CACV,IAAgB,EAChB,UAAoD;QAEpD,wDAAwD;QACxD,OAAO,6BAAc,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE;YACnC,gEAAgE;YAChE,6BAAc,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;YACxC,6BAAc,CAAC,GAAG,CAAC,cAAc,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9C,6BAAc,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;YAEnD,OAAO,MAAM,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACrC,2CAA2C;QAC7C,CAAC,CAAC,CAAC;IACL,CAAC;CACF,CAAA;AAjBY,sCAAa;wBAAb,aAAa;IAFzB,IAAA,+BAAgB,GAAE;IAClB,IAAA,sBAAU,GAAE;GACA,aAAa,CAiBzB","sourcesContent":["import { injectable } from 'inversify';\nimport { provideSingleton } from '@webpieces/http-routing';\nimport { RequestContext } from '@webpieces/core-context';\nimport { Filter, WpResponse, Service } from '@webpieces/http-filters';\nimport { MethodMeta } from '../MethodMeta';\n\n/**\n * ContextFilter - Sets up AsyncLocalStorage context for each request.\n * Priority: 140 (executes first)\n *\n * This filter ensures that all subsequent filters and the controller\n * execute within a context that can store request-scoped data.\n */\n@provideSingleton()\n@injectable()\nexport class ContextFilter extends Filter<MethodMeta, WpResponse<unknown>> {\n\n async filter(\n meta: MethodMeta,\n nextFilter: Service<MethodMeta, WpResponse<unknown>>\n ): Promise<WpResponse<unknown>> {\n // Run the rest of the filter chain within a new context\n return RequestContext.run(async () => {\n // Store request metadata in context for other filters to access\n RequestContext.put('METHOD_META', meta);\n RequestContext.put('REQUEST_PATH', meta.path);\n RequestContext.put('HTTP_METHOD', meta.httpMethod);\n\n return await nextFilter.invoke(meta);\n //RequestContext is auto cleared when done.\n });\n }\n}\n"]}
1
+ {"version":3,"file":"ContextFilter.js","sourceRoot":"","sources":["../../../../../../packages/http/http-server/src/filters/ContextFilter.ts"],"names":[],"mappings":";;;;AAAA,yCAAuC;AACvC,0DAA2D;AAC3D,0DAAyD;AACzD,0DAAsE;AAGtE;;;;;;GAMG;AAGI,IAAM,aAAa,GAAnB,MAAM,aAAc,SAAQ,qBAAuC;IACtE,KAAK,CAAC,MAAM,CACR,IAAgB,EAChB,UAAoD;QAEpD,wDAAwD;QACxD,OAAO,6BAAc,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE;YACjC,gEAAgE;YAChE,6BAAc,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;YACxC,6BAAc,CAAC,GAAG,CAAC,cAAc,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9C,6BAAc,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;YAEnD,OAAO,MAAM,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACrC,2CAA2C;QAC/C,CAAC,CAAC,CAAC;IACP,CAAC;CACJ,CAAA;AAhBY,sCAAa;wBAAb,aAAa;IAFzB,IAAA,+BAAgB,GAAE;IAClB,IAAA,sBAAU,GAAE;GACA,aAAa,CAgBzB","sourcesContent":["import { injectable } from 'inversify';\nimport { provideSingleton } from '@webpieces/http-routing';\nimport { RequestContext } from '@webpieces/core-context';\nimport { Filter, WpResponse, Service } from '@webpieces/http-filters';\nimport { MethodMeta } from '../MethodMeta';\n\n/**\n * ContextFilter - Sets up AsyncLocalStorage context for each request.\n * Priority: 140 (executes first)\n *\n * This filter ensures that all subsequent filters and the controller\n * execute within a context that can store request-scoped data.\n */\n@provideSingleton()\n@injectable()\nexport class ContextFilter extends Filter<MethodMeta, WpResponse<unknown>> {\n async filter(\n meta: MethodMeta,\n nextFilter: Service<MethodMeta, WpResponse<unknown>>,\n ): Promise<WpResponse<unknown>> {\n // Run the rest of the filter chain within a new context\n return RequestContext.run(async () => {\n // Store request metadata in context for other filters to access\n RequestContext.put('METHOD_META', meta);\n RequestContext.put('REQUEST_PATH', meta.path);\n RequestContext.put('HTTP_METHOD', meta.httpMethod);\n\n return await nextFilter.invoke(meta);\n //RequestContext is auto cleared when done.\n });\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"JsonFilter.js","sourceRoot":"","sources":["../../../../../../packages/http/http-server/src/filters/JsonFilter.ts"],"names":[],"mappings":"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","sourcesContent":["import { injectable, inject } from 'inversify';\nimport { provideSingleton } from '@webpieces/http-routing';\nimport { Filter, WpResponse, Service } from '@webpieces/http-filters';\nimport { toError } from '@webpieces/core-util';\nimport { Request, Response } from 'express';\nimport { MethodMeta } from '../MethodMeta';\n\n/**\n * DI tokens for JsonFilter.\n */\nexport const FILTER_TYPES = {\n JsonFilterConfig: Symbol.for('JsonFilterConfig'),\n};\n\n/**\n * Configuration for JsonFilter.\n * Register this in your DI container to customize JsonFilter behavior.\n */\n@injectable()\nexport class JsonFilterConfig {\n // Configuration options can be added here\n}\n\n/**\n * JsonFilter - Handles JSON serialization/deserialization and writes response to HTTP body.\n *\n * Similar to Java WebPieces JacksonCatchAllFilter.\n *\n * Flow:\n * 1. Log request\n * 2. Deserialize request body to DTO and set on meta.requestDto\n * 3. Call next filter/controller\n * 4. Get response (WpResponse)\n * 5. Write response to Express response\n * 6. On ANY exception, send 500\n */\n@provideSingleton()\n@injectable()\nexport class JsonFilter extends Filter<MethodMeta, WpResponse<unknown>> {\n\n constructor(@inject(FILTER_TYPES.JsonFilterConfig) private config: JsonFilterConfig) {\n super();\n }\n\n async filter(\n meta: MethodMeta,\n nextFilter: Service<MethodMeta, WpResponse<unknown>>\n ): Promise<WpResponse<unknown>> {\n // Get Express Request/Response from routeRequest\n const expressRequest = meta.routeRequest.request as Request;\n const expressResponse = meta.routeRequest.response as Response;\n\n try {\n // 1. Log request\n this.logRequest(meta, expressRequest);\n\n // 2. Deserialize request body to DTO\n this.deserializeRequest(meta, expressRequest);\n\n // 3. Call next filter/controller\n const responseWrapper = await nextFilter.invoke(meta);\n\n // 4. Log response\n this.logResponse(responseWrapper);\n\n // 5. Write response to Express response\n this.writeResponse(expressResponse, responseWrapper);\n\n return responseWrapper;\n } catch (err: unknown) {\n const error = toError(err);\n // 6. On ANY exception, send 500\n console.error('[JsonFilter] Error:', error);\n const errorResponse = new WpResponse({ error: 'Internal server error' }, 500);\n this.writeResponse(expressResponse, errorResponse);\n return errorResponse;\n }\n }\n\n /**\n * Deserialize request body to DTO and set on meta.requestDto.\n */\n private deserializeRequest(meta: MethodMeta, expressRequest: Request): void {\n if (expressRequest.body) {\n // Set the deserialized body as requestDto\n meta.requestDto = expressRequest.body;\n }\n }\n\n /**\n * Write WpResponse to HTTP response body as JSON.\n */\n private writeResponse(\n expressResponse: Response,\n responseWrapper: WpResponse<unknown>\n ): void {\n // Set status code\n expressResponse.status(responseWrapper.statusCode);\n\n // Set headers from wrapper\n responseWrapper.headers.forEach((value, name) => {\n expressResponse.setHeader(name, value);\n });\n\n // Set content type to JSON\n expressResponse.setHeader('Content-Type', 'application/json');\n\n // Serialize and write response body\n if (responseWrapper.response !== undefined) {\n expressResponse.json(responseWrapper.response);\n } else {\n expressResponse.end();\n }\n }\n\n /**\n * Log the incoming request.\n */\n private logRequest(meta: MethodMeta, expressRequest: Request): void {\n console.log(`[JsonFilter] ${meta.httpMethod} ${meta.path}`);\n if (expressRequest.body) {\n console.log('[JsonFilter] Request body:', JSON.stringify(expressRequest.body, null, 2));\n }\n }\n\n /**\n * Log the outgoing response.\n */\n private logResponse(responseWrapper: WpResponse<unknown>): void {\n console.log(`[JsonFilter] Response: ${responseWrapper.statusCode}`);\n if (responseWrapper.response) {\n console.log('[JsonFilter] Response body:', JSON.stringify(responseWrapper.response, null, 2));\n }\n }\n}\n\n/**\n * Exception thrown when validation fails.\n */\nexport class ValidationException extends Error {\n constructor(public violations: string[]) {\n super('Validation failed');\n this.name = 'ValidationException';\n }\n}\n\n/**\n * HTTP exception with status code.\n */\nexport class HttpException extends Error {\n constructor(message: string, public statusCode: number) {\n super(message);\n this.name = 'HttpException';\n }\n}\n"]}
1
+ {"version":3,"file":"JsonFilter.js","sourceRoot":"","sources":["../../../../../../packages/http/http-server/src/filters/JsonFilter.ts"],"names":[],"mappings":"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","sourcesContent":["import { injectable, inject } from 'inversify';\nimport { provideSingleton } from '@webpieces/http-routing';\nimport { Filter, WpResponse, Service } from '@webpieces/http-filters';\nimport { toError } from '@webpieces/core-util';\nimport { Request, Response } from 'express';\nimport { MethodMeta } from '../MethodMeta';\n\n/**\n * DI tokens for JsonFilter.\n */\nexport const FILTER_TYPES = {\n JsonFilterConfig: Symbol.for('JsonFilterConfig'),\n};\n\n/**\n * Configuration for JsonFilter.\n * Register this in your DI container to customize JsonFilter behavior.\n */\n@injectable()\nexport class JsonFilterConfig {\n // Configuration options can be added here\n}\n\n/**\n * JsonFilter - Handles JSON serialization/deserialization and writes response to HTTP body.\n *\n * Similar to Java WebPieces JacksonCatchAllFilter.\n *\n * Flow:\n * 1. Log request\n * 2. Deserialize request body to DTO and set on meta.requestDto\n * 3. Call next filter/controller\n * 4. Get response (WpResponse)\n * 5. Write response to Express response\n * 6. On ANY exception, send 500\n */\n@provideSingleton()\n@injectable()\nexport class JsonFilter extends Filter<MethodMeta, WpResponse<unknown>> {\n constructor(@inject(FILTER_TYPES.JsonFilterConfig) private config: JsonFilterConfig) {\n super();\n }\n\n async filter(\n meta: MethodMeta,\n nextFilter: Service<MethodMeta, WpResponse<unknown>>,\n ): Promise<WpResponse<unknown>> {\n // Get Express Request/Response from routeRequest\n const expressRequest = meta.routeRequest.request as Request;\n const expressResponse = meta.routeRequest.response as Response;\n\n try {\n // 1. Log request\n this.logRequest(meta, expressRequest);\n\n // 2. Deserialize request body to DTO\n this.deserializeRequest(meta, expressRequest);\n\n // 3. Call next filter/controller\n const responseWrapper = await nextFilter.invoke(meta);\n\n // 4. Log response\n this.logResponse(responseWrapper);\n\n // 5. Write response to Express response\n this.writeResponse(expressResponse, responseWrapper);\n\n return responseWrapper;\n } catch (err: unknown) {\n const error = toError(err);\n // 6. On ANY exception, send 500\n console.error('[JsonFilter] Error:', error);\n const errorResponse = new WpResponse({ error: 'Internal server error' }, 500);\n this.writeResponse(expressResponse, errorResponse);\n return errorResponse;\n }\n }\n\n /**\n * Deserialize request body to DTO and set on meta.requestDto.\n */\n private deserializeRequest(meta: MethodMeta, expressRequest: Request): void {\n if (expressRequest.body) {\n // Set the deserialized body as requestDto\n meta.requestDto = expressRequest.body;\n }\n }\n\n /**\n * Write WpResponse to HTTP response body as JSON.\n */\n private writeResponse(expressResponse: Response, responseWrapper: WpResponse<unknown>): void {\n // Set status code\n expressResponse.status(responseWrapper.statusCode);\n\n // Set headers from wrapper\n responseWrapper.headers.forEach((value, name) => {\n expressResponse.setHeader(name, value);\n });\n\n // Set content type to JSON\n expressResponse.setHeader('Content-Type', 'application/json');\n\n // Serialize and write response body\n if (responseWrapper.response !== undefined) {\n expressResponse.json(responseWrapper.response);\n } else {\n expressResponse.end();\n }\n }\n\n /**\n * Log the incoming request.\n */\n private logRequest(meta: MethodMeta, expressRequest: Request): void {\n console.log(`[JsonFilter] ${meta.httpMethod} ${meta.path}`);\n if (expressRequest.body) {\n console.log('[JsonFilter] Request body:', JSON.stringify(expressRequest.body, null, 2));\n }\n }\n\n /**\n * Log the outgoing response.\n */\n private logResponse(responseWrapper: WpResponse<unknown>): void {\n console.log(`[JsonFilter] Response: ${responseWrapper.statusCode}`);\n if (responseWrapper.response) {\n console.log(\n '[JsonFilter] Response body:',\n JSON.stringify(responseWrapper.response, null, 2),\n );\n }\n }\n}\n\n/**\n * Exception thrown when validation fails.\n */\nexport class ValidationException extends Error {\n constructor(public violations: string[]) {\n super('Validation failed');\n this.name = 'ValidationException';\n }\n}\n\n/**\n * HTTP exception with status code.\n */\nexport class HttpException extends Error {\n constructor(\n message: string,\n public statusCode: number,\n ) {\n super(message);\n this.name = 'HttpException';\n }\n}\n"]}
package/src/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { WebpiecesServer } from './WebpiecesServer';
2
- export { RouteBuilderImpl, RouteHandlerWithMeta, FilterWithMeta, HttpFilter } from './RouteBuilderImpl';
3
- export { WebpiecesCoreServer } from './WebpiecesCoreServer';
2
+ export { WebpiecesFactory } from './WebpiecesFactory';
3
+ export { RouteBuilderImpl, RouteHandlerWithMeta, FilterWithMeta, HttpFilter, } from './RouteBuilderImpl';
4
4
  export { RouteHandler } from './RouteHandler';
5
5
  export { MethodMeta } from './MethodMeta';
6
6
  export { ContextFilter } from './filters/ContextFilter';
package/src/index.js CHANGED
@@ -1,14 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.HttpException = exports.ValidationException = exports.FILTER_TYPES = exports.JsonFilterConfig = exports.JsonFilter = exports.ContextFilter = exports.MethodMeta = exports.RouteHandler = exports.WebpiecesCoreServer = exports.FilterWithMeta = exports.RouteHandlerWithMeta = exports.RouteBuilderImpl = exports.WebpiecesServer = void 0;
4
- var WebpiecesServer_1 = require("./WebpiecesServer");
5
- Object.defineProperty(exports, "WebpiecesServer", { enumerable: true, get: function () { return WebpiecesServer_1.WebpiecesServer; } });
3
+ exports.HttpException = exports.ValidationException = exports.FILTER_TYPES = exports.JsonFilterConfig = exports.JsonFilter = exports.ContextFilter = exports.MethodMeta = exports.RouteHandler = exports.FilterWithMeta = exports.RouteHandlerWithMeta = exports.RouteBuilderImpl = exports.WebpiecesFactory = void 0;
4
+ var WebpiecesFactory_1 = require("./WebpiecesFactory");
5
+ Object.defineProperty(exports, "WebpiecesFactory", { enumerable: true, get: function () { return WebpiecesFactory_1.WebpiecesFactory; } });
6
6
  var RouteBuilderImpl_1 = require("./RouteBuilderImpl");
7
7
  Object.defineProperty(exports, "RouteBuilderImpl", { enumerable: true, get: function () { return RouteBuilderImpl_1.RouteBuilderImpl; } });
8
8
  Object.defineProperty(exports, "RouteHandlerWithMeta", { enumerable: true, get: function () { return RouteBuilderImpl_1.RouteHandlerWithMeta; } });
9
9
  Object.defineProperty(exports, "FilterWithMeta", { enumerable: true, get: function () { return RouteBuilderImpl_1.FilterWithMeta; } });
10
- var WebpiecesCoreServer_1 = require("./WebpiecesCoreServer");
11
- Object.defineProperty(exports, "WebpiecesCoreServer", { enumerable: true, get: function () { return WebpiecesCoreServer_1.WebpiecesCoreServer; } });
12
10
  var RouteHandler_1 = require("./RouteHandler");
13
11
  Object.defineProperty(exports, "RouteHandler", { enumerable: true, get: function () { return RouteHandler_1.RouteHandler; } });
14
12
  var MethodMeta_1 = require("./MethodMeta");
package/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/http/http-server/src/index.ts"],"names":[],"mappings":";;;AAAA,qDAAoD;AAA3C,kHAAA,eAAe,OAAA;AACxB,uDAAwG;AAA/F,oHAAA,gBAAgB,OAAA;AAAE,wHAAA,oBAAoB,OAAA;AAAE,kHAAA,cAAc,OAAA;AAC/D,6DAA4D;AAAnD,0HAAA,mBAAmB,OAAA;AAC5B,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AACrB,2CAA0C;AAAjC,wGAAA,UAAU,OAAA;AACnB,yDAAwD;AAA/C,8GAAA,aAAa,OAAA;AACtB,mDAM8B;AAL5B,wGAAA,UAAU,OAAA;AACV,8GAAA,gBAAgB,OAAA;AAChB,0GAAA,YAAY,OAAA;AACZ,iHAAA,mBAAmB,OAAA;AACnB,2GAAA,aAAa,OAAA","sourcesContent":["export { WebpiecesServer } from './WebpiecesServer';\nexport { RouteBuilderImpl, RouteHandlerWithMeta, FilterWithMeta, HttpFilter } from './RouteBuilderImpl';\nexport { WebpiecesCoreServer } from './WebpiecesCoreServer';\nexport { RouteHandler } from './RouteHandler';\nexport { MethodMeta } from './MethodMeta';\nexport { ContextFilter } from './filters/ContextFilter';\nexport {\n JsonFilter,\n JsonFilterConfig,\n FILTER_TYPES,\n ValidationException,\n HttpException,\n} from './filters/JsonFilter';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/http/http-server/src/index.ts"],"names":[],"mappings":";;;AACA,uDAAsD;AAA7C,oHAAA,gBAAgB,OAAA;AACzB,uDAK4B;AAJxB,oHAAA,gBAAgB,OAAA;AAChB,wHAAA,oBAAoB,OAAA;AACpB,kHAAA,cAAc,OAAA;AAGlB,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AACrB,2CAA0C;AAAjC,wGAAA,UAAU,OAAA;AACnB,yDAAwD;AAA/C,8GAAA,aAAa,OAAA;AACtB,mDAM8B;AAL1B,wGAAA,UAAU,OAAA;AACV,8GAAA,gBAAgB,OAAA;AAChB,0GAAA,YAAY,OAAA;AACZ,iHAAA,mBAAmB,OAAA;AACnB,2GAAA,aAAa,OAAA","sourcesContent":["export { WebpiecesServer } from './WebpiecesServer';\nexport { WebpiecesFactory } from './WebpiecesFactory';\nexport {\n RouteBuilderImpl,\n RouteHandlerWithMeta,\n FilterWithMeta,\n HttpFilter,\n} from './RouteBuilderImpl';\nexport { RouteHandler } from './RouteHandler';\nexport { MethodMeta } from './MethodMeta';\nexport { ContextFilter } from './filters/ContextFilter';\nexport {\n JsonFilter,\n JsonFilterConfig,\n FILTER_TYPES,\n ValidationException,\n HttpException,\n} from './filters/JsonFilter';\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"WebpiecesCoreServer.js","sourceRoot":"","sources":["../../../../../packages/http/http-server/src/WebpiecesCoreServer.ts"],"names":[],"mappings":"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","sourcesContent":["import express, {Express, NextFunction, Request, Response} from 'express';\nimport {Container, inject, injectable} from 'inversify';\nimport {buildProviderModule} from '@inversifyjs/binding-decorators';\nimport {RouteRequest, WebAppMeta} from '@webpieces/core-meta';\nimport {WpResponse, Service} from '@webpieces/http-filters';\nimport {provideSingleton} from '@webpieces/http-routing';\nimport {RouteBuilderImpl, RouteHandlerWithMeta, FilterWithMeta} from './RouteBuilderImpl';\nimport {FilterMatcher} from './FilterMatcher';\nimport {toError} from '@webpieces/core-util';\nimport {MethodMeta} from './MethodMeta';\n\n/**\n * WebpiecesCoreServer - Core server implementation with DI.\n *\n * This class uses a two-container pattern similar to Java WebPieces:\n * 1. webpiecesContainer: Core WebPieces framework bindings\n * 2. appContainer: User's application bindings (child of webpiecesContainer)\n *\n * This separation allows:\n * - Clean separation of concerns\n * - Better testability\n * - Ability to override framework bindings in tests\n *\n * The server:\n * 1. Initializes both DI containers from WebAppMeta.getDIModules()\n * 2. Registers routes using explicit RouteBuilderImpl\n * 3. Creates filter chains\n * 4. Supports both HTTP server mode and testing mode (no HTTP)\n *\n * DI Pattern: This class is registered in webpiecesContainer via @provideSingleton()\n * and resolved by WebpiecesServer. It receives RouteBuilder via constructor injection.\n */\n@provideSingleton()\n@injectable()\nexport class WebpiecesCoreServer {\n private meta!: WebAppMeta;\n private webpiecesContainer!: Container;\n\n /**\n * Application container: User's application bindings.\n * This is a child container of webpiecesContainer, so it can access\n * framework bindings while keeping app bindings separate.\n */\n private appContainer!: Container;\n\n private initialized = false;\n private app?: Express;\n private server?: ReturnType<Express['listen']>;\n private port: number = 8080;\n\n constructor(\n @inject(RouteBuilderImpl) private routeBuilder: RouteBuilderImpl\n ) {\n }\n\n /**\n * Initialize the server (DI container, routes, filters).\n * This is called by WebpiecesServer after resolving this class from DI.\n *\n * @param webpiecesContainer - The framework container\n * @param meta - User-provided WebAppMeta with DI modules and routes\n */\n initialize(webpiecesContainer: Container, meta: WebAppMeta): void {\n if (this.initialized) {\n return;\n }\n\n this.webpiecesContainer = webpiecesContainer;\n this.meta = meta;\n\n // Create application container as child of framework container\n this.appContainer = new Container({ parent: this.webpiecesContainer });\n\n // Set container on RouteBuilder (late binding - appContainer didn't exist in constructor)\n this.routeBuilder.setContainer(this.appContainer);\n\n // 1. Load DI modules\n this.loadDIModules();\n\n // 2. Register routes and filters\n this.registerRoutes();\n\n this.initialized = true;\n }\n\n /**\n * Load DI modules from WebAppMeta.\n *\n * Currently, all user modules are loaded into the application container.\n * In the future, we could separate:\n * - WebPieces framework modules -> webpiecesContainer\n * - Application modules -> appContainer\n *\n * For now, everything goes into appContainer which has access to webpiecesContainer.\n */\n private loadDIModules(): void {\n const modules = this.meta.getDIModules();\n\n // Load buildProviderModule to auto-scan for @provideSingleton decorators\n this.appContainer.load(buildProviderModule());\n\n // Load all modules into application container\n // (webpiecesContainer is currently empty, reserved for future framework bindings)\n for (const module of modules) {\n this.appContainer.load(module);\n }\n }\n\n /**\n * Register routes from WebAppMeta.\n *\n * Creates an explicit RouteBuilderImpl instead of an anonymous object.\n * This improves:\n * - Traceability: Can Cmd+Click on addRoute to see implementation\n * - Debugging: Explicit class shows up in stack traces\n * - Understanding: Clear class name vs anonymous object\n */\n private registerRoutes(): void {\n const routeConfigs = this.meta.getRoutes();\n\n // Configure routes using the explicit RouteBuilder\n for (const routeConfig of routeConfigs) {\n routeConfig.configure(this.routeBuilder);\n }\n }\n\n /**\n * Global error handler middleware - catches ALL unhandled errors.\n * Returns HTML 500 error page for any errors that escape the filter chain.\n *\n * This is the outermost safety net - JsonFilter catches JSON API errors,\n * this catches everything else.\n */\n private async globalErrorHandler(\n req: Request,\n res: Response,\n next: NextFunction\n ): Promise<void> {\n console.log('🔴 [Layer 1: GlobalErrorHandler] Request START:', req.method, req.path);\n\n try {\n // await next() catches BOTH:\n // 1. Synchronous throws from next() itself\n // 2. Rejected promises from downstream async middleware\n await next();\n console.log('🔴 [Layer 1: GlobalErrorHandler] Request END (success):', req.method, req.path);\n } catch (err: unknown) {\n const error = toError(err);\n console.error('🔴 [Layer 1: GlobalErrorHandler] Caught unhandled error:', error);\n if (!res.headersSent) {\n // Return HTML error page (not JSON - JsonFilter handles JSON errors)\n res.status(500).send(`\n <!DOCTYPE html>\n <html>\n <head><title>Server Error</title></head>\n <body>\n <h1>You hit a server error</h1>\n <p>An unexpected error occurred while processing your request.</p>\n <pre>${error.message}</pre>\n </body>\n </html>\n `);\n }\n console.log('🔴 [Layer 1: GlobalErrorHandler] Request END (error):', req.method, req.path);\n }\n }\n\n /**\n * Logging middleware - logs request/response flow.\n * Demonstrates middleware execution order.\n */\n private logNextLayer(req: Request, res: Response, next: NextFunction): void {\n console.log('🟡 [Layer 2: LogNextLayer] Before next() -', req.method, req.path);\n next();\n console.log('🟡 [Layer 2: LogNextLayer] After next() -', req.method, req.path);\n }\n\n /**\n * Start the HTTP server with Express.\n * Assumes initialize() has already been called by WebpiecesServer.\n */\n start(port: number = 8080): void {\n if (!this.initialized) {\n throw new Error('Server not initialized. Call initialize() before start().');\n }\n\n this.port = port;\n\n // Create Express app\n this.app = express();\n\n // Parse JSON request bodies\n this.app.use(express.json());\n\n // Layer 1: Global Error Handler (OUTERMOST - runs FIRST)\n // Catches all unhandled errors and returns HTML 500 page\n this.app.use(this.globalErrorHandler.bind(this));\n\n // Layer 2: Request/Response Logging\n this.app.use(this.logNextLayer.bind(this));\n\n // Register routes (these become the innermost handlers)\n this.registerExpressRoutes();\n\n const routes = this.routeBuilder.getRoutes();\n\n // Start listening\n this.server = this.app.listen(this.port, () => {\n console.log(`[WebpiecesServer] Server listening on http://localhost:${this.port}`);\n console.log(`[WebpiecesServer] Registered ${routes.size} routes`);\n });\n }\n\n /**\n * Register all routes with Express.\n */\n private registerExpressRoutes(): void {\n if (!this.app) {\n throw new Error('Express app not initialized');\n }\n\n const routes = this.routeBuilder.getRoutes();\n const sortedFilters = this.routeBuilder.getSortedFilters();\n for (const [key, routeWithMeta] of routes.entries()) {\n this.setupRoute(key, routeWithMeta, sortedFilters);\n }\n }\n\n /**\n * Setup a single route with Express.\n * Finds matching filters, creates handler, and registers with Express.\n *\n * @param key - Route key (method:path)\n * @param routeWithMeta - The route handler paired with its definition\n * @param filtersWithMeta - All filters with their definitions\n */\n private setupRoute(key: string, routeWithMeta: RouteHandlerWithMeta, filtersWithMeta: Array<FilterWithMeta>): void {\n if (!this.app) {\n throw new Error('Express app not initialized');\n }\n\n const route = routeWithMeta.definition;\n const routeMeta = route.routeMeta;\n const method = routeMeta.httpMethod.toLowerCase();\n const path = routeMeta.path;\n\n console.log(`[WebpiecesServer] Registering route: ${method.toUpperCase()} ${path}`);\n\n // Find matching filters for this route - FilterMatcher returns Filter[] not FilterWithMeta[]\n // So we need to convert our FilterWithMeta[] to what FilterMatcher expects\n const filterDefinitions = filtersWithMeta.map(fwm => {\n // Set the filter instance on the definition for FilterMatcher\n const def = fwm.definition;\n def.filter = fwm.filter;\n return def;\n });\n\n const matchingFilters = FilterMatcher.findMatchingFilters(\n route.controllerFilepath,\n filterDefinitions\n );\n\n // Create service that wraps the controller execution\n const controllerService: Service<MethodMeta, WpResponse> = {\n invoke: async (meta: MethodMeta): Promise<WpResponse> => {\n // Invoke the controller method via route handler\n const result = await routeWithMeta.handler.execute(meta);\n const responseWrapper = new WpResponse(result);\n return responseWrapper;\n }\n };\n\n // Chain filters with the controller service (reverse order for correct execution)\n // IMPORTANT: MUST USE Filter.chain(filter) and Filter.chainService(svc);\n let filterChain = matchingFilters[matchingFilters.length - 1];\n for (let i = matchingFilters.length - 2; i >= 0; i--) {\n filterChain = filterChain.chain(matchingFilters[i]);\n }\n const svc = filterChain.chainService(controllerService);\n\n // Create Express route handler - delegates to filter chain\n const handler = async (req: Request, res: Response, next: NextFunction) => {\n // Create RouteRequest with Express Request/Response\n const routeRequest = new RouteRequest(req, res);\n\n // Create MethodMeta with route info and Express Request/Response\n const meta = new MethodMeta(\n routeMeta,\n routeRequest\n );\n\n // Response is written by JsonFilter - we just await completion\n await svc.invoke(meta);\n };\n\n // Register with Express\n switch (method) {\n case 'get':\n this.app.get(path, handler);\n break;\n case 'post':\n this.app.post(path, handler);\n break;\n case 'put':\n this.app.put(path, handler);\n break;\n case 'delete':\n this.app.delete(path, handler);\n break;\n case 'patch':\n this.app.patch(path, handler);\n break;\n default:\n console.warn(`[WebpiecesServer] Unknown HTTP method: ${method}`);\n }\n }\n\n /**\n * Stop the HTTP server.\n */\n stop(): void {\n if (this.server) {\n this.server.close(() => {\n console.log('[WebpiecesServer] Server stopped');\n });\n }\n }\n}\n"]}