@omnitronix/game-engine-sdk 1.0.4 → 2.0.2

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 (76) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/MIGRATION.md +126 -0
  3. package/dist/bootstrap/index.d.ts +4 -1
  4. package/dist/bootstrap/index.d.ts.map +1 -1
  5. package/dist/bootstrap/index.js +32 -32
  6. package/dist/common/api-docs/setup-documentation.d.ts +1 -1
  7. package/dist/common/api-docs/setup-documentation.d.ts.map +1 -1
  8. package/dist/common/error-handling/all-exceptions.filter.d.ts +0 -3
  9. package/dist/common/error-handling/all-exceptions.filter.d.ts.map +1 -1
  10. package/dist/common/error-handling/all-exceptions.filter.js +35 -21
  11. package/dist/common/error-handling/error-code-mapper.d.ts.map +1 -1
  12. package/dist/common/error-handling/error-code-mapper.js +15 -70
  13. package/dist/common/error-handling/internal-error-code.d.ts +13 -59
  14. package/dist/common/error-handling/internal-error-code.d.ts.map +1 -1
  15. package/dist/common/error-handling/internal-error-code.js +16 -68
  16. package/dist/common/logger/logger.d.ts +2 -3
  17. package/dist/common/logger/logger.d.ts.map +1 -1
  18. package/dist/common/logger/logger.js +3 -16
  19. package/dist/common/logger/logging.middleware.d.ts.map +1 -1
  20. package/dist/common/logger/logging.middleware.js +16 -4
  21. package/dist/common/metrics/prometheus.service.d.ts +5 -0
  22. package/dist/common/metrics/prometheus.service.d.ts.map +1 -1
  23. package/dist/common/metrics/prometheus.service.js +43 -3
  24. package/dist/common/retry/retry-policy.d.ts.map +1 -1
  25. package/dist/common/retry/retry-policy.js +9 -4
  26. package/dist/common/secrets-provider/secrets-provider.aws.d.ts.map +1 -1
  27. package/dist/common/secrets-provider/secrets-provider.aws.js +2 -1
  28. package/dist/esm/bootstrap/index.js +33 -33
  29. package/dist/esm/common/error-handling/all-exceptions.filter.js +36 -22
  30. package/dist/esm/common/error-handling/error-code-mapper.js +15 -70
  31. package/dist/esm/common/error-handling/internal-error-code.js +16 -68
  32. package/dist/esm/common/logger/logger.js +3 -16
  33. package/dist/esm/common/logger/logging.middleware.js +16 -4
  34. package/dist/esm/common/metrics/prometheus.service.js +10 -3
  35. package/dist/esm/common/retry/retry-policy.js +9 -4
  36. package/dist/esm/common/secrets-provider/secrets-provider.aws.js +2 -1
  37. package/dist/esm/generated/game-engine-registry_pb.js +5 -11
  38. package/dist/esm/generated/game-engine_pb.js +10 -24
  39. package/dist/esm/grpc/connect-router.middleware.js +7 -1
  40. package/dist/esm/grpc/game-engine.grpc.in-adapter.js +64 -20
  41. package/dist/esm/health/application/services/health.service.js +33 -39
  42. package/dist/esm/health/health.module.js +1 -1
  43. package/dist/esm/health/infrastructure/adapters/out/shutdown/shutdown.out-adapter.js +19 -4
  44. package/dist/esm/registration/adapters/game-engine-registry.grpc.out-adapter.js +3 -4
  45. package/dist/esm/registration/services/game-engine-auto-register.service.js +11 -8
  46. package/dist/generated/game-engine-registry_pb.d.ts +5 -5
  47. package/dist/generated/game-engine-registry_pb.d.ts.map +1 -1
  48. package/dist/generated/game-engine-registry_pb.js +4 -10
  49. package/dist/generated/game-engine_pb.d.ts +26 -10
  50. package/dist/generated/game-engine_pb.d.ts.map +1 -1
  51. package/dist/generated/game-engine_pb.js +8 -22
  52. package/dist/grpc/connect-router.middleware.d.ts +9 -1
  53. package/dist/grpc/connect-router.middleware.d.ts.map +1 -1
  54. package/dist/grpc/connect-router.middleware.js +7 -1
  55. package/dist/grpc/game-engine.grpc.in-adapter.d.ts +18 -3
  56. package/dist/grpc/game-engine.grpc.in-adapter.d.ts.map +1 -1
  57. package/dist/grpc/game-engine.grpc.in-adapter.js +64 -20
  58. package/dist/grpc/index.d.ts +1 -1
  59. package/dist/grpc/index.d.ts.map +1 -1
  60. package/dist/health/application/ports/out/shutdown.out-port.d.ts +2 -0
  61. package/dist/health/application/ports/out/shutdown.out-port.d.ts.map +1 -1
  62. package/dist/health/application/services/health.service.d.ts.map +1 -1
  63. package/dist/health/application/services/health.service.js +33 -39
  64. package/dist/health/domain/health.d.ts +1 -1
  65. package/dist/health/domain/health.d.ts.map +1 -1
  66. package/dist/health/health.module.js +1 -1
  67. package/dist/health/infrastructure/adapters/out/shutdown/shutdown.out-adapter.d.ts +2 -1
  68. package/dist/health/infrastructure/adapters/out/shutdown/shutdown.out-adapter.d.ts.map +1 -1
  69. package/dist/health/infrastructure/adapters/out/shutdown/shutdown.out-adapter.js +19 -4
  70. package/dist/registration/adapters/game-engine-registry.grpc.out-adapter.d.ts.map +1 -1
  71. package/dist/registration/adapters/game-engine-registry.grpc.out-adapter.js +3 -4
  72. package/dist/registration/services/game-engine-auto-register.service.d.ts.map +1 -1
  73. package/dist/registration/services/game-engine-auto-register.service.js +11 -8
  74. package/dist/types.d.ts +6 -5
  75. package/dist/types.d.ts.map +1 -1
  76. package/package.json +5 -3
package/CHANGELOG.md ADDED
@@ -0,0 +1,86 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@omnitronix/game-engine-sdk` will be documented in this file.
4
+
5
+ ## [2.0.1] - 2026-02-04
6
+
7
+ ### Fixed
8
+ - **OWASP A01**: ExceptionsFilter HTTP path now returns error code only — no longer leaks `DomainException.message` to clients (consistent with gRPC sanitization)
9
+ - **OWASP A01**: `handleHttpException` now sanitizes 5xx messages — returns "Internal server error" instead of leaking internal details
10
+ - **Type safety**: Replaced `(appModule as any).register` cast in bootstrap with proper `hasRegisterMethod` type guard
11
+ - **Type safety**: Removed dead `safeStringify` from Logger, fixed `errorData: any` → `Record<string, unknown>`
12
+ - **Type safety**: `HealthCheckFailedException.details` typed as `unknown` (was `any`)
13
+ - **Type safety**: `GameEngineAutoRegisterService` catch handlers typed as `unknown` (was `any`)
14
+ - **Type safety**: `RetryPolicy.isRetryable` uses proper typed casts (no `as any`)
15
+ - **Type safety**: `INestApplication<any>` → `INestApplication` in API docs setup
16
+ - **Type safety**: Logger `log()` method uses `Record<string, unknown>` (was `Record<string, any>`)
17
+ - **Shutdown reliability**: Removed duplicate shutdown guard in bootstrap — now uses `ShutdownOutPort.isShuttingDown()` as single source of truth
18
+ - **TypeScript correctness**: `unhandledRejection` handler now types `reason` as `unknown` (not `Error`), handles non-Error rejections safely
19
+ - **HTTP status codes**: `UNAUTHORIZED` error code now maps to HTTP 401 (was incorrectly 403)
20
+ - **Error mapping**: `ErrorCodeMapper` default fallback changed from 400 to 500 — unknown error codes are server errors, not client errors
21
+ - **Dead code**: Removed unused `safeStringify` method from `GameEngineGrpcInAdapter` and `Logger`
22
+ - **Formatting**: All source files pass Prettier check (CI was failing on 6 files)
23
+ - **ExceptionsFilter**: Removed `as any` casts in `handleHttpException` — uses typed `Record<string, unknown>` instead
24
+ - **PrometheusService**: `initializeMetricsFromDatabase` is now a no-op instead of throwing (prevents crash if called)
25
+ - **LoggingMiddleware**: Removed hardcoded `/api/races/active` endpoint — now configurable via `LOG_BLOCKED_ENDPOINTS` env var
26
+ - **Security**: Removed `.npmrc` with hardcoded npm token from disk
27
+ - **Type safety**: All `catch` blocks annotated with `: unknown` — bootstrap, retry-policy, gRPC out-adapter, AWS secrets provider
28
+ - **Type safety**: `RetryPolicy.execute` catch wraps non-Error throws in `new Error()` instead of unsafe `as Error` cast
29
+ - **Type safety**: `handleHttpException` validates pre-shaped responses field-by-field instead of blind `as ErrorResponse` cast
30
+ - **Type safety**: `ErrorCodeMapper` uses `??` (nullish coalescing) instead of `||` for fallback — correct for `0` status codes
31
+ - **Code quality**: Removed redundant catch-and-rethrow in `HealthService.checkHealth()` — errors propagate naturally
32
+ - **CI/CD**: Removed `--passWithNoTests` from CI test step — tests must actually exist and run
33
+ - **CI/CD**: Added coverage artifact upload to CI pipeline (Node 20 matrix)
34
+ - **CI/CD**: Added `format:check` step to release workflow — prevents publishing unformatted code
35
+
36
+ ### Added
37
+ - Jest `coverageThreshold` enforced in config (80% statements/branches/functions/lines)
38
+ - `CHANGELOG.md` and `MIGRATION.md` documentation
39
+
40
+ ## [2.0.0] - 2026-02-04
41
+
42
+ ### Breaking Changes
43
+ - All generic type defaults changed from `any` to `unknown` in `types.ts` (SDK-001)
44
+ - `InternalErrorCode` enum reduced from 64 codes to 11 game-engine-relevant codes (SDK-002)
45
+ - gRPC error responses now return error code only, not internal message (SDK-004)
46
+
47
+ ### Fixed
48
+ - **GLI-19**: RNG seed validation prevents silent data corruption — rejects `undefined`, `NaN`, `Infinity` seeds with `INVALID_RNG_SEED` error (SDK-003)
49
+ - **OWASP A01**: gRPC error messages sanitized — internal details logged but never sent to client (SDK-004)
50
+ - **OWASP A03**: gRPC input validation — missing/empty command ID and type rejected with `INVALID_COMMAND` (SDK-005)
51
+ - **Reliability**: h2c gRPC server integrated with graceful shutdown service — no more race conditions (SDK-006)
52
+
53
+ ### Added
54
+ - `HealthModule` now exports `SHUTDOWN_OUT_PORT` and `HEALTH_IN_PORT` providers (SDK-007)
55
+ - `ConnectRouter` extensibility — `additionalRoutes` option for crash game streaming services (SDK-008)
56
+ - Proto schema `game_name` and `provider` fields in `GetGameEngineInfoResponse` (SDK-009)
57
+ - Comprehensive test coverage — 87 tests, 94.71% statements (SDK-010)
58
+
59
+ ## [1.0.4] - 2026-01-30
60
+
61
+ ### Fixed
62
+ - File log transport disabled by default for container compatibility
63
+ - CORS origin hardcoding removed
64
+ - Error message leakage in Swagger responses
65
+ - Log redaction for sensitive fields
66
+ - `.npmrc` removed from tracking
67
+
68
+ ## [1.0.2] - 2026-01-28
69
+
70
+ ### Fixed
71
+ - Inline CI in release workflow
72
+ - Use published contract from npmjs.org
73
+
74
+ ## [1.0.0] - 2026-01-25
75
+
76
+ ### Added
77
+ - Initial release
78
+ - NestJS application bootstrap with standard configuration
79
+ - Connect RPC (h2c) gRPC server
80
+ - Health check module with graceful shutdown
81
+ - Game engine auto-registration service
82
+ - Logging with Winston (JSON and pretty formats)
83
+ - Prometheus metrics integration
84
+ - AWS Secrets Manager provider
85
+ - Swagger/OpenAPI documentation setup
86
+ - Retry policies with exponential backoff
package/MIGRATION.md ADDED
@@ -0,0 +1,126 @@
1
+ # Migration Guide
2
+
3
+ ## Migrating from v1.x to v2.0.0
4
+
5
+ ### Breaking Changes
6
+
7
+ #### 1. Generic Type Defaults: `any` → `unknown`
8
+
9
+ All generic type parameters in `types.ts` now default to `unknown` instead of `any`.
10
+
11
+ **Before (v1.x):**
12
+ ```typescript
13
+ // Implicit any — no type errors on wrong usage
14
+ const result: CommandProcessingResult = { ... };
15
+ result.publicState.balance; // No error (unsafe)
16
+ ```
17
+
18
+ **After (v2.0):**
19
+ ```typescript
20
+ // Must narrow or cast explicitly
21
+ const result: CommandProcessingResult = { ... };
22
+ (result.publicState as { balance: number }).balance; // Explicit
23
+
24
+ // Or use generics for type safety:
25
+ const result: CommandProcessingResult<MyPublicState, MyPrivateState> = { ... };
26
+ result.publicState.balance; // Type-safe
27
+ ```
28
+
29
+ **Migration:** Add explicit generic type parameters to `CommandProcessingResult`, `GameActionCommand`, and `GameEngine` where you use them. This is the recommended approach — it gives you compile-time type safety.
30
+
31
+ #### 2. InternalErrorCode Enum Reduced
32
+
33
+ `InternalErrorCode` was reduced from 64 platform-level codes to 11 game-engine-relevant codes.
34
+
35
+ **Removed codes (examples):**
36
+ - `SESSION_NOT_FOUND`, `OPERATOR_NOT_FOUND`, `TRANSACTION_FAILED`
37
+ - `JACKPOT_ERROR`, `GEO_BLOCKED`, `RNG_REPLAY_ERROR`
38
+ - All session, operator, auth, transaction, jackpot, geo, and RNG replay codes
39
+
40
+ **Kept codes:**
41
+ ```typescript
42
+ GAME_ENGINE_ERROR
43
+ DEBUG_COMMANDS_DISABLED
44
+ UNKNOWN_COMMAND
45
+ INVALID_COMMAND
46
+ INVALID_RNG_SEED
47
+ INTERNAL_SERVER_ERROR
48
+ FORBIDDEN
49
+ UNAUTHORIZED
50
+ VALIDATION_ERROR
51
+ SERVICE_UNAVAILABLE
52
+ HEALTH_CHECK_FAILED
53
+ ```
54
+
55
+ **Migration:** If your game engine service references removed error codes, either:
56
+ 1. Map to a remaining code (e.g., `SESSION_NOT_FOUND` → `INTERNAL_SERVER_ERROR`)
57
+ 2. Define the code in your service's own enum (platform codes belong in the RGS platform)
58
+
59
+ #### 3. gRPC Error Response Format
60
+
61
+ gRPC error responses now return the error code string as the `message` field, not the internal exception message.
62
+
63
+ **Before (v1.x):**
64
+ ```json
65
+ {
66
+ "success": false,
67
+ "message": "Internal: database connection lost at pool#42"
68
+ }
69
+ ```
70
+
71
+ **After (v2.0):**
72
+ ```json
73
+ {
74
+ "success": false,
75
+ "message": "GAME_ENGINE_ERROR"
76
+ }
77
+ ```
78
+
79
+ **Migration:** If your client-side code parses `message` for internal error details, update it to handle error code strings instead. Internal details are logged server-side but never exposed to clients (OWASP A01).
80
+
81
+ ### New Features (Non-Breaking)
82
+
83
+ #### ConnectRouter Extensibility
84
+
85
+ If you need additional gRPC services (e.g., crash game streaming):
86
+
87
+ ```typescript
88
+ await createGameEngineApp({
89
+ serviceName: 'crash-game-engine',
90
+ appModule: AppModule,
91
+ additionalRoutes: (router) => {
92
+ router.service(CrashGameStreamService, {
93
+ streamMultiplier: myStreamHandler,
94
+ });
95
+ },
96
+ });
97
+ ```
98
+
99
+ #### HealthModule Exports
100
+
101
+ `SHUTDOWN_OUT_PORT` and `HEALTH_IN_PORT` are now exported from `HealthModule`. You can inject them in your own modules:
102
+
103
+ ```typescript
104
+ @Module({
105
+ imports: [HealthModule],
106
+ })
107
+ export class MyModule {
108
+ constructor(
109
+ @Inject(SHUTDOWN_OUT_PORT) private shutdown: ShutdownOutPort,
110
+ @Inject(HEALTH_IN_PORT) private health: HealthInPort,
111
+ ) {}
112
+ }
113
+ ```
114
+
115
+ #### Proto Schema Updates
116
+
117
+ `GetGameEngineInfoResponse` now includes `game_name` and `provider` fields. These are populated from your `GameEngineInfo` implementation.
118
+
119
+ ### Checklist
120
+
121
+ - [ ] Update `@omnitronix/game-engine-sdk` to `^2.0.0` in `package.json`
122
+ - [ ] Add explicit generic type parameters where `CommandProcessingResult` or `GameEngine` are used
123
+ - [ ] Remove references to deleted `InternalErrorCode` values (if any)
124
+ - [ ] Update client-side error handling to expect error code strings in gRPC `message` field
125
+ - [ ] Verify `npm run build` succeeds
126
+ - [ ] Verify `npm test` passes
@@ -1,12 +1,15 @@
1
+ import { ConnectRouterOptions } from '../grpc/connect-router.middleware';
1
2
  import { Type } from '@nestjs/common';
2
3
  export interface GameEngineAppOptions {
3
4
  serviceName: string;
4
- appModule: Type<any>;
5
+ appModule: Type<unknown>;
5
6
  bootstrapOptions?: {
6
7
  driver: 'database' | 'in-memory';
7
8
  };
8
9
  /** Allowed CORS origins. Defaults to false (CORS disabled). */
9
10
  corsOrigins?: string[] | boolean;
11
+ /** Additional Connect RPC routes (e.g., crash game streaming services). */
12
+ additionalRoutes?: ConnectRouterOptions['additionalRoutes'];
10
13
  }
11
14
  /**
12
15
  * Creates and bootstraps a game engine NestJS application with
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/bootstrap/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAAsD,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAK1F,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IACrB,gBAAgB,CAAC,EAAE;QAAE,MAAM,EAAE,UAAU,GAAG,WAAW,CAAA;KAAE,CAAC;IACxD,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;CAClC;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,2DA0HtE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/bootstrap/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAuB,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAC9F,OAAO,EAKL,IAAI,EACL,MAAM,gBAAgB,CAAC;AAKxB,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACzB,gBAAgB,CAAC,EAAE;QAAE,MAAM,EAAE,UAAU,GAAG,WAAW,CAAA;KAAE,CAAC;IACxD,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IACjC,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,oBAAoB,CAAC,kBAAkB,CAAC,CAAC;CAC7D;AASD;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,2DA+HtE"}
@@ -45,18 +45,20 @@ const common_1 = require("@nestjs/common");
45
45
  const config_1 = require("@nestjs/config");
46
46
  const core_1 = require("@nestjs/core");
47
47
  const http2 = __importStar(require("http2"));
48
+ /** Type guard for NestJS dynamic modules with a static `register` method. */
49
+ function hasRegisterMethod(module) {
50
+ return 'register' in module && typeof module.register === 'function';
51
+ }
48
52
  /**
49
53
  * Creates and bootstraps a game engine NestJS application with
50
54
  * standard configuration: CORS, validation pipes, exception filters,
51
55
  * Swagger docs, Connect RPC gRPC server, and graceful shutdown.
52
56
  */
53
57
  async function createGameEngineApp(options) {
54
- const { serviceName, appModule, bootstrapOptions, corsOrigins = false } = options;
58
+ const { serviceName, appModule, bootstrapOptions, corsOrigins = false, additionalRoutes, } = options;
55
59
  const logger = new logger_1.Logger(serviceName);
56
- const moduleArg = bootstrapOptions
57
- ? appModule.register
58
- ? appModule.register(bootstrapOptions)
59
- : appModule
60
+ const moduleArg = bootstrapOptions && hasRegisterMethod(appModule)
61
+ ? appModule.register(bootstrapOptions)
60
62
  : appModule;
61
63
  const app = await core_1.NestFactory.create(moduleArg, {
62
64
  logger: logger,
@@ -66,42 +68,33 @@ async function createGameEngineApp(options) {
66
68
  }
67
69
  const configService = app.get(config_1.ConfigService);
68
70
  const shutdownService = app.get(shutdown_out_port_1.SHUTDOWN_OUT_PORT);
69
- let isShuttingDown = false;
71
+ // Fatal error handler uses ShutdownOutPort's guard to prevent duplicate shutdowns.
72
+ // No separate local flag — single source of truth avoids desync.
70
73
  const handleFatalError = async (errorType, error) => {
71
- logger.error(`${errorType}:`, {
72
- error: error.message,
73
- stack: error.stack,
74
- });
75
- if (isShuttingDown) {
74
+ const errorMessage = error instanceof Error ? error.message : String(error);
75
+ const errorStack = error instanceof Error ? error.stack : undefined;
76
+ logger.error(`${errorType}:`, { error: errorMessage, stack: errorStack });
77
+ if (shutdownService.isShuttingDown()) {
76
78
  logger.warn('Shutdown already in progress, ignoring additional error');
77
79
  return;
78
80
  }
79
- isShuttingDown = true;
80
81
  try {
81
82
  logger.warn(`Initiating graceful shutdown due to ${errorType}...`);
82
83
  await shutdownService.startGracefulShutdown();
83
84
  logger.log('Graceful shutdown completed, exiting process');
84
85
  }
85
86
  catch (shutdownError) {
86
- logger.error('Error during graceful shutdown:', shutdownError);
87
+ const shutdownMsg = shutdownError instanceof Error ? shutdownError.message : String(shutdownError);
88
+ logger.error(`Error during graceful shutdown: ${shutdownMsg}`);
87
89
  }
88
90
  finally {
89
91
  process.exit(1);
90
92
  }
91
93
  };
92
94
  process.on('uncaughtException', (error) => {
93
- logger.error('Uncaught Exception:', {
94
- error: error.message,
95
- stack: error.stack,
96
- });
97
95
  void handleFatalError('uncaughtException', error);
98
96
  });
99
- process.on('unhandledRejection', (reason, promise) => {
100
- logger.error('Unhandled Rejection:', {
101
- reason: reason.message,
102
- stack: reason.stack,
103
- promise,
104
- });
97
+ process.on('unhandledRejection', (reason) => {
105
98
  void handleFatalError('unhandledRejection', reason);
106
99
  });
107
100
  app.setGlobalPrefix('api', {
@@ -127,11 +120,11 @@ async function createGameEngineApp(options) {
127
120
  },
128
121
  }));
129
122
  app.useGlobalFilters(app.get(all_exceptions_filter_1.ExceptionsFilter));
130
- const PORT = configService.get('PORT') || 3000;
123
+ const PORT = configService.get('PORT') ?? 3000;
131
124
  await (0, setup_documentation_1.setupDocumentation)(app, PORT, serviceName);
132
125
  // Setup Connect RPC server
133
126
  const gameEngineAdapter = app.get(game_engine_grpc_in_adapter_1.GameEngineGrpcInAdapter);
134
- const connectRouter = (0, connect_router_middleware_1.createConnectRouter)(gameEngineAdapter);
127
+ const connectRouter = (0, connect_router_middleware_1.createConnectRouter)(gameEngineAdapter, { additionalRoutes });
135
128
  // Mount Connect router on /connect path
136
129
  app.use('/connect', connectRouter);
137
130
  app.enableShutdownHooks(['SIGTERM', 'SIGINT']);
@@ -144,14 +137,21 @@ async function createGameEngineApp(options) {
144
137
  h2cServer.listen(GRPC_PORT, '0.0.0.0', () => {
145
138
  logger.log(`Connect RPC (h2c) is available on: http://localhost:${GRPC_PORT}`);
146
139
  });
147
- // Close h2c server during graceful shutdown
148
- const closeH2c = () => {
149
- h2cServer.close(() => {
150
- logger.log('h2c gRPC server closed');
140
+ // Register h2c server closure with graceful shutdown service
141
+ shutdownService.registerCleanupCallback('h2c-grpc-server', () => {
142
+ return new Promise((resolve, reject) => {
143
+ h2cServer.close(err => {
144
+ if (err) {
145
+ logger.error('Error closing h2c gRPC server:', err);
146
+ reject(err);
147
+ }
148
+ else {
149
+ logger.log('h2c gRPC server closed');
150
+ resolve();
151
+ }
152
+ });
151
153
  });
152
- };
153
- process.on('SIGTERM', closeH2c);
154
- process.on('SIGINT', closeH2c);
154
+ });
155
155
  await app.listen(PORT, '0.0.0.0');
156
156
  logger.log(`Application is running on: http://localhost:${PORT}`);
157
157
  return app;
@@ -1,3 +1,3 @@
1
1
  import { INestApplication } from '@nestjs/common';
2
- export declare const setupDocumentation: (app: INestApplication<any>, port: number, serviceName?: string) => Promise<void>;
2
+ export declare const setupDocumentation: (app: INestApplication, port: number, serviceName?: string) => Promise<void>;
3
3
  //# sourceMappingURL=setup-documentation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"setup-documentation.d.ts","sourceRoot":"","sources":["../../../src/common/api-docs/setup-documentation.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAMlD,eAAO,MAAM,kBAAkB,GAC7B,KAAK,gBAAgB,CAAC,GAAG,CAAC,EAC1B,MAAM,MAAM,EACZ,cAAc,MAAM,kBA0BrB,CAAC"}
1
+ {"version":3,"file":"setup-documentation.d.ts","sourceRoot":"","sources":["../../../src/common/api-docs/setup-documentation.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAMlD,eAAO,MAAM,kBAAkB,GAC7B,KAAK,gBAAgB,EACrB,MAAM,MAAM,EACZ,cAAc,MAAM,kBA0BrB,CAAC"}
@@ -1,4 +1,3 @@
1
- import { MetricsPort } from '../metrics/metrics.port';
2
1
  import { ArgumentsHost, ExceptionFilter } from '@nestjs/common';
3
2
  export interface ErrorResponse {
4
3
  statusCode: number;
@@ -8,9 +7,7 @@ export interface ErrorResponse {
8
7
  path: string;
9
8
  }
10
9
  export declare class ExceptionsFilter implements ExceptionFilter {
11
- private readonly metricsPort;
12
10
  private readonly logger;
13
- constructor(metricsPort: MetricsPort);
14
11
  catch(exception: unknown, host: ArgumentsHost): void;
15
12
  private buildErrorResponse;
16
13
  private handleDomainException;
@@ -1 +1 @@
1
- {"version":3,"file":"all-exceptions.filter.d.ts","sourceRoot":"","sources":["../../../src/common/error-handling/all-exceptions.filter.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EACL,aAAa,EAEb,eAAe,EAIhB,MAAM,gBAAgB,CAAC;AAOxB,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBACa,gBAAiB,YAAW,eAAe;IAKpD,OAAO,CAAC,QAAQ,CAAC,WAAW;IAJ9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAIb,WAAW,EAAE,WAAW;IAK3C,KAAK,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa;IAS7C,OAAO,CAAC,kBAAkB;IAgB1B,OAAO,CAAC,qBAAqB;IAqB7B,OAAO,CAAC,mBAAmB;IAmD3B,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,kBAAkB;IAe1B,OAAO,CAAC,oBAAoB;CAmB7B"}
1
+ {"version":3,"file":"all-exceptions.filter.d.ts","sourceRoot":"","sources":["../../../src/common/error-handling/all-exceptions.filter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAS,eAAe,EAA6B,MAAM,gBAAgB,CAAC;AAOlG,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBACa,gBAAiB,YAAW,eAAe;IACtD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqC;IAE5D,KAAK,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa;IAS7C,OAAO,CAAC,kBAAkB;IAgB1B,OAAO,CAAC,qBAAqB;IAwB7B,OAAO,CAAC,mBAAmB;IA4E3B,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,kBAAkB;IAe1B,OAAO,CAAC,oBAAoB;CAmB7B"}
@@ -5,24 +5,16 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
6
  return c > 3 && r && Object.defineProperty(target, key, r), r;
7
7
  };
8
- var __metadata = (this && this.__metadata) || function (k, v) {
9
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
- };
11
- var __param = (this && this.__param) || function (paramIndex, decorator) {
12
- return function (target, key) { decorator(target, key, paramIndex); }
13
- };
14
8
  var ExceptionsFilter_1;
15
9
  Object.defineProperty(exports, "__esModule", { value: true });
16
10
  exports.ExceptionsFilter = void 0;
17
11
  const logger_1 = require("../logger/logger");
18
- const metrics_port_1 = require("../metrics/metrics.port");
19
12
  const common_1 = require("@nestjs/common");
20
13
  const domain_exception_1 = require("./domain.exception");
21
14
  const error_code_mapper_1 = require("./error-code-mapper");
22
15
  const internal_error_code_1 = require("./internal-error-code");
23
16
  let ExceptionsFilter = ExceptionsFilter_1 = class ExceptionsFilter {
24
- constructor(metricsPort) {
25
- this.metricsPort = metricsPort;
17
+ constructor() {
26
18
  this.logger = new logger_1.Logger(ExceptionsFilter_1.name);
27
19
  }
28
20
  catch(exception, host) {
@@ -47,14 +39,17 @@ let ExceptionsFilter = ExceptionsFilter_1 = class ExceptionsFilter {
47
39
  handleDomainException(exception, request) {
48
40
  const status = error_code_mapper_1.ErrorCodeMapper.mapToHttpStatus(exception.errorCode);
49
41
  const level = status >= 500 ? 'error' : 'warn';
42
+ // Log full internal details for debugging — never sent to client
50
43
  this.logger.log(`DomainException (status: ${status} at ${request.method} ${request.url}): Internal Code ${exception.errorCode} Message: ${exception.message}`, { level });
51
44
  if (exception.cause) {
52
45
  this.logger.error('Exception cause:', exception.cause);
53
46
  }
47
+ // OWASP A01: Return error code only — never expose internal error.message to clients.
48
+ // Consistent with gRPC error sanitization in GameEngineGrpcInAdapter.
54
49
  return this.buildResponsePayload({
55
50
  status,
56
51
  internalCode: exception.errorCode,
57
- message: exception.message,
52
+ message: exception.errorCode,
58
53
  path: request.url,
59
54
  });
60
55
  }
@@ -62,17 +57,29 @@ let ExceptionsFilter = ExceptionsFilter_1 = class ExceptionsFilter {
62
57
  const status = exception.getStatus();
63
58
  const responseData = exception.getResponse();
64
59
  // Check if this is a custom validation error response
65
- if (typeof responseData === 'object' &&
66
- responseData.internalCode &&
67
- responseData.statusCode) {
68
- return responseData;
60
+ const responseRecord = typeof responseData === 'object' && responseData !== null
61
+ ? responseData
62
+ : null;
63
+ if (responseRecord &&
64
+ typeof responseRecord.internalCode === 'string' &&
65
+ typeof responseRecord.statusCode === 'number' &&
66
+ typeof responseRecord.message !== 'undefined') {
67
+ return {
68
+ statusCode: responseRecord.statusCode,
69
+ internalCode: responseRecord.internalCode,
70
+ message: String(responseRecord.message),
71
+ timestamp: typeof responseRecord.timestamp === 'string'
72
+ ? responseRecord.timestamp
73
+ : new Date().toISOString(),
74
+ path: typeof responseRecord.path === 'string' ? responseRecord.path : request.url,
75
+ };
69
76
  }
70
77
  let message = 'HTTP Exception';
71
78
  if (typeof responseData === 'string') {
72
79
  message = responseData;
73
80
  }
74
- else if (typeof responseData === 'object' && responseData.message) {
75
- message = responseData.message;
81
+ else if (responseRecord && typeof responseRecord.message === 'string') {
82
+ message = responseRecord.message;
76
83
  }
77
84
  const level = status >= 500 ? 'error' : 'warn';
78
85
  this.logger.log(`HttpException (status: ${status} at ${request.method} ${request.url}): ${message}`, { level });
@@ -85,12 +92,21 @@ let ExceptionsFilter = ExceptionsFilter_1 = class ExceptionsFilter {
85
92
  path: request.url,
86
93
  });
87
94
  }
88
- if (status === common_1.HttpStatus.INTERNAL_SERVER_ERROR) {
95
+ // OWASP A01: Never expose internal error details for server errors.
96
+ // 5xx messages may contain stack traces, DB errors, or internal details.
97
+ if (status >= 500) {
89
98
  this.logger.error('HttpException stack:', exception.stack);
99
+ return this.buildResponsePayload({
100
+ status,
101
+ internalCode: internal_error_code_1.InternalErrorCode.INTERNAL_SERVER_ERROR,
102
+ message: 'Internal server error',
103
+ path: request.url,
104
+ });
90
105
  }
106
+ // 4xx client errors — framework messages are safe (e.g., "Not Found", "Bad Request")
91
107
  return this.buildResponsePayload({
92
108
  status,
93
- internalCode: internal_error_code_1.InternalErrorCode.INTERNAL_SERVER_ERROR,
109
+ internalCode: internal_error_code_1.InternalErrorCode.VALIDATION_ERROR,
94
110
  message,
95
111
  path: request.url,
96
112
  });
@@ -125,7 +141,5 @@ let ExceptionsFilter = ExceptionsFilter_1 = class ExceptionsFilter {
125
141
  };
126
142
  exports.ExceptionsFilter = ExceptionsFilter;
127
143
  exports.ExceptionsFilter = ExceptionsFilter = ExceptionsFilter_1 = __decorate([
128
- (0, common_1.Catch)(),
129
- __param(0, (0, common_1.Inject)(metrics_port_1.METRICS_PORT)),
130
- __metadata("design:paramtypes", [Object])
144
+ (0, common_1.Catch)()
131
145
  ], ExceptionsFilter);
@@ -1 +1 @@
1
- {"version":3,"file":"error-code-mapper.d.ts","sourceRoot":"","sources":["../../../src/common/error-handling/error-code-mapper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAa1D,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CA4E5C;IAEF,MAAM,CAAC,eAAe,CAAC,SAAS,EAAE,iBAAiB,GAAG,MAAM;CAG7D"}
1
+ {"version":3,"file":"error-code-mapper.d.ts","sourceRoot":"","sources":["../../../src/common/error-handling/error-code-mapper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAc1D,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAmB5C;IAEF,MAAM,CAAC,eAAe,CAAC,SAAS,EAAE,iBAAiB,GAAG,MAAM;CAG7D"}
@@ -5,89 +5,34 @@ const internal_error_code_1 = require("./internal-error-code");
5
5
  // HTTP status codes
6
6
  const HttpStatus = {
7
7
  BAD_REQUEST: 400,
8
- CONFLICT: 409,
8
+ UNAUTHORIZED: 401,
9
9
  FORBIDDEN: 403,
10
10
  NOT_FOUND: 404,
11
+ CONFLICT: 409,
11
12
  UNPROCESSABLE_ENTITY: 422,
12
13
  INTERNAL_SERVER_ERROR: 500,
13
14
  SERVICE_UNAVAILABLE: 503,
14
15
  };
15
16
  class ErrorCodeMapper {
16
17
  static mapToHttpStatus(errorCode) {
17
- return this.ERROR_CODE_TO_HTTP_MAP[errorCode] || HttpStatus.BAD_REQUEST;
18
+ return this.ERROR_CODE_TO_HTTP_MAP[errorCode] ?? HttpStatus.INTERNAL_SERVER_ERROR;
18
19
  }
19
20
  }
20
21
  exports.ErrorCodeMapper = ErrorCodeMapper;
21
22
  ErrorCodeMapper.ERROR_CODE_TO_HTTP_MAP = {
22
- [internal_error_code_1.InternalErrorCode.FORBIDDEN]: HttpStatus.FORBIDDEN,
23
- [internal_error_code_1.InternalErrorCode.INVALID_CURRENCY_CODE]: HttpStatus.BAD_REQUEST,
24
- [internal_error_code_1.InternalErrorCode.INSUFFICIENT_FUNDS]: HttpStatus.UNPROCESSABLE_ENTITY,
25
- [internal_error_code_1.InternalErrorCode.INTERNAL_SERVER_ERROR]: HttpStatus.INTERNAL_SERVER_ERROR,
26
- // Game Catalog errors
27
- [internal_error_code_1.InternalErrorCode.GAME_NOT_FOUND]: HttpStatus.NOT_FOUND,
28
- [internal_error_code_1.InternalErrorCode.GAME_CONFIG_NOT_FOUND]: HttpStatus.NOT_FOUND,
29
- [internal_error_code_1.InternalErrorCode.GAME_CONFIG_ALREADY_EXISTS_WITH_SAME_JURISDICTION_AND_RTP]: HttpStatus.BAD_REQUEST,
30
- [internal_error_code_1.InternalErrorCode.OPERATOR_GAME_NOT_FOUND]: HttpStatus.NOT_FOUND,
31
- [internal_error_code_1.InternalErrorCode.OPERATOR_GAME_ALREADY_ENABLED]: HttpStatus.BAD_REQUEST,
32
- [internal_error_code_1.InternalErrorCode.INVALID_GAME_CONFIG_DATA]: HttpStatus.BAD_REQUEST,
33
- [internal_error_code_1.InternalErrorCode.INVALID_GAME_UPDATE_DATA]: HttpStatus.BAD_REQUEST,
34
- [internal_error_code_1.InternalErrorCode.GAME_CODE_ALREADY_EXISTS]: HttpStatus.BAD_REQUEST,
35
- [internal_error_code_1.InternalErrorCode.GAME_OR_GAME_CONFIG_DISABLED]: HttpStatus.BAD_REQUEST,
23
+ // Core game engine errors
24
+ [internal_error_code_1.InternalErrorCode.GAME_ENGINE_ERROR]: HttpStatus.INTERNAL_SERVER_ERROR,
36
25
  [internal_error_code_1.InternalErrorCode.DEBUG_COMMANDS_DISABLED]: HttpStatus.FORBIDDEN,
37
- // Game Orchestrator errors
38
- [internal_error_code_1.InternalErrorCode.INVALID_SESSION]: HttpStatus.BAD_REQUEST,
39
- [internal_error_code_1.InternalErrorCode.SESSION_NOT_FOUND]: HttpStatus.NOT_FOUND,
40
- [internal_error_code_1.InternalErrorCode.SESSION_NOT_ACTIVE]: HttpStatus.BAD_REQUEST,
41
- [internal_error_code_1.InternalErrorCode.SESSION_MISMATCH]: HttpStatus.BAD_REQUEST,
42
- [internal_error_code_1.InternalErrorCode.INVALID_STATE_TRANSITION]: HttpStatus.BAD_REQUEST,
43
- [internal_error_code_1.InternalErrorCode.INVALID_BET_AMOUNT]: HttpStatus.BAD_REQUEST,
44
- [internal_error_code_1.InternalErrorCode.REALITY_CHECK_IN_PROGRESS]: HttpStatus.BAD_REQUEST,
45
- [internal_error_code_1.InternalErrorCode.UNKNOWN_COMMAND]: HttpStatus.NOT_FOUND,
46
- // Operator errors
47
- [internal_error_code_1.InternalErrorCode.MISSING_OPERATOR_CODE]: HttpStatus.BAD_REQUEST,
48
- [internal_error_code_1.InternalErrorCode.INVALID_OPERATOR_CODE]: HttpStatus.BAD_REQUEST,
49
- [internal_error_code_1.InternalErrorCode.OPERATOR_NOT_FOUND]: HttpStatus.NOT_FOUND,
50
- [internal_error_code_1.InternalErrorCode.OPERATOR_INACTIVE]: HttpStatus.FORBIDDEN,
51
- [internal_error_code_1.InternalErrorCode.COUNTRY_CODE_RESTRICTED]: HttpStatus.BAD_REQUEST,
52
- [internal_error_code_1.InternalErrorCode.UNAUTHORIZED]: HttpStatus.FORBIDDEN,
53
- [internal_error_code_1.InternalErrorCode.AUTH_VALIDATION_FAILED]: HttpStatus.FORBIDDEN,
54
- [internal_error_code_1.InternalErrorCode.BALANCE_NOT_FOUND]: HttpStatus.NOT_FOUND,
55
- [internal_error_code_1.InternalErrorCode.INVALID_AUTH_DATA]: HttpStatus.BAD_REQUEST,
26
+ [internal_error_code_1.InternalErrorCode.UNKNOWN_COMMAND]: HttpStatus.BAD_REQUEST,
27
+ [internal_error_code_1.InternalErrorCode.INVALID_COMMAND]: HttpStatus.BAD_REQUEST,
28
+ // RNG integrity (GLI-19)
29
+ [internal_error_code_1.InternalErrorCode.INVALID_RNG_SEED]: HttpStatus.INTERNAL_SERVER_ERROR,
30
+ // Infrastructure errors
31
+ [internal_error_code_1.InternalErrorCode.INTERNAL_SERVER_ERROR]: HttpStatus.INTERNAL_SERVER_ERROR,
32
+ [internal_error_code_1.InternalErrorCode.FORBIDDEN]: HttpStatus.FORBIDDEN,
33
+ [internal_error_code_1.InternalErrorCode.UNAUTHORIZED]: HttpStatus.UNAUTHORIZED,
56
34
  [internal_error_code_1.InternalErrorCode.VALIDATION_ERROR]: HttpStatus.BAD_REQUEST,
57
- [internal_error_code_1.InternalErrorCode.JURISDICTION_CONFIG_ALREADY_EXISTS]: HttpStatus.BAD_REQUEST,
58
- [internal_error_code_1.InternalErrorCode.JURISDICTION_CONFIG_NOT_FOUND]: HttpStatus.NOT_FOUND,
59
- [internal_error_code_1.InternalErrorCode.OPERATOR_JURISDICTION_CONFIG_NOT_FOUND]: HttpStatus.NOT_FOUND,
60
- [internal_error_code_1.InternalErrorCode.GAME_ENGINE_ERROR]: HttpStatus.INTERNAL_SERVER_ERROR,
61
- [internal_error_code_1.InternalErrorCode.DEBIT_FAILED]: HttpStatus.BAD_REQUEST,
62
- [internal_error_code_1.InternalErrorCode.CREDIT_FAILED]: HttpStatus.BAD_REQUEST,
63
- [internal_error_code_1.InternalErrorCode.GAME_ROUND_LOG_NOT_FOUND]: HttpStatus.NOT_FOUND,
64
- // Geo Validation errors
65
- [internal_error_code_1.InternalErrorCode.COUNTRY_NOT_PERMITTED]: HttpStatus.FORBIDDEN,
66
- [internal_error_code_1.InternalErrorCode.GEO_VALIDATION_SERVICE_UNAVAILABLE]: HttpStatus.INTERNAL_SERVER_ERROR,
67
- [internal_error_code_1.InternalErrorCode.RESOURCE_NOT_FOUND]: HttpStatus.NOT_FOUND,
68
- [internal_error_code_1.InternalErrorCode.FINANCIAL_TRANSACTION_NOT_FOUND]: HttpStatus.NOT_FOUND,
69
- [internal_error_code_1.InternalErrorCode.SESSION_TERMINATED]: HttpStatus.BAD_REQUEST,
70
- [internal_error_code_1.InternalErrorCode.SESSION_CANNOT_BE_TERMINATED]: HttpStatus.BAD_REQUEST,
71
- [internal_error_code_1.InternalErrorCode.SESSION_AUTO_RESOLVE_FAILED]: HttpStatus.BAD_REQUEST,
72
- [internal_error_code_1.InternalErrorCode.SESSION_ALREADY_EXISTS]: HttpStatus.BAD_REQUEST,
73
- [internal_error_code_1.InternalErrorCode.INCOMPATIBLE_GAMEPLAY_CONFIG]: HttpStatus.BAD_REQUEST,
74
- // Health check errors
75
- [internal_error_code_1.InternalErrorCode.HEALTH_CHECK_FAILED]: HttpStatus.SERVICE_UNAVAILABLE,
76
- //Jackpot errors
77
- [internal_error_code_1.InternalErrorCode.JACKPOT_TYPE_NOT_FOUND]: HttpStatus.NOT_FOUND,
78
- [internal_error_code_1.InternalErrorCode.JACKPOT_TIER_NOT_FOUND]: HttpStatus.NOT_FOUND,
79
- [internal_error_code_1.InternalErrorCode.JACKPOT_CONFIGURATION_NOT_FOUND]: HttpStatus.NOT_FOUND,
80
- [internal_error_code_1.InternalErrorCode.JACKPOT_CONFIGURATION_IS_ACTIVE]: HttpStatus.BAD_REQUEST,
81
- // RNG and Replay errors
82
- [internal_error_code_1.InternalErrorCode.RNG_SEED_NOT_FOUND]: HttpStatus.NOT_FOUND,
83
- [internal_error_code_1.InternalErrorCode.RNG_SEED_MISMATCH]: HttpStatus.BAD_REQUEST,
84
- [internal_error_code_1.InternalErrorCode.MASTER_HASH_INVALID]: HttpStatus.BAD_REQUEST,
85
- [internal_error_code_1.InternalErrorCode.REPLAY_OUTCOME_MISMATCH]: HttpStatus.BAD_REQUEST,
86
- [internal_error_code_1.InternalErrorCode.JACKPOT_POOL_CONTRIBUTION_ALREADY_PROCESSED]: HttpStatus.INTERNAL_SERVER_ERROR,
87
- [internal_error_code_1.InternalErrorCode.JACKPOT_TYPE_DOES_NOT_SUPPORT_TIERS]: HttpStatus.BAD_REQUEST,
88
- [internal_error_code_1.InternalErrorCode.CONCURRENT_MODIFICATION]: HttpStatus.CONFLICT,
89
- [internal_error_code_1.InternalErrorCode.ACCOUNT_LOCKED]: HttpStatus.FORBIDDEN,
90
- [internal_error_code_1.InternalErrorCode.DAILY_LIMIT_EXCEEDED]: HttpStatus.UNPROCESSABLE_ENTITY,
91
- [internal_error_code_1.InternalErrorCode.INVALID_AMOUNT]: HttpStatus.BAD_REQUEST,
92
35
  [internal_error_code_1.InternalErrorCode.SERVICE_UNAVAILABLE]: HttpStatus.SERVICE_UNAVAILABLE,
36
+ // Health check
37
+ [internal_error_code_1.InternalErrorCode.HEALTH_CHECK_FAILED]: HttpStatus.SERVICE_UNAVAILABLE,
93
38
  };