@hazeljs/gateway 0.2.0-beta.41

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 (72) hide show
  1. package/LICENSE +192 -0
  2. package/README.md +255 -0
  3. package/dist/__tests__/canary-engine.test.d.ts +2 -0
  4. package/dist/__tests__/canary-engine.test.d.ts.map +1 -0
  5. package/dist/__tests__/canary-engine.test.js +133 -0
  6. package/dist/__tests__/decorators.test.d.ts +2 -0
  7. package/dist/__tests__/decorators.test.d.ts.map +1 -0
  8. package/dist/__tests__/decorators.test.js +174 -0
  9. package/dist/__tests__/from-config.test.d.ts +2 -0
  10. package/dist/__tests__/from-config.test.d.ts.map +1 -0
  11. package/dist/__tests__/from-config.test.js +67 -0
  12. package/dist/__tests__/gateway-metrics.test.d.ts +2 -0
  13. package/dist/__tests__/gateway-metrics.test.d.ts.map +1 -0
  14. package/dist/__tests__/gateway-metrics.test.js +82 -0
  15. package/dist/__tests__/gateway-module.test.d.ts +2 -0
  16. package/dist/__tests__/gateway-module.test.d.ts.map +1 -0
  17. package/dist/__tests__/gateway-module.test.js +91 -0
  18. package/dist/__tests__/gateway.test.d.ts +2 -0
  19. package/dist/__tests__/gateway.test.d.ts.map +1 -0
  20. package/dist/__tests__/gateway.test.js +257 -0
  21. package/dist/__tests__/hazel-integration.test.d.ts +2 -0
  22. package/dist/__tests__/hazel-integration.test.d.ts.map +1 -0
  23. package/dist/__tests__/hazel-integration.test.js +92 -0
  24. package/dist/__tests__/route-matcher.test.d.ts +2 -0
  25. package/dist/__tests__/route-matcher.test.d.ts.map +1 -0
  26. package/dist/__tests__/route-matcher.test.js +67 -0
  27. package/dist/__tests__/service-proxy.test.d.ts +2 -0
  28. package/dist/__tests__/service-proxy.test.d.ts.map +1 -0
  29. package/dist/__tests__/service-proxy.test.js +110 -0
  30. package/dist/__tests__/traffic-mirror.test.d.ts +2 -0
  31. package/dist/__tests__/traffic-mirror.test.d.ts.map +1 -0
  32. package/dist/__tests__/traffic-mirror.test.js +70 -0
  33. package/dist/__tests__/version-router.test.d.ts +2 -0
  34. package/dist/__tests__/version-router.test.d.ts.map +1 -0
  35. package/dist/__tests__/version-router.test.js +136 -0
  36. package/dist/canary/canary-engine.d.ts +107 -0
  37. package/dist/canary/canary-engine.d.ts.map +1 -0
  38. package/dist/canary/canary-engine.js +334 -0
  39. package/dist/decorators/index.d.ts +74 -0
  40. package/dist/decorators/index.d.ts.map +1 -0
  41. package/dist/decorators/index.js +170 -0
  42. package/dist/gateway.d.ts +67 -0
  43. package/dist/gateway.d.ts.map +1 -0
  44. package/dist/gateway.js +310 -0
  45. package/dist/gateway.module.d.ts +67 -0
  46. package/dist/gateway.module.d.ts.map +1 -0
  47. package/dist/gateway.module.js +82 -0
  48. package/dist/hazel-integration.d.ts +24 -0
  49. package/dist/hazel-integration.d.ts.map +1 -0
  50. package/dist/hazel-integration.js +70 -0
  51. package/dist/index.d.ts +20 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +62 -0
  54. package/dist/metrics/gateway-metrics.d.ts +64 -0
  55. package/dist/metrics/gateway-metrics.d.ts.map +1 -0
  56. package/dist/metrics/gateway-metrics.js +159 -0
  57. package/dist/middleware/traffic-mirror.d.ts +19 -0
  58. package/dist/middleware/traffic-mirror.d.ts.map +1 -0
  59. package/dist/middleware/traffic-mirror.js +60 -0
  60. package/dist/proxy/service-proxy.d.ts +68 -0
  61. package/dist/proxy/service-proxy.d.ts.map +1 -0
  62. package/dist/proxy/service-proxy.js +211 -0
  63. package/dist/routing/route-matcher.d.ts +31 -0
  64. package/dist/routing/route-matcher.d.ts.map +1 -0
  65. package/dist/routing/route-matcher.js +112 -0
  66. package/dist/routing/version-router.d.ts +36 -0
  67. package/dist/routing/version-router.d.ts.map +1 -0
  68. package/dist/routing/version-router.js +136 -0
  69. package/dist/types/index.d.ts +217 -0
  70. package/dist/types/index.d.ts.map +1 -0
  71. package/dist/types/index.js +17 -0
  72. package/package.json +74 -0
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ /**
3
+ * Gateway Module
4
+ * Integrates @hazeljs/gateway with @hazeljs/config for config-driven routing.
5
+ *
6
+ * Usage:
7
+ * 1. Define a gateway config loader (gateway.config.ts) that reads env vars
8
+ * 2. Register it with ConfigModule.forRoot({ load: [gatewayConfig] })
9
+ * 3. Use GatewayModule.forRoot() to create the gateway from config
10
+ *
11
+ * Example:
12
+ * GatewayModule.forRoot({ configKey: 'gateway' })
13
+ * // reads ConfigService.get('gateway') -> GatewayFullConfig
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.GatewayModule = void 0;
17
+ /**
18
+ * GatewayModule follows the same forRoot() pattern as ConfigModule.
19
+ *
20
+ * It stores the options statically so the gateway can be created later
21
+ * when the app boots and ConfigService is available.
22
+ */
23
+ class GatewayModule {
24
+ /**
25
+ * Register the gateway module with configuration options.
26
+ *
27
+ * @example
28
+ * // Config-driven (reads from ConfigService):
29
+ * GatewayModule.forRoot({ configKey: 'gateway' })
30
+ *
31
+ * // Direct config (no ConfigService needed):
32
+ * GatewayModule.forRoot({ config: { discovery: {...}, routes: [...] } })
33
+ */
34
+ static forRoot(options = {}) {
35
+ GatewayModule.options = {
36
+ configKey: 'gateway',
37
+ ...options,
38
+ };
39
+ return { module: GatewayModule };
40
+ }
41
+ /**
42
+ * Get the registered options.
43
+ */
44
+ static getOptions() {
45
+ return GatewayModule.options;
46
+ }
47
+ /**
48
+ * Resolve the gateway configuration.
49
+ *
50
+ * If `config` was provided directly, returns it.
51
+ * Otherwise, reads from the provided ConfigService using the configKey.
52
+ *
53
+ * @param configService - Optional ConfigService instance. Required if
54
+ * no direct `config` was provided in forRoot().
55
+ */
56
+ static resolveConfig(configService) {
57
+ const opts = GatewayModule.options;
58
+ // Direct config takes precedence
59
+ if (opts.config) {
60
+ return opts.config;
61
+ }
62
+ // Otherwise read from ConfigService
63
+ if (!configService) {
64
+ throw new Error('GatewayModule: No config provided and no ConfigService available. ' +
65
+ 'Either pass config directly via GatewayModule.forRoot({ config: {...} }) ' +
66
+ 'or provide a ConfigService instance.');
67
+ }
68
+ const key = opts.configKey || 'gateway';
69
+ const config = configService.get(key);
70
+ if (!config) {
71
+ throw new Error(`GatewayModule: No configuration found at key "${key}". ` +
72
+ `Make sure your config loader returns a "${key}" key with routes.`);
73
+ }
74
+ if (!config.routes || !Array.isArray(config.routes)) {
75
+ throw new Error(`GatewayModule: Configuration at key "${key}" is missing a "routes" array. ` +
76
+ 'Each route needs at least { path, serviceName }.');
77
+ }
78
+ return config;
79
+ }
80
+ }
81
+ exports.GatewayModule = GatewayModule;
82
+ GatewayModule.options = {};
@@ -0,0 +1,24 @@
1
+ /**
2
+ * HazelJS Core Integration
3
+ * Creates a proxy handler for use with HazelApp.addProxyHandler()
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * import { HazelApp } from '@hazeljs/core';
8
+ * import { GatewayServer, createGatewayHandler } from '@hazeljs/gateway';
9
+ *
10
+ * const gateway = GatewayServer.fromConfig(config);
11
+ * const app = new HazelApp(AppModule);
12
+ * app.addProxyHandler('/api', createGatewayHandler(gateway));
13
+ * app.listen(3000);
14
+ * ```
15
+ */
16
+ import { IncomingMessage, ServerResponse } from 'http';
17
+ import type { RequestContext } from '@hazeljs/core';
18
+ import type { GatewayServer } from './gateway';
19
+ /**
20
+ * Create a proxy handler that bridges Node.js HTTP to GatewayServer.handleRequest().
21
+ * Use with HazelApp.addProxyHandler(pathPrefix, createGatewayHandler(gateway)).
22
+ */
23
+ export declare function createGatewayHandler(gateway: GatewayServer): (req: IncomingMessage, res: ServerResponse, context: RequestContext) => Promise<boolean>;
24
+ //# sourceMappingURL=hazel-integration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hazel-integration.d.ts","sourceRoot":"","sources":["../src/hazel-integration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAG/C;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,aAAa,IAEvD,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,SAAS,cAAc,KACtB,OAAO,CAAC,OAAO,CAAC,CAMpB"}
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ /**
3
+ * HazelJS Core Integration
4
+ * Creates a proxy handler for use with HazelApp.addProxyHandler()
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import { HazelApp } from '@hazeljs/core';
9
+ * import { GatewayServer, createGatewayHandler } from '@hazeljs/gateway';
10
+ *
11
+ * const gateway = GatewayServer.fromConfig(config);
12
+ * const app = new HazelApp(AppModule);
13
+ * app.addProxyHandler('/api', createGatewayHandler(gateway));
14
+ * app.listen(3000);
15
+ * ```
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.createGatewayHandler = createGatewayHandler;
19
+ /**
20
+ * Create a proxy handler that bridges Node.js HTTP to GatewayServer.handleRequest().
21
+ * Use with HazelApp.addProxyHandler(pathPrefix, createGatewayHandler(gateway)).
22
+ */
23
+ function createGatewayHandler(gateway) {
24
+ return async function gatewayHandler(req, res, context) {
25
+ const proxyRequest = toProxyRequest(req, context);
26
+ const proxyResponse = await gateway.handleRequest(proxyRequest);
27
+ writeProxyResponse(res, proxyResponse);
28
+ return true;
29
+ };
30
+ }
31
+ function toProxyRequest(req, context) {
32
+ const url = req.url || '/';
33
+ const pathname = url.split('?')[0];
34
+ const headers = {};
35
+ if (req.headers) {
36
+ for (const [key, value] of Object.entries(req.headers)) {
37
+ if (value !== undefined) {
38
+ headers[key] = Array.isArray(value) ? value.join(', ') : String(value);
39
+ }
40
+ }
41
+ }
42
+ return {
43
+ method: context.method || req.method || 'GET',
44
+ path: pathname,
45
+ headers,
46
+ body: context.body,
47
+ query: context.query ?? {},
48
+ };
49
+ }
50
+ function writeProxyResponse(res, proxyRes) {
51
+ if (res.writableEnded)
52
+ return;
53
+ const headers = {};
54
+ for (const [key, value] of Object.entries(proxyRes.headers)) {
55
+ if (value !== undefined) {
56
+ headers[key] = Array.isArray(value) ? value.join(', ') : String(value);
57
+ }
58
+ }
59
+ if (!headers['content-type']) {
60
+ headers['content-type'] = 'application/json';
61
+ }
62
+ res.writeHead(proxyRes.status, headers);
63
+ if (proxyRes.body !== undefined) {
64
+ const body = typeof proxyRes.body === 'string' ? proxyRes.body : JSON.stringify(proxyRes.body);
65
+ res.end(body);
66
+ }
67
+ else {
68
+ res.end();
69
+ }
70
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @hazeljs/gateway
3
+ * Intelligent API Gateway for HazelJS
4
+ *
5
+ * Provides version-based routing, canary deployments, circuit breaking,
6
+ * traffic management, and automatic rollback — all via decorators or
7
+ * programmatic API.
8
+ */
9
+ export { GatewayConfig, GatewayFullConfig, GatewayResilienceDefaults, GatewayMetricsConfig, GatewayMiddlewareConfig, CorsConfig, RouteConfig, ServiceRouteConfig, VersionResolutionStrategy, VersionRouteConfig, VersionRouteEntry, CanaryConfig, CanaryVersionConfig, CanaryPromotionConfig, CanaryMetrics, CanaryVersionMetrics, CanaryDecision, CanaryState, TrafficPolicyConfig, TrafficMirrorConfig, TrafficTransformConfig, ProxyRequest, ProxyResponse, GatewayRouteDefinition, GatewayEvent, GatewayEventType, } from './types';
10
+ export { GatewayServer } from './gateway';
11
+ export { GatewayModule, GatewayModuleOptions } from './gateway.module';
12
+ export { ServiceProxy, ServiceProxyConfig } from './proxy/service-proxy';
13
+ export { VersionRouter, VersionResolution } from './routing/version-router';
14
+ export { matchRoute, sortRoutesBySpecificity, RouteMatch } from './routing/route-matcher';
15
+ export { CanaryEngine, CanaryStatus, parseInterval } from './canary/canary-engine';
16
+ export { GatewayMetrics, GatewayMetricsSnapshot, RouteMetricsSnapshot, } from './metrics/gateway-metrics';
17
+ export { TrafficMirror } from './middleware/traffic-mirror';
18
+ export { createGatewayHandler } from './hazel-integration';
19
+ export { Gateway, Route, ServiceRoute, VersionRoute, Canary, TrafficPolicy, GatewayCircuitBreaker, GatewayRateLimit, getGatewayConfig, getRouteConfig, getServiceRouteConfig, getVersionRouteConfig, getCanaryConfig, getTrafficPolicyConfig, getCircuitBreakerConfig, getRateLimitConfig, collectRouteDefinitions, } from './decorators';
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,yBAAyB,EACzB,oBAAoB,EACpB,uBAAuB,EACvB,UAAU,EACV,WAAW,EACX,kBAAkB,EAClB,yBAAyB,EACzB,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EACZ,mBAAmB,EACnB,qBAAqB,EACrB,aAAa,EACb,oBAAoB,EACpB,cAAc,EACd,WAAW,EACX,mBAAmB,EACnB,mBAAmB,EACnB,sBAAsB,EACtB,YAAY,EACZ,aAAa,EACb,sBAAsB,EACtB,YAAY,EACZ,gBAAgB,GACjB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAG1C,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAGvE,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAGzE,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC5E,OAAO,EAAE,UAAU,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAG1F,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAGnF,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAG5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAG3D,OAAO,EACL,OAAO,EACP,KAAK,EACL,YAAY,EACZ,YAAY,EACZ,MAAM,EACN,aAAa,EACb,qBAAqB,EACrB,gBAAgB,EAEhB,gBAAgB,EAChB,cAAc,EACd,qBAAqB,EACrB,qBAAqB,EACrB,eAAe,EACf,sBAAsB,EACtB,uBAAuB,EACvB,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ /**
3
+ * @hazeljs/gateway
4
+ * Intelligent API Gateway for HazelJS
5
+ *
6
+ * Provides version-based routing, canary deployments, circuit breaking,
7
+ * traffic management, and automatic rollback — all via decorators or
8
+ * programmatic API.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.collectRouteDefinitions = exports.getRateLimitConfig = exports.getCircuitBreakerConfig = exports.getTrafficPolicyConfig = exports.getCanaryConfig = exports.getVersionRouteConfig = exports.getServiceRouteConfig = exports.getRouteConfig = exports.getGatewayConfig = exports.GatewayRateLimit = exports.GatewayCircuitBreaker = exports.TrafficPolicy = exports.Canary = exports.VersionRoute = exports.ServiceRoute = exports.Route = exports.Gateway = exports.createGatewayHandler = exports.TrafficMirror = exports.GatewayMetrics = exports.parseInterval = exports.CanaryEngine = exports.sortRoutesBySpecificity = exports.matchRoute = exports.VersionRouter = exports.ServiceProxy = exports.GatewayModule = exports.GatewayServer = exports.CanaryState = void 0;
12
+ // Types
13
+ var types_1 = require("./types");
14
+ Object.defineProperty(exports, "CanaryState", { enumerable: true, get: function () { return types_1.CanaryState; } });
15
+ // Gateway Server
16
+ var gateway_1 = require("./gateway");
17
+ Object.defineProperty(exports, "GatewayServer", { enumerable: true, get: function () { return gateway_1.GatewayServer; } });
18
+ // Gateway Module (config integration)
19
+ var gateway_module_1 = require("./gateway.module");
20
+ Object.defineProperty(exports, "GatewayModule", { enumerable: true, get: function () { return gateway_module_1.GatewayModule; } });
21
+ // Service Proxy
22
+ var service_proxy_1 = require("./proxy/service-proxy");
23
+ Object.defineProperty(exports, "ServiceProxy", { enumerable: true, get: function () { return service_proxy_1.ServiceProxy; } });
24
+ // Routing
25
+ var version_router_1 = require("./routing/version-router");
26
+ Object.defineProperty(exports, "VersionRouter", { enumerable: true, get: function () { return version_router_1.VersionRouter; } });
27
+ var route_matcher_1 = require("./routing/route-matcher");
28
+ Object.defineProperty(exports, "matchRoute", { enumerable: true, get: function () { return route_matcher_1.matchRoute; } });
29
+ Object.defineProperty(exports, "sortRoutesBySpecificity", { enumerable: true, get: function () { return route_matcher_1.sortRoutesBySpecificity; } });
30
+ // Canary Engine
31
+ var canary_engine_1 = require("./canary/canary-engine");
32
+ Object.defineProperty(exports, "CanaryEngine", { enumerable: true, get: function () { return canary_engine_1.CanaryEngine; } });
33
+ Object.defineProperty(exports, "parseInterval", { enumerable: true, get: function () { return canary_engine_1.parseInterval; } });
34
+ // Metrics
35
+ var gateway_metrics_1 = require("./metrics/gateway-metrics");
36
+ Object.defineProperty(exports, "GatewayMetrics", { enumerable: true, get: function () { return gateway_metrics_1.GatewayMetrics; } });
37
+ // Traffic Mirror
38
+ var traffic_mirror_1 = require("./middleware/traffic-mirror");
39
+ Object.defineProperty(exports, "TrafficMirror", { enumerable: true, get: function () { return traffic_mirror_1.TrafficMirror; } });
40
+ // HazelJS Core Integration
41
+ var hazel_integration_1 = require("./hazel-integration");
42
+ Object.defineProperty(exports, "createGatewayHandler", { enumerable: true, get: function () { return hazel_integration_1.createGatewayHandler; } });
43
+ // Decorators
44
+ var decorators_1 = require("./decorators");
45
+ Object.defineProperty(exports, "Gateway", { enumerable: true, get: function () { return decorators_1.Gateway; } });
46
+ Object.defineProperty(exports, "Route", { enumerable: true, get: function () { return decorators_1.Route; } });
47
+ Object.defineProperty(exports, "ServiceRoute", { enumerable: true, get: function () { return decorators_1.ServiceRoute; } });
48
+ Object.defineProperty(exports, "VersionRoute", { enumerable: true, get: function () { return decorators_1.VersionRoute; } });
49
+ Object.defineProperty(exports, "Canary", { enumerable: true, get: function () { return decorators_1.Canary; } });
50
+ Object.defineProperty(exports, "TrafficPolicy", { enumerable: true, get: function () { return decorators_1.TrafficPolicy; } });
51
+ Object.defineProperty(exports, "GatewayCircuitBreaker", { enumerable: true, get: function () { return decorators_1.GatewayCircuitBreaker; } });
52
+ Object.defineProperty(exports, "GatewayRateLimit", { enumerable: true, get: function () { return decorators_1.GatewayRateLimit; } });
53
+ // Metadata readers
54
+ Object.defineProperty(exports, "getGatewayConfig", { enumerable: true, get: function () { return decorators_1.getGatewayConfig; } });
55
+ Object.defineProperty(exports, "getRouteConfig", { enumerable: true, get: function () { return decorators_1.getRouteConfig; } });
56
+ Object.defineProperty(exports, "getServiceRouteConfig", { enumerable: true, get: function () { return decorators_1.getServiceRouteConfig; } });
57
+ Object.defineProperty(exports, "getVersionRouteConfig", { enumerable: true, get: function () { return decorators_1.getVersionRouteConfig; } });
58
+ Object.defineProperty(exports, "getCanaryConfig", { enumerable: true, get: function () { return decorators_1.getCanaryConfig; } });
59
+ Object.defineProperty(exports, "getTrafficPolicyConfig", { enumerable: true, get: function () { return decorators_1.getTrafficPolicyConfig; } });
60
+ Object.defineProperty(exports, "getCircuitBreakerConfig", { enumerable: true, get: function () { return decorators_1.getCircuitBreakerConfig; } });
61
+ Object.defineProperty(exports, "getRateLimitConfig", { enumerable: true, get: function () { return decorators_1.getRateLimitConfig; } });
62
+ Object.defineProperty(exports, "collectRouteDefinitions", { enumerable: true, get: function () { return decorators_1.collectRouteDefinitions; } });
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Gateway Metrics
3
+ * Aggregates metrics across all routes and services for observability.
4
+ * Wraps @hazeljs/resilience MetricsCollector per route/service/version.
5
+ */
6
+ import { MetricsCollector, MetricsSnapshot } from '@hazeljs/resilience';
7
+ export interface RouteMetricsSnapshot {
8
+ route: string;
9
+ serviceName: string;
10
+ version?: string;
11
+ metrics: MetricsSnapshot;
12
+ }
13
+ export interface GatewayMetricsSnapshot {
14
+ timestamp: number;
15
+ totalRoutes: number;
16
+ routes: RouteMetricsSnapshot[];
17
+ aggregated: MetricsSnapshot;
18
+ }
19
+ export declare class GatewayMetrics {
20
+ /** route -> MetricsCollector */
21
+ private routeMetrics;
22
+ /** route:version -> MetricsCollector */
23
+ private versionMetrics;
24
+ private windowMs;
25
+ constructor(windowMs?: number);
26
+ /**
27
+ * Get or create a metrics collector for a route
28
+ */
29
+ getRouteCollector(route: string): MetricsCollector;
30
+ /**
31
+ * Get or create a metrics collector for a route+version
32
+ */
33
+ getVersionCollector(route: string, version: string): MetricsCollector;
34
+ /**
35
+ * Record a successful request
36
+ */
37
+ recordSuccess(route: string, duration: number, version?: string): void;
38
+ /**
39
+ * Record a failed request
40
+ */
41
+ recordFailure(route: string, duration: number, error?: string, version?: string): void;
42
+ /**
43
+ * Get metrics for a specific route
44
+ */
45
+ getRouteMetrics(route: string): MetricsSnapshot | undefined;
46
+ /**
47
+ * Get metrics for a specific route+version
48
+ */
49
+ getVersionMetrics(route: string, version: string): MetricsSnapshot | undefined;
50
+ /**
51
+ * Get error rate for a specific route+version
52
+ */
53
+ getVersionErrorRate(route: string, version: string): number;
54
+ /**
55
+ * Get a full snapshot of all gateway metrics
56
+ */
57
+ getSnapshot(): GatewayMetricsSnapshot;
58
+ /**
59
+ * Reset all metrics
60
+ */
61
+ reset(): void;
62
+ private aggregateMetrics;
63
+ }
64
+ //# sourceMappingURL=gateway-metrics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway-metrics.d.ts","sourceRoot":"","sources":["../../src/metrics/gateway-metrics.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAExE,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,eAAe,CAAC;CAC1B;AAED,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,oBAAoB,EAAE,CAAC;IAC/B,UAAU,EAAE,eAAe,CAAC;CAC7B;AAED,qBAAa,cAAc;IACzB,gCAAgC;IAChC,OAAO,CAAC,YAAY,CAAuC;IAC3D,wCAAwC;IACxC,OAAO,CAAC,cAAc,CAAuC;IAC7D,OAAO,CAAC,QAAQ,CAAS;gBAEb,QAAQ,GAAE,MAAe;IAIrC;;OAEG;IACH,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB;IASlD;;OAEG;IACH,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,gBAAgB;IAUrE;;OAEG;IACH,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI;IAOtE;;OAEG;IACH,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI;IAOtF;;OAEG;IACH,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAI3D;;OAEG;IACH,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAI9E;;OAEG;IACH,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAK3D;;OAEG;IACH,WAAW,IAAI,sBAAsB;IAsBrC;;OAEG;IACH,KAAK,IAAI,IAAI;IASb,OAAO,CAAC,gBAAgB;CAmDzB"}
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+ /**
3
+ * Gateway Metrics
4
+ * Aggregates metrics across all routes and services for observability.
5
+ * Wraps @hazeljs/resilience MetricsCollector per route/service/version.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.GatewayMetrics = void 0;
9
+ const resilience_1 = require("@hazeljs/resilience");
10
+ class GatewayMetrics {
11
+ constructor(windowMs = 60000) {
12
+ /** route -> MetricsCollector */
13
+ this.routeMetrics = new Map();
14
+ /** route:version -> MetricsCollector */
15
+ this.versionMetrics = new Map();
16
+ this.windowMs = windowMs;
17
+ }
18
+ /**
19
+ * Get or create a metrics collector for a route
20
+ */
21
+ getRouteCollector(route) {
22
+ let collector = this.routeMetrics.get(route);
23
+ if (!collector) {
24
+ collector = new resilience_1.MetricsCollector(this.windowMs);
25
+ this.routeMetrics.set(route, collector);
26
+ }
27
+ return collector;
28
+ }
29
+ /**
30
+ * Get or create a metrics collector for a route+version
31
+ */
32
+ getVersionCollector(route, version) {
33
+ const key = `${route}:${version}`;
34
+ let collector = this.versionMetrics.get(key);
35
+ if (!collector) {
36
+ collector = new resilience_1.MetricsCollector(this.windowMs);
37
+ this.versionMetrics.set(key, collector);
38
+ }
39
+ return collector;
40
+ }
41
+ /**
42
+ * Record a successful request
43
+ */
44
+ recordSuccess(route, duration, version) {
45
+ this.getRouteCollector(route).recordSuccess(duration);
46
+ if (version) {
47
+ this.getVersionCollector(route, version).recordSuccess(duration);
48
+ }
49
+ }
50
+ /**
51
+ * Record a failed request
52
+ */
53
+ recordFailure(route, duration, error, version) {
54
+ this.getRouteCollector(route).recordFailure(duration, error);
55
+ if (version) {
56
+ this.getVersionCollector(route, version).recordFailure(duration, error);
57
+ }
58
+ }
59
+ /**
60
+ * Get metrics for a specific route
61
+ */
62
+ getRouteMetrics(route) {
63
+ return this.routeMetrics.get(route)?.getSnapshot();
64
+ }
65
+ /**
66
+ * Get metrics for a specific route+version
67
+ */
68
+ getVersionMetrics(route, version) {
69
+ return this.versionMetrics.get(`${route}:${version}`)?.getSnapshot();
70
+ }
71
+ /**
72
+ * Get error rate for a specific route+version
73
+ */
74
+ getVersionErrorRate(route, version) {
75
+ const collector = this.versionMetrics.get(`${route}:${version}`);
76
+ return collector ? collector.getFailureRate() : 0;
77
+ }
78
+ /**
79
+ * Get a full snapshot of all gateway metrics
80
+ */
81
+ getSnapshot() {
82
+ const routes = [];
83
+ for (const [route, collector] of this.routeMetrics) {
84
+ routes.push({
85
+ route,
86
+ serviceName: route, // Will be enriched by gateway
87
+ metrics: collector.getSnapshot(),
88
+ });
89
+ }
90
+ // Aggregate all route metrics
91
+ const aggregated = this.aggregateMetrics(routes.map((r) => r.metrics));
92
+ return {
93
+ timestamp: Date.now(),
94
+ totalRoutes: routes.length,
95
+ routes,
96
+ aggregated,
97
+ };
98
+ }
99
+ /**
100
+ * Reset all metrics
101
+ */
102
+ reset() {
103
+ for (const collector of this.routeMetrics.values()) {
104
+ collector.reset();
105
+ }
106
+ for (const collector of this.versionMetrics.values()) {
107
+ collector.reset();
108
+ }
109
+ }
110
+ aggregateMetrics(snapshots) {
111
+ if (snapshots.length === 0) {
112
+ return {
113
+ totalCalls: 0,
114
+ successCalls: 0,
115
+ failureCalls: 0,
116
+ failureRate: 0,
117
+ averageResponseTime: 0,
118
+ p50ResponseTime: 0,
119
+ p95ResponseTime: 0,
120
+ p99ResponseTime: 0,
121
+ minResponseTime: 0,
122
+ maxResponseTime: 0,
123
+ };
124
+ }
125
+ let totalCalls = 0;
126
+ let successCalls = 0;
127
+ let failureCalls = 0;
128
+ let totalResponseTime = 0;
129
+ let minResponseTime = Infinity;
130
+ let maxResponseTime = 0;
131
+ let maxP50 = 0;
132
+ let maxP95 = 0;
133
+ let maxP99 = 0;
134
+ for (const s of snapshots) {
135
+ totalCalls += s.totalCalls;
136
+ successCalls += s.successCalls;
137
+ failureCalls += s.failureCalls;
138
+ totalResponseTime += s.averageResponseTime * s.totalCalls;
139
+ minResponseTime = Math.min(minResponseTime, s.minResponseTime || Infinity);
140
+ maxResponseTime = Math.max(maxResponseTime, s.maxResponseTime);
141
+ maxP50 = Math.max(maxP50, s.p50ResponseTime);
142
+ maxP95 = Math.max(maxP95, s.p95ResponseTime);
143
+ maxP99 = Math.max(maxP99, s.p99ResponseTime);
144
+ }
145
+ return {
146
+ totalCalls,
147
+ successCalls,
148
+ failureCalls,
149
+ failureRate: totalCalls > 0 ? (failureCalls / totalCalls) * 100 : 0,
150
+ averageResponseTime: totalCalls > 0 ? totalResponseTime / totalCalls : 0,
151
+ p50ResponseTime: maxP50,
152
+ p95ResponseTime: maxP95,
153
+ p99ResponseTime: maxP99,
154
+ minResponseTime: minResponseTime === Infinity ? 0 : minResponseTime,
155
+ maxResponseTime,
156
+ };
157
+ }
158
+ }
159
+ exports.GatewayMetrics = GatewayMetrics;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Traffic Mirror
3
+ * Sends shadow copies of requests to a secondary service for testing
4
+ * without affecting the primary response.
5
+ */
6
+ import { DiscoveryClient } from '@hazeljs/discovery';
7
+ import { TrafficMirrorConfig, ProxyRequest } from '../types';
8
+ export declare class TrafficMirror {
9
+ private config;
10
+ private discoveryClient;
11
+ private axiosInstance;
12
+ constructor(config: TrafficMirrorConfig, discoveryClient: DiscoveryClient);
13
+ /**
14
+ * Mirror a request to the shadow service.
15
+ * Fire-and-forget unless waitForResponse is true.
16
+ */
17
+ mirror(request: ProxyRequest): Promise<void>;
18
+ }
19
+ //# sourceMappingURL=traffic-mirror.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traffic-mirror.d.ts","sourceRoot":"","sources":["../../src/middleware/traffic-mirror.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7D,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,aAAa,CAAgB;gBAEzB,MAAM,EAAE,mBAAmB,EAAE,eAAe,EAAE,eAAe;IAMzE;;;OAGG;IACG,MAAM,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;CAsCnD"}
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ /**
3
+ * Traffic Mirror
4
+ * Sends shadow copies of requests to a secondary service for testing
5
+ * without affecting the primary response.
6
+ */
7
+ var __importDefault = (this && this.__importDefault) || function (mod) {
8
+ return (mod && mod.__esModule) ? mod : { "default": mod };
9
+ };
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.TrafficMirror = void 0;
12
+ const axios_1 = __importDefault(require("axios"));
13
+ class TrafficMirror {
14
+ constructor(config, discoveryClient) {
15
+ this.config = config;
16
+ this.discoveryClient = discoveryClient;
17
+ this.axiosInstance = axios_1.default.create({ timeout: 5000 });
18
+ }
19
+ /**
20
+ * Mirror a request to the shadow service.
21
+ * Fire-and-forget unless waitForResponse is true.
22
+ */
23
+ async mirror(request) {
24
+ // Check if this request should be mirrored (based on percentage)
25
+ if (Math.random() * 100 > this.config.percentage) {
26
+ return;
27
+ }
28
+ const mirrorFn = async () => {
29
+ try {
30
+ const instance = await this.discoveryClient.getInstance(this.config.service);
31
+ if (!instance)
32
+ return;
33
+ const baseURL = `${instance.protocol || 'http'}://${instance.host}:${instance.port}`;
34
+ await this.axiosInstance.request({
35
+ method: request.method,
36
+ url: request.path,
37
+ baseURL,
38
+ headers: {
39
+ ...request.headers,
40
+ 'X-Mirror': 'true',
41
+ 'X-Mirror-Source': 'hazeljs-gateway',
42
+ },
43
+ data: request.body,
44
+ params: request.query,
45
+ });
46
+ }
47
+ catch {
48
+ // Silently ignore mirror failures — they must not affect the primary flow
49
+ }
50
+ };
51
+ if (this.config.waitForResponse) {
52
+ await mirrorFn();
53
+ }
54
+ else {
55
+ // Fire and forget
56
+ mirrorFn();
57
+ }
58
+ }
59
+ }
60
+ exports.TrafficMirror = TrafficMirror;
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Service Proxy
3
+ * Enhanced HTTP client that integrates discovery, resilience, and traffic policies.
4
+ * The core request engine of the gateway.
5
+ */
6
+ import { DiscoveryClient, ServiceFilter } from '@hazeljs/discovery';
7
+ import { CircuitBreaker, MetricsCollector, CircuitBreakerConfig, RetryConfig, RateLimiterConfig } from '@hazeljs/resilience';
8
+ import { ProxyRequest, ProxyResponse, TrafficTransformConfig } from '../types';
9
+ export interface ServiceProxyConfig {
10
+ /** Service name in the discovery registry */
11
+ serviceName: string;
12
+ /** Load balancing strategy */
13
+ loadBalancingStrategy?: string;
14
+ /** Service instance filter */
15
+ filter?: ServiceFilter;
16
+ /** Strip this prefix from the path before forwarding */
17
+ stripPrefix?: string;
18
+ /** Add this prefix to the path when forwarding */
19
+ addPrefix?: string;
20
+ /** Request timeout in ms */
21
+ timeout?: number;
22
+ /** Retry configuration */
23
+ retry?: Partial<RetryConfig>;
24
+ /** Circuit breaker configuration */
25
+ circuitBreaker?: Partial<CircuitBreakerConfig>;
26
+ /** Rate limiter configuration */
27
+ rateLimit?: Partial<RateLimiterConfig>;
28
+ /** Request/response transformers */
29
+ transform?: TrafficTransformConfig;
30
+ }
31
+ export declare class ServiceProxy {
32
+ private discoveryClient;
33
+ private axiosInstance;
34
+ private retryPolicy?;
35
+ private circuitBreaker?;
36
+ private timeout?;
37
+ private rateLimiter?;
38
+ private metrics;
39
+ private config;
40
+ constructor(discoveryClient: DiscoveryClient, config: ServiceProxyConfig);
41
+ /**
42
+ * Forward an incoming request to the target service
43
+ */
44
+ forward(request: ProxyRequest): Promise<ProxyResponse>;
45
+ /**
46
+ * Forward a request with a specific version filter
47
+ */
48
+ forwardToVersion(request: ProxyRequest, version: string, additionalFilter?: ServiceFilter): Promise<ProxyResponse>;
49
+ /**
50
+ * Forward with a custom filter override
51
+ */
52
+ forwardWithFilter(request: ProxyRequest, filter: ServiceFilter): Promise<ProxyResponse>;
53
+ /**
54
+ * Get the metrics collector for this proxy
55
+ */
56
+ getMetrics(): MetricsCollector;
57
+ /**
58
+ * Get the circuit breaker instance (if configured)
59
+ */
60
+ getCircuitBreaker(): CircuitBreaker | undefined;
61
+ /**
62
+ * Get the service name this proxy routes to
63
+ */
64
+ getServiceName(): string;
65
+ private doForward;
66
+ private doForwardToInstance;
67
+ }
68
+ //# sourceMappingURL=service-proxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-proxy.d.ts","sourceRoot":"","sources":["../../src/proxy/service-proxy.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACrF,OAAO,EACL,cAAc,EAMd,gBAAgB,EAChB,oBAAoB,EACpB,WAAW,EACX,iBAAiB,EAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAE/E,MAAM,WAAW,kBAAkB;IACjC,6CAA6C;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,8BAA8B;IAC9B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,8BAA8B;IAC9B,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4BAA4B;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0BAA0B;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAC7B,oCAAoC;IACpC,cAAc,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC/C,iCAAiC;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACvC,oCAAoC;IACpC,SAAS,CAAC,EAAE,sBAAsB,CAAC;CACpC;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,WAAW,CAAC,CAAc;IAClC,OAAO,CAAC,cAAc,CAAC,CAAiB;IACxC,OAAO,CAAC,OAAO,CAAC,CAAU;IAC1B,OAAO,CAAC,WAAW,CAAC,CAAc;IAClC,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,MAAM,CAAqB;gBAEvB,eAAe,EAAE,eAAe,EAAE,MAAM,EAAE,kBAAkB;IA6BxE;;OAEG;IACG,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;IAgE5D;;OAEG;IACG,gBAAgB,CACpB,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,MAAM,EACf,gBAAgB,CAAC,EAAE,aAAa,GAC/B,OAAO,CAAC,aAAa,CAAC;IAczB;;OAEG;IACG,iBAAiB,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;IAc7F;;OAEG;IACH,UAAU,IAAI,gBAAgB;IAI9B;;OAEG;IACH,iBAAiB,IAAI,cAAc,GAAG,SAAS;IAI/C;;OAEG;IACH,cAAc,IAAI,MAAM;YAMV,SAAS;YAcT,mBAAmB;CA0ElC"}