@webpieces/http-server 0.3.234 → 0.3.235

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.
Files changed (36) hide show
  1. package/package.json +2 -2
  2. package/src/InProcessApiClientFactory.d.ts +34 -0
  3. package/src/InProcessApiClientFactory.js +77 -0
  4. package/src/InProcessApiClientFactory.js.map +1 -0
  5. package/src/WebpiecesRouteCreator.d.ts +90 -0
  6. package/src/WebpiecesRouteCreator.js +157 -0
  7. package/src/WebpiecesRouteCreator.js.map +1 -0
  8. package/src/WebpiecesServerImpl.d.ts +2 -10
  9. package/src/WebpiecesServerImpl.js +12 -84
  10. package/src/WebpiecesServerImpl.js.map +1 -1
  11. package/src/filters/ContextFilter.d.ts +3 -3
  12. package/src/filters/ContextFilter.js +9 -15
  13. package/src/filters/ContextFilter.js.map +1 -1
  14. package/src/filters/LogApiFilter.d.ts +2 -2
  15. package/src/filters/LogApiFilter.js +8 -10
  16. package/src/filters/LogApiFilter.js.map +1 -1
  17. package/src/filters/RecordingFilter.d.ts +30 -0
  18. package/src/filters/RecordingFilter.js +91 -0
  19. package/src/filters/RecordingFilter.js.map +1 -0
  20. package/src/headers/WebpiecesCoreHeaders.d.ts +4 -37
  21. package/src/headers/WebpiecesCoreHeaders.js +5 -48
  22. package/src/headers/WebpiecesCoreHeaders.js.map +1 -1
  23. package/src/index.d.ts +8 -1
  24. package/src/index.js +18 -1
  25. package/src/index.js.map +1 -1
  26. package/src/modules/WebpiecesModule.js +9 -0
  27. package/src/modules/WebpiecesModule.js.map +1 -1
  28. package/src/recorder/SpecGenerator.d.ts +16 -0
  29. package/src/recorder/SpecGenerator.js +70 -0
  30. package/src/recorder/SpecGenerator.js.map +1 -0
  31. package/src/recorder/TestCaseRecorderImpl.d.ts +33 -0
  32. package/src/recorder/TestCaseRecorderImpl.js +75 -0
  33. package/src/recorder/TestCaseRecorderImpl.js.map +1 -0
  34. package/src/recorder/recordable.d.ts +16 -0
  35. package/src/recorder/recordable.js +52 -0
  36. package/src/recorder/recordable.js.map +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/http-server",
3
- "version": "0.3.234",
3
+ "version": "0.3.235",
4
4
  "description": "WebPieces server with filter chain and dependency injection",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -22,7 +22,7 @@
22
22
  "access": "public"
23
23
  },
24
24
  "dependencies": {
25
- "@webpieces/http-routing": "0.3.234",
25
+ "@webpieces/http-routing": "0.3.235",
26
26
  "cors": "2.8.5"
27
27
  },
28
28
  "devDependencies": {
@@ -0,0 +1,34 @@
1
+ import { RouteBuilderImpl } from '@webpieces/http-routing';
2
+ /**
3
+ * InProcessApiClientFactory - Creates API client proxies that invoke routes
4
+ * in-process (full filter chain + controller) WITHOUT any HTTP overhead.
5
+ *
6
+ * Extracted from WebpiecesServerImpl so both the full server
7
+ * (WebpiecesServer.createApiClient) and the embeddable adapter
8
+ * (WebpiecesRouteCreator.createApiClient) share one code path.
9
+ *
10
+ * The client uses the ApiPrototype class to discover routes via decorators,
11
+ * then creates pre-configured invoker functions for each API method.
12
+ *
13
+ * IMPORTANT: This loops over the API methods (from decorators), NOT all routes.
14
+ * For each API method, it sets up the filter chain ONCE during proxy creation,
15
+ * so subsequent calls reuse the same filter chain (efficient!).
16
+ */
17
+ export declare class InProcessApiClientFactory {
18
+ private routeBuilder;
19
+ constructor(routeBuilder: RouteBuilderImpl);
20
+ /**
21
+ * Create an API client proxy for testing.
22
+ *
23
+ * @param apiPrototype - The API prototype class with routing decorators (can be abstract)
24
+ * @returns A proxy that implements the API interface
25
+ *
26
+ * Example:
27
+ * ```typescript
28
+ * const saveApi = factory.createApiClient<SaveApi>(SaveApi);
29
+ * const response = await saveApi.save(request);
30
+ * ```
31
+ */
32
+ createApiClient<T>(apiPrototype: abstract new (...args: any[]) => T): T;
33
+ private runMethod;
34
+ }
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InProcessApiClientFactory = void 0;
4
+ const http_routing_1 = require("@webpieces/http-routing");
5
+ const core_context_1 = require("@webpieces/core-context");
6
+ /**
7
+ * InProcessApiClientFactory - Creates API client proxies that invoke routes
8
+ * in-process (full filter chain + controller) WITHOUT any HTTP overhead.
9
+ *
10
+ * Extracted from WebpiecesServerImpl so both the full server
11
+ * (WebpiecesServer.createApiClient) and the embeddable adapter
12
+ * (WebpiecesRouteCreator.createApiClient) share one code path.
13
+ *
14
+ * The client uses the ApiPrototype class to discover routes via decorators,
15
+ * then creates pre-configured invoker functions for each API method.
16
+ *
17
+ * IMPORTANT: This loops over the API methods (from decorators), NOT all routes.
18
+ * For each API method, it sets up the filter chain ONCE during proxy creation,
19
+ * so subsequent calls reuse the same filter chain (efficient!).
20
+ */
21
+ class InProcessApiClientFactory {
22
+ routeBuilder;
23
+ constructor(routeBuilder) {
24
+ this.routeBuilder = routeBuilder;
25
+ }
26
+ /**
27
+ * Create an API client proxy for testing.
28
+ *
29
+ * @param apiPrototype - The API prototype class with routing decorators (can be abstract)
30
+ * @returns A proxy that implements the API interface
31
+ *
32
+ * Example:
33
+ * ```typescript
34
+ * const saveApi = factory.createApiClient<SaveApi>(SaveApi);
35
+ * const response = await saveApi.save(request);
36
+ * ```
37
+ */
38
+ // webpieces-disable no-any-unknown -- abstract constructor signature requires any[] args
39
+ createApiClient(apiPrototype) {
40
+ // Get endpoints from the API prototype using @ApiPath/@Endpoint decorators
41
+ const basePath = (0, http_routing_1.getApiPath)(apiPrototype) || '';
42
+ const endpoints = (0, http_routing_1.getEndpoints)(apiPrototype) || {};
43
+ // Create proxy object
44
+ // webpieces-disable no-any-unknown -- proxy holds methods of arbitrary API shapes
45
+ const proxy = {};
46
+ // Loop over API endpoints and create proxy functions
47
+ for (const [methodName, endpointPath] of Object.entries(endpoints)) {
48
+ const httpMethod = 'POST';
49
+ const path = basePath + endpointPath;
50
+ const authMeta = (0, http_routing_1.getAuthMeta)(apiPrototype, methodName);
51
+ const routeMeta = new http_routing_1.RouteMetadata(httpMethod, path, methodName, apiPrototype.name, authMeta);
52
+ // Create invoker service ONCE (sets up filter chain once, not on every call!)
53
+ const service = this.routeBuilder.createRouteInvoker(httpMethod, path);
54
+ // Proxy method creates MethodMeta and calls the pre-configured service
55
+ // webpieces-disable no-any-unknown -- request/response DTO types are erased at proxy level
56
+ proxy[methodName] = async (requestDto) => {
57
+ // Auto-activate a RequestContext if the test did not wrap the call itself
58
+ if (!core_context_1.RequestContext.isActive()) {
59
+ return core_context_1.RequestContext.run(async () => {
60
+ return await this.runMethod(routeMeta, requestDto, service);
61
+ });
62
+ }
63
+ return await this.runMethod(routeMeta, requestDto, service);
64
+ };
65
+ }
66
+ return proxy;
67
+ }
68
+ // webpieces-disable no-any-unknown -- DTO types are erased at the routing layer
69
+ async runMethod(routeMeta, requestDto, service) {
70
+ // Create MethodMeta without headers (in-process mode - no HTTP involved)
71
+ const meta = new http_routing_1.MethodMeta(routeMeta, undefined, requestDto);
72
+ const responseWrapper = await service.invoke(meta);
73
+ return responseWrapper.response;
74
+ }
75
+ }
76
+ exports.InProcessApiClientFactory = InProcessApiClientFactory;
77
+ //# sourceMappingURL=InProcessApiClientFactory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InProcessApiClientFactory.js","sourceRoot":"","sources":["../../../../../packages/http/http-server/src/InProcessApiClientFactory.ts"],"names":[],"mappings":";;;AAAA,0DAOiC;AACjC,0DAAyD;AAGzD;;;;;;;;;;;;;;GAcG;AACH,MAAa,yBAAyB;IACd;IAApB,YAAoB,YAA8B;QAA9B,iBAAY,GAAZ,YAAY,CAAkB;IAAG,CAAC;IAEtD;;;;;;;;;;;OAWG;IACH,yFAAyF;IACzF,eAAe,CAAI,YAAgD;QAC/D,2EAA2E;QAC3E,MAAM,QAAQ,GAAG,IAAA,yBAAU,EAAC,YAAY,CAAC,IAAI,EAAE,CAAC;QAChD,MAAM,SAAS,GAAG,IAAA,2BAAY,EAAC,YAAY,CAAC,IAAI,EAAE,CAAC;QAEnD,sBAAsB;QACtB,kFAAkF;QAClF,MAAM,KAAK,GAA4B,EAAE,CAAC;QAE1C,qDAAqD;QACrD,KAAK,MAAM,CAAC,UAAU,EAAE,YAAY,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;YACjE,MAAM,UAAU,GAAG,MAAM,CAAC;YAC1B,MAAM,IAAI,GAAG,QAAQ,GAAG,YAAY,CAAC;YAErC,MAAM,QAAQ,GAAG,IAAA,0BAAW,EAAC,YAAY,EAAE,UAAU,CAAC,CAAC;YACvD,MAAM,SAAS,GAAG,IAAI,4BAAa,CAAC,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,YAAY,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YAE/F,8EAA8E;YAC9E,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,kBAAkB,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;YAEvE,uEAAuE;YACvE,2FAA2F;YAC3F,KAAK,CAAC,UAAU,CAAC,GAAG,KAAK,EAAE,UAAmB,EAAoB,EAAE;gBAChE,0EAA0E;gBAC1E,IAAI,CAAC,6BAAc,CAAC,QAAQ,EAAE,EAAE,CAAC;oBAC7B,OAAO,6BAAc,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE;wBACjC,OAAO,MAAM,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;oBAChE,CAAC,CAAC,CAAC;gBACP,CAAC;gBACD,OAAO,MAAM,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;YAChE,CAAC,CAAC;QACN,CAAC;QAED,OAAO,KAAU,CAAC;IACtB,CAAC;IAED,gFAAgF;IACxE,KAAK,CAAC,SAAS,CAAC,SAAwB,EAAE,UAAmB,EAAE,OAAiD;QACpH,yEAAyE;QACzE,MAAM,IAAI,GAAG,IAAI,yBAAU,CAAC,SAAS,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;QAC9D,MAAM,eAAe,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACnD,OAAO,eAAe,CAAC,QAAQ,CAAC;IACpC,CAAC;CACJ;AA3DD,8DA2DC","sourcesContent":["import {\n getApiPath,\n getAuthMeta,\n getEndpoints,\n MethodMeta,\n RouteBuilderImpl,\n RouteMetadata,\n} from '@webpieces/http-routing';\nimport { RequestContext } from '@webpieces/core-context';\nimport { Service, WpResponse } from '@webpieces/http-filters';\n\n/**\n * InProcessApiClientFactory - Creates API client proxies that invoke routes\n * in-process (full filter chain + controller) WITHOUT any HTTP overhead.\n *\n * Extracted from WebpiecesServerImpl so both the full server\n * (WebpiecesServer.createApiClient) and the embeddable adapter\n * (WebpiecesRouteCreator.createApiClient) share one code path.\n *\n * The client uses the ApiPrototype class to discover routes via decorators,\n * then creates pre-configured invoker functions for each API method.\n *\n * IMPORTANT: This loops over the API methods (from decorators), NOT all routes.\n * For each API method, it sets up the filter chain ONCE during proxy creation,\n * so subsequent calls reuse the same filter chain (efficient!).\n */\nexport class InProcessApiClientFactory {\n constructor(private routeBuilder: RouteBuilderImpl) {}\n\n /**\n * Create an API client proxy for testing.\n *\n * @param apiPrototype - The API prototype class with routing decorators (can be abstract)\n * @returns A proxy that implements the API interface\n *\n * Example:\n * ```typescript\n * const saveApi = factory.createApiClient<SaveApi>(SaveApi);\n * const response = await saveApi.save(request);\n * ```\n */\n // webpieces-disable no-any-unknown -- abstract constructor signature requires any[] args\n createApiClient<T>(apiPrototype: abstract new (...args: any[]) => T): T {\n // Get endpoints from the API prototype using @ApiPath/@Endpoint decorators\n const basePath = getApiPath(apiPrototype) || '';\n const endpoints = getEndpoints(apiPrototype) || {};\n\n // Create proxy object\n // webpieces-disable no-any-unknown -- proxy holds methods of arbitrary API shapes\n const proxy: Record<string, unknown> = {};\n\n // Loop over API endpoints and create proxy functions\n for (const [methodName, endpointPath] of Object.entries(endpoints)) {\n const httpMethod = 'POST';\n const path = basePath + endpointPath;\n\n const authMeta = getAuthMeta(apiPrototype, methodName);\n const routeMeta = new RouteMetadata(httpMethod, path, methodName, apiPrototype.name, authMeta);\n\n // Create invoker service ONCE (sets up filter chain once, not on every call!)\n const service = this.routeBuilder.createRouteInvoker(httpMethod, path);\n\n // Proxy method creates MethodMeta and calls the pre-configured service\n // webpieces-disable no-any-unknown -- request/response DTO types are erased at proxy level\n proxy[methodName] = async (requestDto: unknown): Promise<unknown> => {\n // Auto-activate a RequestContext if the test did not wrap the call itself\n if (!RequestContext.isActive()) {\n return RequestContext.run(async () => {\n return await this.runMethod(routeMeta, requestDto, service);\n });\n }\n return await this.runMethod(routeMeta, requestDto, service);\n };\n }\n\n return proxy as T;\n }\n\n // webpieces-disable no-any-unknown -- DTO types are erased at the routing layer\n private async runMethod(routeMeta: RouteMetadata, requestDto: unknown, service: Service<MethodMeta, WpResponse<unknown>>): Promise<unknown> {\n // Create MethodMeta without headers (in-process mode - no HTTP involved)\n const meta = new MethodMeta(routeMeta, undefined, requestDto);\n const responseWrapper = await service.invoke(meta);\n return responseWrapper.response;\n }\n}\n"]}
@@ -0,0 +1,90 @@
1
+ import { Express } from 'express';
2
+ import { Container } from 'inversify';
3
+ import { ClassType, FilterDefinition, RouteBuilderImpl } from '@webpieces/http-routing';
4
+ import { WebpiecesMiddleware } from './WebpiecesMiddleware';
5
+ /**
6
+ * WebpiecesRouteCreator - Embeddable adapter that mounts the webpieces
7
+ * api -> filters -> controller pipeline onto ANY existing Express app.
8
+ *
9
+ * Legacy Express apps can adopt webpieces incrementally: existing routes and
10
+ * middleware keep working untouched; each wired webpieces route is fully
11
+ * self-contained (own body parsing, own RequestContext, own error->JSON mapping).
12
+ * This class never calls app.use() - it only registers per-route handlers.
13
+ *
14
+ * Usage:
15
+ * ```typescript
16
+ * const app = express(); // your existing legacy app
17
+ * const container = new Container();
18
+ * await container.load(buildProviderModule()); // picks up @provideSingleton classes
19
+ * await container.load(WebpiecesModule); // required if you use ContextFilter
20
+ *
21
+ * const creator = new WebpiecesRouteCreator(app, container);
22
+ * creator.wireFilters(
23
+ * new FilterDefinition(2000, ContextFilter, '*'),
24
+ * new FilterDefinition(1900, AuthFilter, 'src/controllers/admin/**'),
25
+ * );
26
+ * creator.wireApi(SaveApi, SaveController); // controller resolved from container
27
+ * creator.wireApi(PublicApi, PublicController);
28
+ * app.listen(8080);
29
+ * ```
30
+ *
31
+ * Notes:
32
+ * - ALL wireFilters() calls must come BEFORE the first wireApi() call. Filter
33
+ * chains are composed per-route at wireApi time, so late filters would be
34
+ * silently ignored - we throw instead.
35
+ * - Scoped filter glob patterns match the controller filepath from the
36
+ * @SourceFile decorator, falling back to the pattern `**\/{ClassName}.ts`.
37
+ * - Want webpieces' localhost CORS? Opt in yourself:
38
+ * `app.use(new WebpiecesMiddleware().corsForLocalhost())`.
39
+ *
40
+ * This same class is used internally by WebpiecesServerImpl.start(), so the
41
+ * full server and the embeddable adapter share one code path.
42
+ */
43
+ export declare class WebpiecesRouteCreator {
44
+ private app;
45
+ private routeBuilder;
46
+ private middleware;
47
+ private clientFactory;
48
+ /** Locks wireFilters() once the first wireApi() has composed a filter chain. */
49
+ private apisWired;
50
+ /**
51
+ * @param app - The Express app to mount routes on (yours - never taken over)
52
+ * @param container - Inversify container used to resolve controllers and filters
53
+ * @param routeBuilder - Internal: WebpiecesServerImpl passes its DI singleton; standalone users omit
54
+ * @param middleware - Internal: WebpiecesServerImpl passes its DI singleton; standalone users omit
55
+ */
56
+ constructor(app: Express, container: Container, routeBuilder?: RouteBuilderImpl, middleware?: WebpiecesMiddleware);
57
+ /**
58
+ * Register filters that wrap every matching route (glob pattern vs controller filepath).
59
+ * Must be called before the first wireApi() - filter chains are composed per-route.
60
+ */
61
+ wireFilters(...defs: FilterDefinition[]): void;
62
+ /**
63
+ * Wire an API prototype class (with @ApiPath/@Endpoint decorators) to its
64
+ * controller, mounting one Express route per endpoint with the full filter
65
+ * chain. The controller is resolved from the Inversify container.
66
+ */
67
+ wireApi<TApi, TController extends TApi>(apiPrototype: ClassType<TApi>, controllerClass: ClassType<TController>): void;
68
+ /**
69
+ * Mount every route currently registered on the RouteBuilder.
70
+ * Used by WebpiecesServerImpl.start() where routes were registered up front
71
+ * from WebAppMeta.getRoutes().
72
+ *
73
+ * @returns Number of routes mounted
74
+ */
75
+ mountRegisteredRoutes(): number;
76
+ /**
77
+ * Create an in-process API client (full filter chain + controller, no HTTP).
78
+ * Same testing story as WebpiecesServer.createApiClient().
79
+ */
80
+ createApiClient<T>(apiPrototype: abstract new (...args: any[]) => T): T;
81
+ /**
82
+ * Escape hatch for advanced wiring (e.g. addRoute with a hand-built RouteDefinition).
83
+ */
84
+ getRouteBuilder(): RouteBuilderImpl;
85
+ /**
86
+ * Compose the filter chain for one route and register it on the Express app.
87
+ */
88
+ private mountRoute;
89
+ private registerHandler;
90
+ }
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WebpiecesRouteCreator = void 0;
4
+ const http_routing_1 = require("@webpieces/http-routing");
5
+ const WebpiecesMiddleware_1 = require("./WebpiecesMiddleware");
6
+ const InProcessApiClientFactory_1 = require("./InProcessApiClientFactory");
7
+ /**
8
+ * WebpiecesRouteCreator - Embeddable adapter that mounts the webpieces
9
+ * api -> filters -> controller pipeline onto ANY existing Express app.
10
+ *
11
+ * Legacy Express apps can adopt webpieces incrementally: existing routes and
12
+ * middleware keep working untouched; each wired webpieces route is fully
13
+ * self-contained (own body parsing, own RequestContext, own error->JSON mapping).
14
+ * This class never calls app.use() - it only registers per-route handlers.
15
+ *
16
+ * Usage:
17
+ * ```typescript
18
+ * const app = express(); // your existing legacy app
19
+ * const container = new Container();
20
+ * await container.load(buildProviderModule()); // picks up @provideSingleton classes
21
+ * await container.load(WebpiecesModule); // required if you use ContextFilter
22
+ *
23
+ * const creator = new WebpiecesRouteCreator(app, container);
24
+ * creator.wireFilters(
25
+ * new FilterDefinition(2000, ContextFilter, '*'),
26
+ * new FilterDefinition(1900, AuthFilter, 'src/controllers/admin/**'),
27
+ * );
28
+ * creator.wireApi(SaveApi, SaveController); // controller resolved from container
29
+ * creator.wireApi(PublicApi, PublicController);
30
+ * app.listen(8080);
31
+ * ```
32
+ *
33
+ * Notes:
34
+ * - ALL wireFilters() calls must come BEFORE the first wireApi() call. Filter
35
+ * chains are composed per-route at wireApi time, so late filters would be
36
+ * silently ignored - we throw instead.
37
+ * - Scoped filter glob patterns match the controller filepath from the
38
+ * @SourceFile decorator, falling back to the pattern `**\/{ClassName}.ts`.
39
+ * - Want webpieces' localhost CORS? Opt in yourself:
40
+ * `app.use(new WebpiecesMiddleware().corsForLocalhost())`.
41
+ *
42
+ * This same class is used internally by WebpiecesServerImpl.start(), so the
43
+ * full server and the embeddable adapter share one code path.
44
+ */
45
+ class WebpiecesRouteCreator {
46
+ app;
47
+ routeBuilder;
48
+ middleware;
49
+ clientFactory;
50
+ /** Locks wireFilters() once the first wireApi() has composed a filter chain. */
51
+ apisWired = false;
52
+ /**
53
+ * @param app - The Express app to mount routes on (yours - never taken over)
54
+ * @param container - Inversify container used to resolve controllers and filters
55
+ * @param routeBuilder - Internal: WebpiecesServerImpl passes its DI singleton; standalone users omit
56
+ * @param middleware - Internal: WebpiecesServerImpl passes its DI singleton; standalone users omit
57
+ */
58
+ constructor(app, container, routeBuilder, middleware) {
59
+ this.app = app;
60
+ this.routeBuilder = routeBuilder ?? new http_routing_1.RouteBuilderImpl();
61
+ this.routeBuilder.setContainer(container);
62
+ this.middleware = middleware ?? new WebpiecesMiddleware_1.WebpiecesMiddleware();
63
+ this.clientFactory = new InProcessApiClientFactory_1.InProcessApiClientFactory(this.routeBuilder);
64
+ }
65
+ /**
66
+ * Register filters that wrap every matching route (glob pattern vs controller filepath).
67
+ * Must be called before the first wireApi() - filter chains are composed per-route.
68
+ */
69
+ wireFilters(...defs) {
70
+ if (this.apisWired) {
71
+ throw new Error('wireFilters() must be called before wireApi() - filter chains are composed per-route at wireApi time, so filters added later would never run.');
72
+ }
73
+ for (const def of defs) {
74
+ this.routeBuilder.addFilter(def);
75
+ }
76
+ }
77
+ /**
78
+ * Wire an API prototype class (with @ApiPath/@Endpoint decorators) to its
79
+ * controller, mounting one Express route per endpoint with the full filter
80
+ * chain. The controller is resolved from the Inversify container.
81
+ */
82
+ wireApi(apiPrototype, controllerClass) {
83
+ this.apisWired = true;
84
+ // Reuses all existing validation: @ApiPath present, controller extends
85
+ // api prototype, every endpoint implemented + has @Authentication.
86
+ const factory = new http_routing_1.ApiRoutingFactory(apiPrototype, controllerClass);
87
+ // Mount only the routes added by THIS call
88
+ const routesBefore = this.routeBuilder.getRoutes().length;
89
+ factory.configure(this.routeBuilder);
90
+ const routes = this.routeBuilder.getRoutes();
91
+ for (let i = routesBefore; i < routes.length; i++) {
92
+ this.mountRoute(routes[i]);
93
+ }
94
+ }
95
+ /**
96
+ * Mount every route currently registered on the RouteBuilder.
97
+ * Used by WebpiecesServerImpl.start() where routes were registered up front
98
+ * from WebAppMeta.getRoutes().
99
+ *
100
+ * @returns Number of routes mounted
101
+ */
102
+ mountRegisteredRoutes() {
103
+ const routes = this.routeBuilder.getRoutes();
104
+ for (const routeWithMeta of routes) {
105
+ this.mountRoute(routeWithMeta);
106
+ }
107
+ return routes.length;
108
+ }
109
+ /**
110
+ * Create an in-process API client (full filter chain + controller, no HTTP).
111
+ * Same testing story as WebpiecesServer.createApiClient().
112
+ */
113
+ // webpieces-disable no-any-unknown -- abstract constructor signature requires any[] args
114
+ createApiClient(apiPrototype) {
115
+ return this.clientFactory.createApiClient(apiPrototype);
116
+ }
117
+ /**
118
+ * Escape hatch for advanced wiring (e.g. addRoute with a hand-built RouteDefinition).
119
+ */
120
+ getRouteBuilder() {
121
+ return this.routeBuilder;
122
+ }
123
+ /**
124
+ * Compose the filter chain for one route and register it on the Express app.
125
+ */
126
+ mountRoute(routeWithMeta) {
127
+ const service = this.routeBuilder.createRouteHandler(routeWithMeta);
128
+ const routeMeta = routeWithMeta.definition.routeMeta;
129
+ // ExpressWrapper handles the full request/response cycle per route:
130
+ // RequestContext.run, header read, manual JSON body parse, error->ProtocolError
131
+ const wrapper = this.middleware.createExpressWrapper(service, routeMeta);
132
+ this.registerHandler(routeMeta.httpMethod, routeMeta.path, wrapper.execute.bind(wrapper));
133
+ }
134
+ registerHandler(httpMethod, path, expressHandler) {
135
+ switch (httpMethod.toLowerCase()) {
136
+ case 'get':
137
+ this.app.get(path, expressHandler);
138
+ break;
139
+ case 'post':
140
+ this.app.post(path, expressHandler);
141
+ break;
142
+ case 'put':
143
+ this.app.put(path, expressHandler);
144
+ break;
145
+ case 'delete':
146
+ this.app.delete(path, expressHandler);
147
+ break;
148
+ case 'patch':
149
+ this.app.patch(path, expressHandler);
150
+ break;
151
+ default:
152
+ console.warn(`[WebpiecesRouteCreator] Unknown HTTP method: ${httpMethod}`);
153
+ }
154
+ }
155
+ }
156
+ exports.WebpiecesRouteCreator = WebpiecesRouteCreator;
157
+ //# sourceMappingURL=WebpiecesRouteCreator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebpiecesRouteCreator.js","sourceRoot":"","sources":["../../../../../packages/http/http-server/src/WebpiecesRouteCreator.ts"],"names":[],"mappings":"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","sourcesContent":["import { Express } from 'express';\nimport { Container } from 'inversify';\nimport {\n ApiRoutingFactory,\n ClassType,\n ExpressRouteHandler,\n FilterDefinition,\n RouteBuilderImpl,\n RouteHandlerWithMeta,\n} from '@webpieces/http-routing';\nimport { WebpiecesMiddleware } from './WebpiecesMiddleware';\nimport { InProcessApiClientFactory } from './InProcessApiClientFactory';\n\n/**\n * WebpiecesRouteCreator - Embeddable adapter that mounts the webpieces\n * api -> filters -> controller pipeline onto ANY existing Express app.\n *\n * Legacy Express apps can adopt webpieces incrementally: existing routes and\n * middleware keep working untouched; each wired webpieces route is fully\n * self-contained (own body parsing, own RequestContext, own error->JSON mapping).\n * This class never calls app.use() - it only registers per-route handlers.\n *\n * Usage:\n * ```typescript\n * const app = express(); // your existing legacy app\n * const container = new Container();\n * await container.load(buildProviderModule()); // picks up @provideSingleton classes\n * await container.load(WebpiecesModule); // required if you use ContextFilter\n *\n * const creator = new WebpiecesRouteCreator(app, container);\n * creator.wireFilters(\n * new FilterDefinition(2000, ContextFilter, '*'),\n * new FilterDefinition(1900, AuthFilter, 'src/controllers/admin/**'),\n * );\n * creator.wireApi(SaveApi, SaveController); // controller resolved from container\n * creator.wireApi(PublicApi, PublicController);\n * app.listen(8080);\n * ```\n *\n * Notes:\n * - ALL wireFilters() calls must come BEFORE the first wireApi() call. Filter\n * chains are composed per-route at wireApi time, so late filters would be\n * silently ignored - we throw instead.\n * - Scoped filter glob patterns match the controller filepath from the\n * @SourceFile decorator, falling back to the pattern `**\\/{ClassName}.ts`.\n * - Want webpieces' localhost CORS? Opt in yourself:\n * `app.use(new WebpiecesMiddleware().corsForLocalhost())`.\n *\n * This same class is used internally by WebpiecesServerImpl.start(), so the\n * full server and the embeddable adapter share one code path.\n */\nexport class WebpiecesRouteCreator {\n private routeBuilder: RouteBuilderImpl;\n private middleware: WebpiecesMiddleware;\n private clientFactory: InProcessApiClientFactory;\n\n /** Locks wireFilters() once the first wireApi() has composed a filter chain. */\n private apisWired = false;\n\n /**\n * @param app - The Express app to mount routes on (yours - never taken over)\n * @param container - Inversify container used to resolve controllers and filters\n * @param routeBuilder - Internal: WebpiecesServerImpl passes its DI singleton; standalone users omit\n * @param middleware - Internal: WebpiecesServerImpl passes its DI singleton; standalone users omit\n */\n constructor(\n private app: Express,\n container: Container,\n routeBuilder?: RouteBuilderImpl,\n middleware?: WebpiecesMiddleware,\n ) {\n this.routeBuilder = routeBuilder ?? new RouteBuilderImpl();\n this.routeBuilder.setContainer(container);\n this.middleware = middleware ?? new WebpiecesMiddleware();\n this.clientFactory = new InProcessApiClientFactory(this.routeBuilder);\n }\n\n /**\n * Register filters that wrap every matching route (glob pattern vs controller filepath).\n * Must be called before the first wireApi() - filter chains are composed per-route.\n */\n wireFilters(...defs: FilterDefinition[]): void {\n if (this.apisWired) {\n throw new Error(\n 'wireFilters() must be called before wireApi() - filter chains are composed per-route at wireApi time, so filters added later would never run.',\n );\n }\n for (const def of defs) {\n this.routeBuilder.addFilter(def);\n }\n }\n\n /**\n * Wire an API prototype class (with @ApiPath/@Endpoint decorators) to its\n * controller, mounting one Express route per endpoint with the full filter\n * chain. The controller is resolved from the Inversify container.\n */\n wireApi<TApi, TController extends TApi>(\n apiPrototype: ClassType<TApi>,\n controllerClass: ClassType<TController>,\n ): void {\n this.apisWired = true;\n\n // Reuses all existing validation: @ApiPath present, controller extends\n // api prototype, every endpoint implemented + has @Authentication.\n const factory = new ApiRoutingFactory(apiPrototype, controllerClass);\n\n // Mount only the routes added by THIS call\n const routesBefore = this.routeBuilder.getRoutes().length;\n factory.configure(this.routeBuilder);\n const routes = this.routeBuilder.getRoutes();\n\n for (let i = routesBefore; i < routes.length; i++) {\n this.mountRoute(routes[i]);\n }\n }\n\n /**\n * Mount every route currently registered on the RouteBuilder.\n * Used by WebpiecesServerImpl.start() where routes were registered up front\n * from WebAppMeta.getRoutes().\n *\n * @returns Number of routes mounted\n */\n mountRegisteredRoutes(): number {\n const routes = this.routeBuilder.getRoutes();\n for (const routeWithMeta of routes) {\n this.mountRoute(routeWithMeta);\n }\n return routes.length;\n }\n\n /**\n * Create an in-process API client (full filter chain + controller, no HTTP).\n * Same testing story as WebpiecesServer.createApiClient().\n */\n // webpieces-disable no-any-unknown -- abstract constructor signature requires any[] args\n createApiClient<T>(apiPrototype: abstract new (...args: any[]) => T): T {\n return this.clientFactory.createApiClient(apiPrototype);\n }\n\n /**\n * Escape hatch for advanced wiring (e.g. addRoute with a hand-built RouteDefinition).\n */\n getRouteBuilder(): RouteBuilderImpl {\n return this.routeBuilder;\n }\n\n /**\n * Compose the filter chain for one route and register it on the Express app.\n */\n private mountRoute(routeWithMeta: RouteHandlerWithMeta): void {\n const service = this.routeBuilder.createRouteHandler(routeWithMeta);\n const routeMeta = routeWithMeta.definition.routeMeta;\n\n // ExpressWrapper handles the full request/response cycle per route:\n // RequestContext.run, header read, manual JSON body parse, error->ProtocolError\n const wrapper = this.middleware.createExpressWrapper(service, routeMeta);\n\n this.registerHandler(\n routeMeta.httpMethod,\n routeMeta.path,\n wrapper.execute.bind(wrapper),\n );\n }\n\n private registerHandler(httpMethod: string, path: string, expressHandler: ExpressRouteHandler): void {\n switch (httpMethod.toLowerCase()) {\n case 'get':\n this.app.get(path, expressHandler);\n break;\n case 'post':\n this.app.post(path, expressHandler);\n break;\n case 'put':\n this.app.put(path, expressHandler);\n break;\n case 'delete':\n this.app.delete(path, expressHandler);\n break;\n case 'patch':\n this.app.patch(path, expressHandler);\n break;\n default:\n console.warn(`[WebpiecesRouteCreator] Unknown HTTP method: ${httpMethod}`);\n }\n }\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { Container, ContainerModule } from 'inversify';
2
- import { ExpressRouteHandler, RouteBuilderImpl, WebAppMeta } from '@webpieces/http-routing';
2
+ import { RouteBuilderImpl, WebAppMeta } from '@webpieces/http-routing';
3
3
  import { WebpiecesServer } from './WebpiecesServer';
4
4
  import { WebpiecesMiddleware } from './WebpiecesMiddleware';
5
5
  /**
@@ -84,14 +84,6 @@ export declare class WebpiecesServerImpl implements WebpiecesServer {
84
84
  * @returns Promise that resolves when server is ready
85
85
  */
86
86
  start(port?: number, testMode?: boolean): Promise<void>;
87
- /**
88
- * Register Express routes - the SINGLE loop over routes.
89
- * For each route: createHandler (sets up filter chain) → wrapExpress → registerHandler.
90
- *
91
- * @returns Number of routes registered
92
- */
93
- private registerExpressRoutes;
94
- registerHandler(httpMethod: string, path: string, expressHandler: ExpressRouteHandler): void;
95
87
  /**
96
88
  * Stop the HTTP server.
97
89
  * Returns a Promise that resolves when the server is stopped,
@@ -131,5 +123,5 @@ export declare class WebpiecesServerImpl implements WebpiecesServer {
131
123
  * ```
132
124
  */
133
125
  createApiClient<T>(apiPrototype: abstract new (...args: any[]) => T): T;
134
- private runMethod;
126
+ private clientFactory?;
135
127
  }
@@ -7,7 +7,8 @@ const inversify_1 = require("inversify");
7
7
  const binding_decorators_1 = require("@inversifyjs/binding-decorators");
8
8
  const http_routing_1 = require("@webpieces/http-routing");
9
9
  const WebpiecesMiddleware_1 = require("./WebpiecesMiddleware");
10
- const core_context_1 = require("@webpieces/core-context");
10
+ const WebpiecesRouteCreator_1 = require("./WebpiecesRouteCreator");
11
+ const InProcessApiClientFactory_1 = require("./InProcessApiClientFactory");
11
12
  /**
12
13
  * WebpiecesServerImpl - Internal server implementation.
13
14
  *
@@ -145,8 +146,10 @@ let WebpiecesServerImpl = class WebpiecesServerImpl {
145
146
  this.app.use(this.middleware.corsForLocalhost());
146
147
  // Layer 3: Request/Response Logging
147
148
  this.app.use(this.middleware.logNextLayer.bind(this.middleware));
148
- // Register routes
149
- const routeCount = this.registerExpressRoutes();
149
+ // Register routes via the shared adapter (same code path as the
150
+ // embeddable WebpiecesRouteCreator used by legacy Express apps)
151
+ const routeCreator = new WebpiecesRouteCreator_1.WebpiecesRouteCreator(this.app, this.appContainer, this.routeBuilder, this.middleware);
152
+ const routeCount = routeCreator.mountRegisteredRoutes();
150
153
  // Start listening - wrap in Promise
151
154
  const promise = new Promise((resolve, reject) => {
152
155
  this.server = this.app.listen(this.port, (error) => {
@@ -162,52 +165,6 @@ let WebpiecesServerImpl = class WebpiecesServerImpl {
162
165
  });
163
166
  await promise;
164
167
  }
165
- /**
166
- * Register Express routes - the SINGLE loop over routes.
167
- * For each route: createHandler (sets up filter chain) → wrapExpress → registerHandler.
168
- *
169
- * @returns Number of routes registered
170
- */
171
- registerExpressRoutes() {
172
- if (!this.app) {
173
- throw new Error('Express app not initialized');
174
- }
175
- const routes = this.routeBuilder.getRoutes();
176
- let count = 0;
177
- for (const routeWithMeta of routes) {
178
- const service = this.routeBuilder.createRouteHandler(routeWithMeta);
179
- const routeMeta = routeWithMeta.definition.routeMeta;
180
- // Create ExpressWrapper directly (handles full request/response cycle)
181
- const wrapper = this.middleware.createExpressWrapper(service, routeMeta);
182
- this.registerHandler(routeMeta.httpMethod, routeMeta.path, wrapper.execute.bind(wrapper));
183
- count++;
184
- }
185
- return count;
186
- }
187
- registerHandler(httpMethod, path, expressHandler) {
188
- if (!this.app) {
189
- throw new Error('Express app not initialized');
190
- }
191
- switch (httpMethod.toLowerCase()) {
192
- case 'get':
193
- this.app.get(path, expressHandler);
194
- break;
195
- case 'post':
196
- this.app.post(path, expressHandler);
197
- break;
198
- case 'put':
199
- this.app.put(path, expressHandler);
200
- break;
201
- case 'delete':
202
- this.app.delete(path, expressHandler);
203
- break;
204
- case 'patch':
205
- this.app.patch(path, expressHandler);
206
- break;
207
- default:
208
- console.warn(`[WebpiecesServer] Unknown HTTP method: ${httpMethod}`);
209
- }
210
- }
211
168
  /**
212
169
  * Stop the HTTP server.
213
170
  * Returns a Promise that resolves when the server is stopped,
@@ -263,47 +220,18 @@ let WebpiecesServerImpl = class WebpiecesServerImpl {
263
220
  * const response = await saveApi.save(request);
264
221
  * ```
265
222
  */
223
+ // webpieces-disable no-any-unknown -- abstract constructor signature requires any[] args
266
224
  createApiClient(apiPrototype) {
267
225
  if (!this.initialized) {
268
226
  throw new Error('Server not initialized. Call initialize() before createApiClient().');
269
227
  }
270
- // Get endpoints from the API prototype using @ApiPath/@Endpoint decorators
271
- const basePath = (0, http_routing_1.getApiPath)(apiPrototype) || '';
272
- const endpoints = (0, http_routing_1.getEndpoints)(apiPrototype) || {};
273
- // Create proxy object
274
- const proxy = {};
275
- // Loop over API endpoints and create proxy functions
276
- for (const [methodName, endpointPath] of Object.entries(endpoints)) {
277
- const httpMethod = 'POST';
278
- const path = basePath + endpointPath;
279
- const authMeta = (0, http_routing_1.getAuthMeta)(apiPrototype, methodName);
280
- const routeMeta = new http_routing_1.RouteMetadata(httpMethod, path, methodName, apiPrototype.name, authMeta);
281
- // Create invoker service ONCE (sets up filter chain once, not on every call!)
282
- const service = this.routeBuilder.createRouteInvoker(httpMethod, path);
283
- // Proxy method creates MethodMeta and calls the pre-configured service
284
- // IMPORTANT: Tests MUST wrap calls in RequestContext.run() themselves
285
- // This forces explicit context setup in tests, matching production behavior
286
- proxy[methodName] = async (requestDto) => {
287
- // Verify we're inside an active RequestContext
288
- // This helps test authors know they need to wrap their test in RequestContext.run()
289
- if (!core_context_1.RequestContext.isActive()) {
290
- //Many devs may not activate headers
291
- return core_context_1.RequestContext.run(async () => {
292
- return await this.runMethod(routeMeta, requestDto, service);
293
- });
294
- }
295
- return await this.runMethod(routeMeta, requestDto, service);
296
- };
228
+ // Delegates to the shared factory (same code path as WebpiecesRouteCreator.createApiClient)
229
+ if (!this.clientFactory) {
230
+ this.clientFactory = new InProcessApiClientFactory_1.InProcessApiClientFactory(this.routeBuilder);
297
231
  }
298
- return proxy;
299
- }
300
- async runMethod(routeMeta, requestDto, service) {
301
- // Create MethodMeta without headers (test mode - no HTTP involved)
302
- // requestHeaders is optional, so we can omit it
303
- const meta = new http_routing_1.MethodMeta(routeMeta, undefined, requestDto);
304
- const responseWrapper = await service.invoke(meta);
305
- return responseWrapper.response;
232
+ return this.clientFactory.createApiClient(apiPrototype);
306
233
  }
234
+ clientFactory;
307
235
  };
308
236
  exports.WebpiecesServerImpl = WebpiecesServerImpl;
309
237
  exports.WebpiecesServerImpl = WebpiecesServerImpl = tslib_1.__decorate([