Shared NestJS infrastructure SDK for Omnitronix game engine services
npm install @omnitronix/game-engine-sdkShared NestJS infrastructure SDK for Omnitronix game engine services. Provides health checks, gRPC adapters, auto-registration, metrics, logging, secrets management, error handling, and application bootstrap — eliminating duplicated infrastructure code across game engine services.
``bash`
npm install @omnitronix/game-engine-sdk
Peer dependencies (your service must provide these):
`bash`
npm install @nestjs/common @nestjs/core @nestjs/config @nestjs/platform-express @omnitronix/game-engine-contract reflect-metadata rxjs
Optional peer dependencies:
`bash`
npm install @aws-sdk/client-secrets-manager # For AWS Secrets Manager
npm install @nestjs/swagger # For Swagger API docs
A minimal game engine service using the SDK:
`typescript
import { createGameEngineApp } from '@omnitronix/game-engine-sdk';
import { AppModule } from './app.module';
async function bootstrap() {
await createGameEngineApp({
serviceName: 'My Game Engine Service',
appModule: AppModule,
bootstrapOptions: { driver: 'database' },
});
}
bootstrap();
`
`typescript
import { MiddlewareConsumer, Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import {
HealthModule,
LoggerModule,
LoggingMiddleware,
MetricsModule,
SecretsModule,
} from '@omnitronix/game-engine-sdk';
import { PrometheusModule } from '@willsoto/nestjs-prometheus';
import { MyGameEngineModule } from './modules/my-game-engine.module';
@Module({})
export class AppModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggingMiddleware).forRoutes('*');
}
static register() {
return {
module: AppModule,
imports: [
ConfigModule.forRoot({ isGlobal: true, envFilePath: '.env' }),
SecretsModule.register(),
LoggerModule,
PrometheusModule.register({
path: '/metrics',
defaultMetrics: { enabled: true },
}),
HealthModule,
MetricsModule,
MyGameEngineModule,
],
};
}
}
`
`typescript
import { Module } from '@nestjs/common';
import {
GAME_ENGINE,
GAME_ENGINE_IN_PORT,
GAME_ENGINE_REGISTRY_OUT_PORT,
GameEngineAutoRegisterService,
GameEngineGrpcInAdapter,
GameEngineRegistryGrpcOutAdapter,
MetricsModule,
} from '@omnitronix/game-engine-sdk';
import { MyGameEngine } from '@omnitronix/my-game-engine';
import { MyGameEngineService } from './services/game-engine.service';
@Module({
imports: [MetricsModule],
providers: [
GameEngineGrpcInAdapter,
GameEngineAutoRegisterService,
MyGameEngineService,
{
provide: GAME_ENGINE_IN_PORT,
useExisting: MyGameEngineService,
},
{
provide: GAME_ENGINE,
useClass: MyGameEngine,
},
{
provide: GAME_ENGINE_REGISTRY_OUT_PORT,
useClass: GameEngineRegistryGrpcOutAdapter,
},
],
exports: [GameEngineGrpcInAdapter],
})
export class MyGameEngineModule {}
`
``
@omnitronix/game-engine-contract -- Pure TS interfaces, zero framework deps
|
@omnitronix/game-engine-sdk -- NestJS modules, gRPC adapters, infrastructure (this package)
|
game-engine-service-* -- Individual game engine services
The SDK depends on the contract. Game engine services depend on the SDK. Game engine packages (pure math/logic) are unaffected.
The SDK exposes sub-path exports for selective imports:
| Import Path | Contents |
|---|---|
| @omnitronix/game-engine-sdk | Everything (recommended for services) |@omnitronix/game-engine-sdk/common
| | Logger, error handling, secrets, retry, metrics, API docs |@omnitronix/game-engine-sdk/health
| | Health check module, readiness probes, shutdown |@omnitronix/game-engine-sdk/registration
| | Auto-register service, registry gRPC adapter |@omnitronix/game-engine-sdk/grpc
| | gRPC in-adapter, Connect RPC router |@omnitronix/game-engine-sdk/bootstrap
| | createGameEngineApp() factory |
Factory function that creates a fully configured NestJS application.
`typescript
import { createGameEngineApp } from '@omnitronix/game-engine-sdk';
await createGameEngineApp({
serviceName: 'Happy Panda Game Engine Service',
appModule: AppModule,
bootstrapOptions: { driver: 'database' },
});
`
What it does:
1. Creates NestJS app with custom logger and CORS enabled
2. Sets up global validation pipe (whitelist + transform)
3. Configures global exception filter (ExceptionsFilter)/api
4. Sets prefix (excludes /health, /ready, /metrics)/docs
5. Registers Swagger docs at GRPC_PORT
6. Creates a separate HTTP/2 cleartext (h2c) server for Connect RPC on uncaughtException
7. Handles graceful shutdown on / unhandledRejection
Environment variables:
| Variable | Default | Description |
|---|---|---|
| PORT | 3000 | HTTP server port |GRPC_PORT
| | 50051 | gRPC (Connect RPC) server port |
Provides /health and /ready HTTP endpoints with system health checks.
`typescript
import { HealthModule } from '@omnitronix/game-engine-sdk';
@Module({
imports: [HealthModule],
})
`
Endpoints:
| Endpoint | Purpose | Response |
|---|---|---|
| GET /health | Liveness probe | 200 if healthy, 500 if unhealthy |GET /ready
| | Readiness probe | 200 if accepting traffic, 503 if draining |
Health checks:
- Heap memory usage (configurable threshold)
- System load average
Environment variables:
| Variable | Default | Description |
|---|---|---|
| HEALTH_MAX_HEAP_USAGE_PERCENTAGE | 98 | Max heap usage before unhealthy |HEALTH_MAX_MEMORY_USAGE_PART
| | 10 | Max load average divisor |
Auto-registers the game engine with the RGS Game Engine Registry on startup.
`typescript
import {
GAME_ENGINE_REGISTRY_OUT_PORT,
GameEngineAutoRegisterService,
GameEngineRegistryGrpcOutAdapter,
} from '@omnitronix/game-engine-sdk';
@Module({
providers: [
GameEngineAutoRegisterService,
{
provide: GAME_ENGINE_REGISTRY_OUT_PORT,
useClass: GameEngineRegistryGrpcOutAdapter,
},
],
})
`
Registration happens asynchronously on module init (non-blocking). Uses exponential backoff retry (5 attempts, 1s initial delay, 2x multiplier, 30s max).
Environment variables:
| Variable | Required | Description |
|---|---|---|
| HOST_NAME | Yes | Hostname for the gRPC URL advertised to the registry |GRPC_PORT
| | No (default 50051) | gRPC port advertised to the registry |GAME_ENGINE_REGISTRY_GRPC_URL
| | Yes | URL of the Game Engine Registry service |
Connect RPC adapter that exposes ProcessCommand and GetGameEngineInfo RPCs.
`typescript
import {
GAME_ENGINE_IN_PORT,
GameEngineGrpcInAdapter,
} from '@omnitronix/game-engine-sdk';
@Module({
providers: [
GameEngineGrpcInAdapter,
{
provide: GAME_ENGINE_IN_PORT,
useExisting: MyGameEngineService,
},
],
exports: [GameEngineGrpcInAdapter],
})
`
The gRPC adapter handles:
- Deserializing protobuf requests into domain commands
- Converting RNG seed types (number internally, string on wire)
- Error handling with state preservation on failure
Structured Winston-based logger implementing NestJS LoggerService.
`typescript
import { Logger, LoggerModule, LoggingMiddleware } from '@omnitronix/game-engine-sdk';
// As a module
@Module({ imports: [LoggerModule] })
// Direct usage
const logger = new Logger('MyService');
logger.log('Processing command', { commandId: '123' });
logger.error('Failed to process', error);
`
Features:
- JSON or pretty-print output (configurable)
- HTTP request/response logging middleware
- Sensitive field sanitization (password, token redacted)DomainException
- -aware (includes errorCode in logs)
Environment variables:
| Variable | Default | Description |
|---|---|---|
| LOG_FORMAT | json | Log format: json or pretty |
Dynamic module for secrets management with local and AWS providers.
`typescript
import { SecretsModule, SECRETS_PROVIDER, SecretsProvider } from '@omnitronix/game-engine-sdk';
// In module
@Module({ imports: [SecretsModule.register()] })
// In service
@Injectable()
class MyService {
constructor(@Inject(SECRETS_PROVIDER) private secrets: SecretsProvider) {}
async getApiKey() {
return this.secrets.get('API_KEY');
}
}
`
Environment variables:
| Variable | Default | Description |
|---|---|---|
| LOCAL_SECRETS | false | true to load from .secrets.env file |AWS_SECRET_ID
| | - | AWS Secrets Manager secret ID |AWS_REGION
| | eu-central-1 | AWS region |
Prometheus metrics collection using prom-client and @willsoto/nestjs-prometheus.
`typescript
import { MetricsModule, METRICS_PORT, MetricsPort } from '@omnitronix/game-engine-sdk';
@Module({ imports: [MetricsModule] })
// In a service
@Injectable()
class MyMetricsHandler {
constructor(@Inject(METRICS_PORT) private metrics: MetricsPort) {}
recordCommand(duration: number) {
this.metrics.getHistogram('game_engine_command_duration_ms').observe(duration);
this.metrics.getCounter('game_engine_commands_total').inc();
}
}
`
Pre-configured metrics: HTTP request duration, WebSocket connections, session tracking, wallet operations, game engine commands, RNG buffer stats, database connection pool, event store operations, and more (40+ metrics).
Global exception filter and domain exception hierarchy.
`typescript
import {
DomainException,
InternalErrorCode,
ExceptionsFilter,
} from '@omnitronix/game-engine-sdk';
// Throw domain exceptions
throw new DomainException(
InternalErrorCode.GAME_NOT_FOUND,
'Game engine not registered',
);
// The ExceptionsFilter (auto-configured by createGameEngineApp) maps
// InternalErrorCode to HTTP status codes:
// 400 - BAD_REQUEST, INVALID_*
// 401 - UNAUTHORIZED
// 403 - FORBIDDEN
// 404 - *_NOT_FOUND
// 409 - CONCURRENT_MODIFICATION
// 422 - INSUFFICIENT_FUNDS
// 503 - SERVICE_UNAVAILABLE, HEALTH_CHECK_FAILED
`
Configurable retry with exponential backoff.
`typescript
import { RetryPolicy, RetryPolicies } from '@omnitronix/game-engine-sdk';
// Pre-built policies
const result = await RetryPolicies.externalApi().execute(() =>
fetch('https://api.example.com/data'),
);
// Custom policy
const policy = new RetryPolicy({
maxAttempts: 5,
delayMs: 200,
backoffMultiplier: 2,
maxDelayMs: 5000,
retryableErrors: ['ECONNREFUSED', 'TIMEOUT'],
operationName: 'fetchData',
});
await policy.execute(() => riskyOperation());
`
Built-in policies:
| Policy | Attempts | Initial Delay | Backoff | Max Delay |
|---|---|---|---|---|
| walletOperation() | 5 | 200ms | 2x | 3s |externalApi()
| | 3 | 100ms | 2x | 1s |database()
| | 3 | 50ms | 2x | 500ms |
| Type | Description |
|---|---|
| GameEngine
| Main engine interface (re-exported from contract) |GameEngineInfo
| | Engine metadata (code, version, RTP, type, name, provider) |GameActionCommand
| | Command sent to the engine |CommandProcessingResult
| | Result of processing a command |RngOutcome
| | Collection of RNG call records |GameEngineInPort
| | Port interface for the engine service layer |Dependency Injection Tokens
| Token | Type | Description |
|---|---|---|
| GAME_ENGINE | GameEngine | The game engine implementation |GAME_ENGINE_IN_PORT
| | GameEngineInPort | Engine service (business logic layer) |GAME_ENGINE_REGISTRY_OUT_PORT
| | GameEngineRegistryOutPort | Registry client adapter |HEALTH_IN_PORT
| | HealthInPort | Health check service |METRICS_PORT
| | MetricsPort | Prometheus metrics service |SECRETS_PROVIDER
| | SecretsProvider | Secrets management provider |SYSTEM_HEALTH_OUT_PORT
| | SystemHealthOutPort | System health adapter |SHUTDOWN_OUT_PORT
| | ShutdownOutPort | Graceful shutdown handler |
Complete reference of all environment variables used by the SDK:
| Variable | Default | Module | Description |
|---|---|---|---|
| PORT | 3000 | Bootstrap | HTTP server port |GRPC_PORT
| | 50051 | Bootstrap / Registration | gRPC server port |HOST_NAME
| | - | Registration | Hostname for registry registration |GAME_ENGINE_REGISTRY_GRPC_URL
| | - | Registration | Registry service URL |LOG_FORMAT
| | json | Logger | json or pretty |LOCAL_SECRETS
| | false | Secrets | Use local .secrets.env file |AWS_SECRET_ID
| | - | Secrets | AWS Secrets Manager secret ID |AWS_REGION
| | eu-central-1 | Secrets | AWS region |HEALTH_MAX_HEAP_USAGE_PERCENTAGE
| | 98 | Health | Max heap usage threshold |HEALTH_MAX_MEMORY_USAGE_PART
| | 10 | Health | Max load average divisor |
The SDK includes proto files and generated TypeScript code for:
- GameEngineService - ProcessCommand, GetGameEngineInfo RPCsGameEngineRegistryService
- - RegisterGameEngine RPC
Proto generation is handled at SDK build time. Consumers do not need to run protoc.
`bash`
npm install # Install dependencies
npm run build # Build CJS + ESM
npm run test # Run tests
npm run lint # Lint with auto-fix
npm run format:check # Check formatting
- @omnitronix/game-engine-contract - Pure TypeScript interfaces (zero framework deps)
- @omnitronix/service-client` - Service client for non-game services
PROPRIETARY