@omen.foundation/node-microservice-runtime 0.1.118 → 0.1.121

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/runtime.cjs CHANGED
@@ -30,6 +30,7 @@ class MicroserviceRuntime {
30
30
  constructor(env) {
31
31
  this.microServiceId = (0, node_crypto_1.randomUUID)();
32
32
  this.isReady = false;
33
+ this.dependencyProvidersReady = false;
33
34
  this.env = env !== null && env !== void 0 ? env : (0, env_js_1.loadEnvironmentConfig)();
34
35
  const envConfig = this.env;
35
36
  (0, env_loader_js_1.loadDeveloperEnvVarsSync)();
@@ -133,14 +134,18 @@ class MicroserviceRuntime {
133
134
  this.logger.info('Initializing Beamable SDK services...');
134
135
  const primaryServiceName = (_a = this.services[0]) === null || _a === void 0 ? void 0 : _a.definition.name;
135
136
  await this.serviceManager.initialize(primaryServiceName);
137
+ this.dependencyProvidersReady = false;
138
+ this.logger.info('Registering basic service provider with gateway...');
139
+ await this.registerBasicServiceProvider();
140
+ this.isReady = true;
141
+ this.logger.info('Beamable microservice runtime is ready to accept traffic.');
136
142
  this.logger.info('Initializing dependency providers...');
137
143
  await this.initializeDependencyProviders();
138
- this.logger.info('Registering service with gateway...');
139
- await this.registerService();
144
+ this.dependencyProvidersReady = true;
145
+ this.logger.info('Registering event provider with gateway...');
146
+ await this.registerEventServiceProvider();
140
147
  this.logger.info('Starting discovery broadcaster...');
141
148
  await ((_b = this.discovery) === null || _b === void 0 ? void 0 : _b.start());
142
- this.isReady = true;
143
- this.logger.info('Beamable microservice runtime is ready to accept traffic.');
144
149
  }
145
150
  catch (error) {
146
151
  debugLog('[BEAMABLE-NODE] FATAL ERROR during startup:');
@@ -154,12 +159,14 @@ class MicroserviceRuntime {
154
159
  isReady: this.isReady,
155
160
  }, 'Failed to fully initialize microservice runtime. Health check will continue to return 503 until initialization completes.');
156
161
  this.isReady = false;
162
+ this.dependencyProvidersReady = false;
157
163
  }
158
164
  }
159
165
  async shutdown() {
160
166
  var _a;
161
167
  this.logger.info('Shutting down microservice runtime.');
162
168
  this.isReady = false;
169
+ this.dependencyProvidersReady = false;
163
170
  (_a = this.discovery) === null || _a === void 0 ? void 0 : _a.stop();
164
171
  await this.stopHealthCheckServer();
165
172
  this.requester.dispose();
@@ -209,13 +216,12 @@ class MicroserviceRuntime {
209
216
  });
210
217
  });
211
218
  }
212
- async registerService() {
213
- var _a, _b;
219
+ async registerBasicServiceProvider() {
220
+ var _a;
214
221
  const primary = (_a = this.services[0]) === null || _a === void 0 ? void 0 : _a.definition;
215
222
  if (!primary) {
216
223
  throw new Error('Unexpected missing service definition during registration.');
217
224
  }
218
- const options = (_b = (0, decorators_js_1.getServiceOptions)(primary.ctor)) !== null && _b !== void 0 ? _b : {};
219
225
  const isDeployed = isRunningInContainer();
220
226
  const request = {
221
227
  type: 'basic',
@@ -244,9 +250,7 @@ class MicroserviceRuntime {
244
250
  await this.requester.request('post', 'gateway/provider', request);
245
251
  this.logger.info({ serviceName: primary.qualifiedName }, 'Service provider registered successfully.');
246
252
  if (isDeployed) {
247
- this.logger.debug('Waiting for gateway to establish bindings after registration...');
248
- await new Promise((resolve) => setTimeout(resolve, 2000));
249
- this.logger.debug('Wait complete, gateway should have established bindings by now.');
253
+ this.logger.debug('Gateway binding setup continues asynchronously (no blocking sleep before health readiness).');
250
254
  }
251
255
  }
252
256
  catch (error) {
@@ -258,20 +262,27 @@ class MicroserviceRuntime {
258
262
  }, 'Failed to register service provider with gateway. This will prevent the service from receiving requests.');
259
263
  throw error;
260
264
  }
265
+ }
266
+ async registerEventServiceProvider() {
267
+ var _a, _b;
268
+ const primary = (_a = this.services[0]) === null || _a === void 0 ? void 0 : _a.definition;
269
+ if (!primary) {
270
+ return;
271
+ }
272
+ const options = (_b = (0, decorators_js_1.getServiceOptions)(primary.ctor)) !== null && _b !== void 0 ? _b : {};
261
273
  if (options.disableAllBeamableEvents) {
262
274
  this.logger.info('Beamable events disabled by configuration.');
275
+ return;
263
276
  }
264
- else {
265
- const eventRequest = {
266
- type: 'event',
267
- evtWhitelist: ['content.manifest', 'realm.config', 'logging.context'],
268
- };
269
- try {
270
- await this.requester.request('post', 'gateway/provider', eventRequest);
271
- }
272
- catch (error) {
273
- this.logger.warn({ err: error }, 'Failed to register event provider. Continuing without events.');
274
- }
277
+ const eventRequest = {
278
+ type: 'event',
279
+ evtWhitelist: ['content.manifest', 'realm.config', 'logging.context'],
280
+ };
281
+ try {
282
+ await this.requester.request('post', 'gateway/provider', eventRequest);
283
+ }
284
+ catch (error) {
285
+ this.logger.warn({ err: error }, 'Failed to register event provider. Continuing without events.');
275
286
  }
276
287
  }
277
288
  async handleEvent(envelope) {
@@ -284,6 +295,17 @@ class MicroserviceRuntime {
284
295
  await this.requester.acknowledge(envelope.id);
285
296
  return;
286
297
  }
298
+ if (!this.dependencyProvidersReady) {
299
+ await this.requester.sendResponse({
300
+ id: envelope.id,
301
+ status: 503,
302
+ body: {
303
+ error: 'ServiceUnavailable',
304
+ message: 'Microservice dependency providers are still initializing.',
305
+ },
306
+ });
307
+ return;
308
+ }
287
309
  const context = this.toRequestContext(envelope);
288
310
  await this.dispatch(context);
289
311
  }
package/dist/runtime.d.ts CHANGED
@@ -11,12 +11,19 @@ export declare class MicroserviceRuntime {
11
11
  private readonly serviceManager;
12
12
  private healthCheckServer?;
13
13
  private isReady;
14
+ /** False until DI container is built; traffic is rejected with 503 while isReady may already be true. */
15
+ private dependencyProvidersReady;
14
16
  constructor(env?: EnvironmentConfig);
15
17
  start(): Promise<void>;
16
18
  shutdown(): Promise<void>;
17
19
  private startHealthCheckServer;
18
20
  private stopHealthCheckServer;
19
- private registerService;
21
+ /**
22
+ * POST basic provider to gateway. In start(), isReady is set immediately after this succeeds,
23
+ * before dependency providers are built; traffic is gated until dependencyProvidersReady.
24
+ */
25
+ private registerBasicServiceProvider;
26
+ private registerEventServiceProvider;
20
27
  private handleEvent;
21
28
  private toRequestContext;
22
29
  private findServiceForPath;
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,iBAAiB,EAOlB,MAAM,YAAY,CAAC;AA4BpB,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoB;IACxC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoB;IAC9C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmB;IAC7C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAuB;IAClD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAgB;IAC/C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAyB;IACxD,OAAO,CAAC,iBAAiB,CAAC,CAAS;IACnC,OAAO,CAAC,OAAO,CAAkB;gBAErB,GAAG,CAAC,EAAE,iBAAiB;IA2H7B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA+DtB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;YASjB,sBAAsB;YA2CtB,qBAAqB;YAarB,eAAe;YA+Gf,WAAW;IA8BzB,OAAO,CAAC,gBAAgB;IAuFxB,OAAO,CAAC,kBAAkB;IAW1B;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;YA+BV,QAAQ;IAwDtB,OAAO,CAAC,wBAAwB;YA8BlB,mBAAmB;YAiBnB,sBAAsB;YAStB,wBAAwB;YAsBxB,mBAAmB;IA6GjC,OAAO,CAAC,kBAAkB;IAgB1B,OAAO,CAAC,gBAAgB;YA+BV,6BAA6B;IAiC3C,OAAO,CAAC,kBAAkB;IAO1B;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;CAI5B;AA+GD,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAoDrD;AAED,UAAU,sBAAsB;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,4CAA4C,CAC1D,SAAS,GAAE,OAAO,CAAC,iBAAiB,CAAM,EAC1C,OAAO,GAAE,sBAA2B,GACnC,OAAO,CA6BT"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,iBAAiB,EAOlB,MAAM,YAAY,CAAC;AA4BpB,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoB;IACxC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoB;IAC9C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmB;IAC7C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAuB;IAClD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAgB;IAC/C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAyB;IACxD,OAAO,CAAC,iBAAiB,CAAC,CAAS;IACnC,OAAO,CAAC,OAAO,CAAkB;IACjC,yGAAyG;IACzG,OAAO,CAAC,wBAAwB,CAAkB;gBAEtC,GAAG,CAAC,EAAE,iBAAiB;IA2H7B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAuEtB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;YAUjB,sBAAsB;YA2CtB,qBAAqB;IAanC;;;OAGG;YACW,4BAA4B;YA2F5B,4BAA4B;YAqB5B,WAAW;IA2CzB,OAAO,CAAC,gBAAgB;IAuFxB,OAAO,CAAC,kBAAkB;IAW1B;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;YA+BV,QAAQ;IAwDtB,OAAO,CAAC,wBAAwB;YA8BlB,mBAAmB;YAiBnB,sBAAsB;YAStB,wBAAwB;YAsBxB,mBAAmB;IA6GjC,OAAO,CAAC,kBAAkB;IAgB1B,OAAO,CAAC,gBAAgB;YA+BV,6BAA6B;IAiC3C,OAAO,CAAC,kBAAkB;IAO1B;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;CAI5B;AA+GD,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAoDrD;AAED,UAAU,sBAAsB;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,4CAA4C,CAC1D,SAAS,GAAE,OAAO,CAAC,iBAAiB,CAAM,EAC1C,OAAO,GAAE,sBAA2B,GACnC,OAAO,CA6BT"}
package/dist/runtime.js CHANGED
@@ -31,6 +31,8 @@ export class MicroserviceRuntime {
31
31
  serviceManager;
32
32
  healthCheckServer;
33
33
  isReady = false;
34
+ /** False until DI container is built; traffic is rejected with 503 while isReady may already be true. */
35
+ dependencyProvidersReady = false;
34
36
  constructor(env) {
35
37
  this.env = env ?? loadEnvironmentConfig();
36
38
  const envConfig = this.env; // Capture for async IIFE to satisfy TypeScript
@@ -164,15 +166,20 @@ export class MicroserviceRuntime {
164
166
  // Pass service name to initialize so routing key can be configured for service-level requests
165
167
  const primaryServiceName = this.services[0]?.definition.name;
166
168
  await this.serviceManager.initialize(primaryServiceName);
169
+ this.dependencyProvidersReady = false;
170
+ // Register with gateway before building the app DI graph so GET /health can go 200 while
171
+ // ConfigureServices/build still run (large graphs no longer block Portal probes).
172
+ this.logger.info('Registering basic service provider with gateway...');
173
+ await this.registerBasicServiceProvider();
174
+ this.isReady = true;
175
+ this.logger.info('Beamable microservice runtime is ready to accept traffic.');
167
176
  this.logger.info('Initializing dependency providers...');
168
177
  await this.initializeDependencyProviders();
169
- this.logger.info('Registering service with gateway...');
170
- await this.registerService();
178
+ this.dependencyProvidersReady = true;
179
+ this.logger.info('Registering event provider with gateway...');
180
+ await this.registerEventServiceProvider();
171
181
  this.logger.info('Starting discovery broadcaster...');
172
182
  await this.discovery?.start();
173
- // Mark as ready only after service is fully registered and discovery is started
174
- this.isReady = true;
175
- this.logger.info('Beamable microservice runtime is ready to accept traffic.');
176
183
  }
177
184
  catch (error) {
178
185
  // Log the error with full context but don't crash - health check server is running
@@ -191,11 +198,13 @@ export class MicroserviceRuntime {
191
198
  // DON'T re-throw - keep process alive so health check can show 503
192
199
  // This allows us to see the error in logs
193
200
  this.isReady = false;
201
+ this.dependencyProvidersReady = false;
194
202
  }
195
203
  }
196
204
  async shutdown() {
197
205
  this.logger.info('Shutting down microservice runtime.');
198
206
  this.isReady = false; // Mark as not ready during shutdown
207
+ this.dependencyProvidersReady = false;
199
208
  this.discovery?.stop();
200
209
  await this.stopHealthCheckServer();
201
210
  this.requester.dispose();
@@ -253,12 +262,15 @@ export class MicroserviceRuntime {
253
262
  });
254
263
  });
255
264
  }
256
- async registerService() {
265
+ /**
266
+ * POST basic provider to gateway. In start(), isReady is set immediately after this succeeds,
267
+ * before dependency providers are built; traffic is gated until dependencyProvidersReady.
268
+ */
269
+ async registerBasicServiceProvider() {
257
270
  const primary = this.services[0]?.definition;
258
271
  if (!primary) {
259
272
  throw new Error('Unexpected missing service definition during registration.');
260
273
  }
261
- const options = getServiceOptions(primary.ctor) ?? {};
262
274
  // Match C# exactly: use qualifiedName (preserves case) for name field.
263
275
  // The gateway lowercases service names when creating bindings, but uses the original case
264
276
  // from the registration request when constructing routing key lookups.
@@ -317,16 +329,10 @@ export class MicroserviceRuntime {
317
329
  try {
318
330
  await this.requester.request('post', 'gateway/provider', request);
319
331
  this.logger.info({ serviceName: primary.qualifiedName }, 'Service provider registered successfully.');
320
- // After registration, the gateway's BasicServiceProvider.start() is called asynchronously
321
- // This triggers afterRabbitInit() -> setupDirectServiceCommunication() -> scheduleServiceBindingCheck()
322
- // The first updateBindings() call happens immediately (0.millisecond delay)
323
- // We wait a bit to allow the gateway to set up the HTTP binding and call updateBindings()
324
- // This is especially important for deployed services where the gateway needs to establish
325
- // the external host binding before bindings can be created
332
+ // Former 2s deployed-only await blocked isReady and Portal "Deploying"; gateway still runs
333
+ // binding setup asynchronously health can go green while that finishes.
326
334
  if (isDeployed) {
327
- this.logger.debug('Waiting for gateway to establish bindings after registration...');
328
- await new Promise((resolve) => setTimeout(resolve, 2000)); // 2 second wait for deployed services
329
- this.logger.debug('Wait complete, gateway should have established bindings by now.');
335
+ this.logger.debug('Gateway binding setup continues asynchronously (no blocking sleep before health readiness).');
330
336
  }
331
337
  }
332
338
  catch (error) {
@@ -338,20 +344,26 @@ export class MicroserviceRuntime {
338
344
  }, 'Failed to register service provider with gateway. This will prevent the service from receiving requests.');
339
345
  throw error; // Re-throw so startup fails and we can see the error
340
346
  }
347
+ }
348
+ async registerEventServiceProvider() {
349
+ const primary = this.services[0]?.definition;
350
+ if (!primary) {
351
+ return;
352
+ }
353
+ const options = getServiceOptions(primary.ctor) ?? {};
341
354
  if (options.disableAllBeamableEvents) {
342
355
  this.logger.info('Beamable events disabled by configuration.');
356
+ return;
343
357
  }
344
- else {
345
- const eventRequest = {
346
- type: 'event',
347
- evtWhitelist: ['content.manifest', 'realm.config', 'logging.context'],
348
- };
349
- try {
350
- await this.requester.request('post', 'gateway/provider', eventRequest);
351
- }
352
- catch (error) {
353
- this.logger.warn({ err: error }, 'Failed to register event provider. Continuing without events.');
354
- }
358
+ const eventRequest = {
359
+ type: 'event',
360
+ evtWhitelist: ['content.manifest', 'realm.config', 'logging.context'],
361
+ };
362
+ try {
363
+ await this.requester.request('post', 'gateway/provider', eventRequest);
364
+ }
365
+ catch (error) {
366
+ this.logger.warn({ err: error }, 'Failed to register event provider. Continuing without events.');
355
367
  }
356
368
  }
357
369
  async handleEvent(envelope) {
@@ -364,6 +376,18 @@ export class MicroserviceRuntime {
364
376
  await this.requester.acknowledge(envelope.id);
365
377
  return;
366
378
  }
379
+ // Avoid dispatch with an empty DI scope while ConfigureServices/build is still running.
380
+ if (!this.dependencyProvidersReady) {
381
+ await this.requester.sendResponse({
382
+ id: envelope.id,
383
+ status: 503,
384
+ body: {
385
+ error: 'ServiceUnavailable',
386
+ message: 'Microservice dependency providers are still initializing.',
387
+ },
388
+ });
389
+ return;
390
+ }
367
391
  const context = this.toRequestContext(envelope);
368
392
  await this.dispatch(context);
369
393
  }
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { BeamableWebSocket } from './websocket.js';\nimport { GatewayRequester } from './requester.js';\nimport { AuthManager } from './auth.js';\nimport { createLogger } from './logger.js';\nimport { loadEnvironmentConfig } from './env.js';\nimport { loadAndInjectEnvironmentVariables, loadDeveloperEnvVarsSync } from './env-loader.js';\nimport { startCollectorAndWaitForReady } from './collector-manager.js';\nimport pino from 'pino';\n// Removed deasync - using non-blocking async pattern instead\nimport { listRegisteredServices, getServiceOptions, getConfigureServicesHandlers, getInitializeServicesHandlers } from './decorators.js';\nimport { generateOpenApiDocument } from './docs.js';\nimport { VERSION } from './index.js';\nimport { DiscoveryBroadcaster } from './discovery.js';\nimport { BeamableRuntimeError, MissingScopesError, UnauthorizedUserError, UnknownRouteError } from './errors.js';\nimport type {\n EnvironmentConfig,\n RequestContext,\n ServiceDefinition,\n ServiceCallableMetadata,\n GatewayResponse,\n WebsocketEventEnvelope,\n ServiceAccess,\n} from './types.js';\nimport type { Logger } from 'pino';\nimport { BeamableServiceManager } from './services.js';\nimport { StorageService } from './storage.js';\nimport {\n DependencyBuilder,\n LOGGER_TOKEN,\n ENVIRONMENT_CONFIG_TOKEN,\n REQUEST_CONTEXT_TOKEN,\n BEAMABLE_SERVICES_TOKEN,\n ServiceProvider,\n MutableDependencyScope,\n} from './dependency.js';\nimport { hostToHttpUrl, hostToPortalUrl } from './utils/urls.js';\nimport { FederationRegistry, getFederationComponents, getFederatedInventoryMetadata } from './federation.js';\nimport type { FederatedRequestContext } from './federation.js';\nimport { createServer, type Server } from 'node:http';\n\ninterface ServiceInstance {\n definition: ServiceDefinition;\n instance: Record<string, unknown>;\n configureHandlers: ReturnType<typeof getConfigureServicesHandlers>;\n initializeHandlers: ReturnType<typeof getInitializeServicesHandlers>;\n provider?: ServiceProvider;\n logger: Logger;\n federationRegistry: FederationRegistry;\n}\n\nexport class MicroserviceRuntime {\n private readonly env: EnvironmentConfig;\n private logger: Logger; // Mutable to allow upgrading from console logger to structured logger when collector is ready\n private readonly services: ServiceInstance[];\n private readonly webSocket: BeamableWebSocket;\n private readonly requester: GatewayRequester;\n private readonly authManager: AuthManager;\n private readonly discovery?: DiscoveryBroadcaster;\n private readonly microServiceId = randomUUID();\n private readonly serviceManager: BeamableServiceManager;\n private healthCheckServer?: Server;\n private isReady: boolean = false;\n\n constructor(env?: EnvironmentConfig) {\n this.env = env ?? loadEnvironmentConfig();\n const envConfig = this.env; // Capture for async IIFE to satisfy TypeScript\n \n // STEP 1: Load developer-defined environment variables SYNCHRONOUSLY before logger creation\n // This ensures BetterStack and other env vars are available when the logger initializes\n loadDeveloperEnvVarsSync();\n \n // STEP 2: Create minimal console logger for startup messages (before collector setup)\n // This ensures we have logging available immediately, even before collector is ready\n const startupLogger = pino({\n name: 'beamable-runtime-startup',\n level: 'info',\n }, process.stdout);\n // Display runtime version at startup (VERSION is imported synchronously at top of file)\n startupLogger.info(`Starting Beamable Node microservice runtime (version: ${VERSION}).`);\n \n // STEP 2.5: Load Beamable Config asynchronously (in background, non-blocking)\n // Developer-defined vars are already loaded synchronously above\n // Beamable Config is fetched via API with a 2-second timeout\n (async () => {\n try {\n startupLogger.info('Loading Beamable Config environment variables...');\n await loadAndInjectEnvironmentVariables(envConfig, undefined, true, 2000);\n startupLogger.info('Beamable Config loading completed');\n } catch (error) {\n startupLogger.warn(`Beamable Config loading completed with warnings: ${error instanceof Error ? error.message : String(error)}`);\n }\n })();\n \n // STEP 2: Get registered services to extract service name\n const registered = listRegisteredServices();\n if (registered.length === 0) {\n throw new Error('No microservices registered. Use the @Microservice decorator to register at least one class.');\n }\n \n // Use the first service's name for the main logger (for CloudWatch filtering and ClickHouse compatibility)\n const primaryService = registered[0];\n const qualifiedServiceName = `micro_${primaryService.qualifiedName}`;\n \n // STEP 3: Create logger immediately (non-blocking pattern, matching C#)\n // Start with minimal console logger, upgrade to structured logger when collector is ready\n // This allows the service to start immediately without blocking\n startupLogger.info('Setting up OpenTelemetry collector in background (non-blocking)...');\n \n // Create logger immediately with console output (no OTLP yet)\n // This ensures we have logging available right away\n this.logger = createLogger(this.env, {\n name: 'beamable-node-microservice',\n serviceName: primaryService.name,\n qualifiedServiceName: qualifiedServiceName,\n // No otlpEndpoint - will use console logger initially\n });\n \n // STEP 4: Start collector setup in background (non-blocking)\n // When collector is ready, upgrade logger to structured logger with OTLP\n // This matches C# pattern: collector starts in background, service starts immediately\n startCollectorAndWaitForReady(this.env)\n .then((endpoint) => {\n if (endpoint) {\n // Collector is ready - upgrade to structured logger with OTLP support\n this.logger.info(`Collector ready at ${endpoint}, upgrading to structured logger for Portal logs...`);\n this.logger = createLogger(this.env, {\n name: 'beamable-node-microservice',\n serviceName: primaryService.name,\n qualifiedServiceName: qualifiedServiceName,\n otlpEndpoint: endpoint, // Collector is ready, Portal logs will now work\n });\n this.logger.info('Portal logs enabled - structured logs will now appear in Beamable Portal');\n } else {\n this.logger.warn('Collector setup completed but no endpoint was returned. Continuing with console logs. Portal logs will not be available.');\n }\n })\n .catch((error) => {\n const errorMsg = error instanceof Error ? error.message : String(error);\n this.logger.error(`Failed to setup collector: ${errorMsg}. Continuing with console logs. Portal logs will not be available.`);\n // Service continues to work with console logger - graceful degradation\n });\n \n // Continue immediately - don't wait for collector!\n // Service can start accepting requests right away\n // Collector setup happens in background, logger upgrades automatically when ready\n this.serviceManager = new BeamableServiceManager(this.env, this.logger);\n\n this.services = registered.map((definition) => {\n const instance = new definition.ctor() as Record<string, unknown>;\n const configureHandlers = getConfigureServicesHandlers(definition.ctor);\n const initializeHandlers = getInitializeServicesHandlers(definition.ctor);\n const logger = this.logger.child({ service: definition.name });\n const federationRegistry = new FederationRegistry(logger);\n const decoratedFederations = getFederationComponents(definition.ctor);\n for (const component of decoratedFederations) {\n federationRegistry.register(component);\n }\n const inventoryMetadata = getFederatedInventoryMetadata(definition.ctor);\n if (inventoryMetadata.length > 0) {\n federationRegistry.registerInventoryHandlers(instance, inventoryMetadata);\n }\n this.serviceManager.registerFederationRegistry(definition.name, federationRegistry);\n return { definition, instance, configureHandlers, initializeHandlers, logger, federationRegistry };\n });\n\n const socketUrl = this.env.host.endsWith('/socket') ? this.env.host : `${this.env.host}/socket`;\n this.webSocket = new BeamableWebSocket({ url: socketUrl, logger: this.logger });\n this.webSocket.on('message', (payload) => {\n this.logger.debug({ payload }, 'Runtime observed websocket frame.');\n });\n this.requester = new GatewayRequester(this.webSocket, this.logger);\n this.authManager = new AuthManager(this.env, this.requester);\n this.requester.on('event', (envelope) => this.handleEvent(envelope));\n\n // Discovery broadcaster only runs in local development (not in containers)\n // This allows the portal to detect that the service is running locally\n if (!isRunningInContainer() && this.services.length > 0) {\n this.discovery = new DiscoveryBroadcaster({\n env: this.env,\n serviceName: this.services[0].definition.name,\n routingKey: this.env.routingKey,\n logger: this.logger.child({ component: 'DiscoveryBroadcaster' }),\n });\n }\n }\n\n async start(): Promise<void> {\n // Immediate console output for container logs (before logger is ready)\n debugLog('[BEAMABLE-NODE] MicroserviceRuntime.start() called');\n debugLog(`[BEAMABLE-NODE] Service count: ${this.services.length}`);\n \n this.printHelpfulUrls(this.services[0]);\n this.logger.info('Starting Beamable Node microservice runtime.');\n \n // Start health check server FIRST - this is critical for container health checks\n // Even if startup fails, the health check server must be running so we can debug\n debugLog('[BEAMABLE-NODE] Starting health check server...');\n await this.startHealthCheckServer();\n debugLog('[BEAMABLE-NODE] Health check server started');\n \n try {\n this.logger.info('Connecting to Beamable gateway...');\n await this.webSocket.connect();\n await new Promise((resolve) => setTimeout(resolve, 250));\n \n this.logger.info('Authenticating with Beamable...');\n await this.authManager.authenticate();\n \n this.logger.info('Initializing Beamable SDK services...');\n // Pass service name to initialize so routing key can be configured for service-level requests\n const primaryServiceName = this.services[0]?.definition.name;\n await this.serviceManager.initialize(primaryServiceName);\n \n this.logger.info('Initializing dependency providers...');\n await this.initializeDependencyProviders();\n \n this.logger.info('Registering service with gateway...');\n await this.registerService();\n \n this.logger.info('Starting discovery broadcaster...');\n await this.discovery?.start();\n \n // Mark as ready only after service is fully registered and discovery is started\n this.isReady = true;\n this.logger.info('Beamable microservice runtime is ready to accept traffic.');\n } catch (error) {\n // Log the error with full context but don't crash - health check server is running\n // This allows the container to stay alive so we can debug the issue\n // Debug output for local development only (in containers, logger handles this)\n debugLog('[BEAMABLE-NODE] FATAL ERROR during startup:');\n debugLog(`[BEAMABLE-NODE] Error message: ${error instanceof Error ? error.message : String(error)}`);\n debugLog(`[BEAMABLE-NODE] Error stack: ${error instanceof Error ? error.stack : 'No stack trace'}`);\n debugLog(`[BEAMABLE-NODE] isReady: ${this.isReady}`);\n \n this.logger.error(\n { \n err: error,\n errorMessage: error instanceof Error ? error.message : String(error),\n errorStack: error instanceof Error ? error.stack : undefined,\n isReady: this.isReady,\n },\n 'Failed to fully initialize microservice runtime. Health check will continue to return 503 until initialization completes.'\n );\n // DON'T re-throw - keep process alive so health check can show 503\n // This allows us to see the error in logs\n this.isReady = false;\n }\n }\n\n async shutdown(): Promise<void> {\n this.logger.info('Shutting down microservice runtime.');\n this.isReady = false; // Mark as not ready during shutdown\n this.discovery?.stop();\n await this.stopHealthCheckServer();\n this.requester.dispose();\n await this.webSocket.close();\n }\n\n private async startHealthCheckServer(): Promise<void> {\n // For deployed services, always start health check server (required for container health checks)\n // For local development, only start if healthPort is explicitly set\n // IMPORTANT: Always default to 6565 if HEALTH_PORT env var is not set, as this is the standard port\n const healthPort = this.env.healthPort || 6565;\n \n // Always start health check server if we have a valid port\n // The container orchestrator expects this endpoint to be available\n if (!healthPort || healthPort === 0) {\n // Health check server not needed (local development without explicit port)\n this.logger.debug('Health check server skipped (no healthPort set)');\n return;\n }\n\n this.healthCheckServer = createServer((req, res) => {\n if (req.url === '/health' && req.method === 'GET') {\n // Only return success if service is fully ready (registered and accepting traffic)\n if (this.isReady) {\n res.writeHead(200, { 'Content-Type': 'text/plain' });\n res.end('responsive');\n } else {\n // Service is still starting up\n res.writeHead(503, { 'Content-Type': 'text/plain' });\n res.end('Service Unavailable');\n }\n } else {\n res.writeHead(404, { 'Content-Type': 'text/plain' });\n res.end('Not Found');\n }\n });\n\n return new Promise((resolve, reject) => {\n this.healthCheckServer!.listen(healthPort, '0.0.0.0', () => {\n this.logger.info({ port: healthPort }, 'Health check server started on port');\n resolve();\n });\n this.healthCheckServer!.on('error', (err) => {\n this.logger.error({ err, port: healthPort }, 'Failed to start health check server');\n reject(err);\n });\n });\n }\n\n private async stopHealthCheckServer(): Promise<void> {\n if (!this.healthCheckServer) {\n return;\n }\n\n return new Promise((resolve) => {\n this.healthCheckServer!.close(() => {\n this.logger.info('Health check server stopped');\n resolve();\n });\n });\n }\n\n private async registerService(): Promise<void> {\n const primary = this.services[0]?.definition;\n if (!primary) {\n throw new Error('Unexpected missing service definition during registration.');\n }\n const options = getServiceOptions(primary.ctor) ?? {};\n // Match C# exactly: use qualifiedName (preserves case) for name field.\n // The gateway lowercases service names when creating bindings, but uses the original case\n // from the registration request when constructing routing key lookups.\n // This ensures the routing key format matches what the gateway expects.\n // The gateway's binding lookup behavior:\n // - Gateway error shows: \"No binding found for service ...micro_examplenodeservice.basic\"\n // - This means the gateway lowercases the service name for binding storage/lookup\n // - Portal sends requests with mixed case in URL and routing key header\n // - The gateway lowercases the URL path for binding lookup, which should work\n // - But we need to register with the format the gateway expects for the binding key\n // - The gateway constructs binding key as: {cid}.{pid}.{lowercaseServiceName}.{type}\n // - So we register with lowercase to match what the gateway stores in the binding\n // - The portal will still send mixed case, and the gateway will lowercase it for lookup\n // Register with mixed-case qualifiedName to match C# behavior\n // The gateway will lowercase the service name when creating the binding key,\n // but the registration request should use the original case (as C# does)\n // This ensures the service name in the registration matches what the portal expects\n // Register with mixed-case qualifiedName to match C# behavior\n // The gateway's ServiceIdentity.fullNameNoType lowercases the service name when creating bindings,\n // but the registration request should use the original case (as C# does)\n // This ensures the service name in the registration matches what the portal expects\n const isDeployed = isRunningInContainer();\n \n // For deployed services, routingKey should be null/undefined (None in Scala)\n // For local dev, routingKey should be the actual routing key string\n // The backend expects Option[String] = None for deployed services\n // Note: microServiceId is not part of SocketSessionProviderRegisterRequest, so we don't include it\n // All fields except 'type' are Option[T] in Scala, so undefined fields will be omitted from JSON\n // This matches C# behavior where null/None fields are not serialized\n const request: Record<string, unknown> = {\n type: 'basic',\n name: primary.qualifiedName, // Use mixed-case as C# does - gateway handles lowercasing for binding storage\n beamoName: primary.name,\n };\n \n // Only include routingKey and startedById if they have values (for local dev)\n // For deployed services, these should be omitted (undefined) to match None in Scala\n if (!isDeployed && this.env.routingKey) {\n request.routingKey = this.env.routingKey;\n }\n if (!isDeployed && this.env.accountId) {\n request.startedById = this.env.accountId;\n }\n\n // Log registration request to match C# behavior exactly\n // Also log the actual JSON that will be sent (undefined fields will be omitted)\n const serializedRequest = JSON.stringify(request);\n this.logger.debug(\n {\n request: {\n type: request.type,\n name: request.name,\n beamoName: request.beamoName,\n routingKey: request.routingKey,\n startedById: request.startedById,\n },\n serializedJson: serializedRequest,\n isDeployed,\n },\n 'Registering service provider with gateway.',\n );\n\n try {\n await this.requester.request('post', 'gateway/provider', request);\n this.logger.info({ serviceName: primary.qualifiedName }, 'Service provider registered successfully.');\n \n // After registration, the gateway's BasicServiceProvider.start() is called asynchronously\n // This triggers afterRabbitInit() -> setupDirectServiceCommunication() -> scheduleServiceBindingCheck()\n // The first updateBindings() call happens immediately (0.millisecond delay)\n // We wait a bit to allow the gateway to set up the HTTP binding and call updateBindings()\n // This is especially important for deployed services where the gateway needs to establish\n // the external host binding before bindings can be created\n if (isDeployed) {\n this.logger.debug('Waiting for gateway to establish bindings after registration...');\n await new Promise((resolve) => setTimeout(resolve, 2000)); // 2 second wait for deployed services\n this.logger.debug('Wait complete, gateway should have established bindings by now.');\n }\n } catch (error) {\n this.logger.error(\n {\n err: error,\n request,\n serviceName: primary.qualifiedName,\n errorMessage: error instanceof Error ? error.message : String(error),\n },\n 'Failed to register service provider with gateway. This will prevent the service from receiving requests.'\n );\n throw error; // Re-throw so startup fails and we can see the error\n }\n\n if (options.disableAllBeamableEvents) {\n this.logger.info('Beamable events disabled by configuration.');\n } else {\n const eventRequest = {\n type: 'event',\n evtWhitelist: ['content.manifest', 'realm.config', 'logging.context'],\n };\n try {\n await this.requester.request('post', 'gateway/provider', eventRequest);\n } catch (error) {\n this.logger.warn({ err: error }, 'Failed to register event provider. Continuing without events.');\n }\n }\n }\n\n private async handleEvent(envelope: WebsocketEventEnvelope): Promise<void> {\n try {\n if (!envelope.path) {\n this.logger.debug({ envelope }, 'Ignoring websocket event without path.');\n return;\n }\n\n if (envelope.path.startsWith('event/')) {\n await this.requester.acknowledge(envelope.id);\n return;\n }\n\n const context = this.toRequestContext(envelope);\n await this.dispatch(context);\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n this.logger.error({ err, envelope }, 'Failed to handle websocket event.');\n const status = this.resolveErrorStatus(err);\n const response: GatewayResponse = {\n id: envelope.id,\n status,\n body: {\n error: err.name,\n message: err.message,\n },\n };\n await this.requester.sendResponse(response);\n }\n }\n\n private toRequestContext(envelope: WebsocketEventEnvelope): RequestContext {\n const path = envelope.path ?? '';\n const method = envelope.method ?? 'post';\n const userId = typeof envelope.from === 'number' ? envelope.from : 0;\n const headers = envelope.headers ?? {};\n \n // Parse query parameters from path (e.g., /service/route?param1=value1&param2=value2)\n const [pathWithoutQuery, queryString] = path.split('?', 2);\n const query = this.parseQueryString(queryString ?? '');\n \n // Extract scopes from envelope.scopes array\n // Note: X-DE-SCOPE header contains CID.PID, not scope values\n // The gateway sends scopes in envelope.scopes array\n // For admin endpoints, the gateway may not send scopes in the envelope,\n // so we infer admin scope from the path if it's an admin route\n const envelopeScopes = envelope.scopes ?? [];\n let scopes = normalizeScopes(envelopeScopes);\n \n // If this is an admin endpoint and no scopes are provided, infer admin scope\n // The gateway may not always send scopes for admin routes\n const pathLower = pathWithoutQuery.toLowerCase();\n if (pathLower.includes('/admin/') && scopes.size === 0) {\n scopes = normalizeScopes(['admin']);\n }\n\n let body: Record<string, unknown> | undefined;\n if (envelope.body && typeof envelope.body === 'string') {\n try {\n body = JSON.parse(envelope.body) as Record<string, unknown>;\n } catch (error) {\n this.logger.warn({ err: error, raw: envelope.body }, 'Failed to parse body string.');\n }\n } else if (envelope.body && typeof envelope.body === 'object') {\n body = envelope.body as Record<string, unknown>;\n }\n\n let payload: unknown;\n if (body && typeof body === 'object' && 'payload' in body) {\n const rawPayload = (body as Record<string, unknown>).payload;\n if (typeof rawPayload === 'string') {\n try {\n payload = JSON.parse(rawPayload) as unknown[];\n } catch (error) {\n this.logger.warn({ err: error }, 'Failed to parse payload JSON.');\n payload = rawPayload;\n }\n } else {\n payload = rawPayload;\n }\n }\n\n const targetService = this.findServiceForPath(pathWithoutQuery);\n const services = this.serviceManager.createFacade(userId, scopes, targetService?.definition.name);\n const provider = this.createRequestScope(pathWithoutQuery, targetService);\n\n const context: RequestContext = {\n id: envelope.id,\n path,\n method,\n status: envelope.status ?? 0,\n userId,\n payload,\n body,\n query,\n scopes,\n headers,\n cid: this.env.cid,\n pid: this.env.pid,\n services,\n throwIfCancelled: () => {},\n isCancelled: () => false,\n hasScopes: (...requiredScopes: string[]) => requiredScopes.every((scope) => scopeSetHas(scopes, scope)),\n requireScopes: (...requiredScopes: string[]) => {\n const missingScopes = requiredScopes.filter((scope) => !scopeSetHas(scopes, scope));\n if (missingScopes.length > 0) {\n throw new MissingScopesError(missingScopes);\n }\n },\n provider,\n };\n\n provider.setInstance(REQUEST_CONTEXT_TOKEN, context);\n provider.setInstance(BEAMABLE_SERVICES_TOKEN, services);\n\n return context;\n }\n\n private findServiceForPath(path: string): ServiceInstance | undefined {\n // Gateway sends paths with lowercase service names, so we need case-insensitive matching\n // Match by comparing lowercase versions to handle gateway's lowercase path format\n // Note: path should already have query string stripped before calling this method\n const pathLower = path.toLowerCase();\n return this.services.find((service) => {\n const qualifiedNameLower = service.definition.qualifiedName.toLowerCase();\n return pathLower.startsWith(`${qualifiedNameLower}/`);\n });\n }\n\n /**\n * Parses a query string into a record of parameter names to values.\n * Handles multiple values for the same parameter name by storing them as an array.\n * @param queryString The query string (without the leading '?')\n * @returns A record mapping parameter names to their values (single string or array of strings)\n */\n private parseQueryString(queryString: string): Record<string, string | string[]> {\n if (!queryString || queryString.trim().length === 0) {\n return {};\n }\n\n const params: Record<string, string | string[]> = {};\n const pairs = queryString.split('&');\n\n for (const pair of pairs) {\n const [key, value = ''] = pair.split('=', 2);\n if (key) {\n const decodedKey = decodeURIComponent(key);\n const decodedValue = decodeURIComponent(value);\n\n // If the key already exists, convert to array or append to existing array\n if (decodedKey in params) {\n const existing = params[decodedKey];\n if (Array.isArray(existing)) {\n existing.push(decodedValue);\n } else {\n params[decodedKey] = [existing, decodedValue];\n }\n } else {\n params[decodedKey] = decodedValue;\n }\n }\n }\n\n return params;\n }\n\n private async dispatch(ctx: RequestContext): Promise<void> {\n // Extract path without query string for routing\n const pathWithoutQuery = this.getPathWithoutQuery(ctx.path);\n const service = this.findServiceForPath(pathWithoutQuery);\n if (!service) {\n throw new UnknownRouteError(ctx.path);\n }\n\n if (await this.tryHandleFederationRoute(ctx, service)) {\n return;\n }\n\n if (await this.tryHandleAdminRoute(ctx, service)) {\n return;\n }\n\n // Extract route from path - handle case-insensitive path matching\n const pathLower = pathWithoutQuery.toLowerCase();\n const qualifiedNameLower = service.definition.qualifiedName.toLowerCase();\n const route = pathLower.substring(qualifiedNameLower.length + 1);\n const metadata = service.definition.callables.get(route);\n if (!metadata) {\n throw new UnknownRouteError(ctx.path);\n }\n\n // For server and admin access, allow userId: 0 if the appropriate scope is present\n // For client access, always require userId > 0\n if (metadata.requireAuth && ctx.userId <= 0) {\n if (metadata.access === 'server' && scopeSetHas(ctx.scopes, 'server')) {\n // Server callables with server scope don't need a user ID\n } else if (metadata.access === 'admin' && scopeSetHas(ctx.scopes, 'admin')) {\n // Admin callables with admin scope don't need a user ID\n } else {\n throw new UnauthorizedUserError(route);\n }\n }\n\n enforceAccess(metadata.access, ctx);\n\n if (metadata.requiredScopes.length > 0) {\n const missing = metadata.requiredScopes.filter((scope) => !scopeSetHas(ctx.scopes, scope));\n if (missing.length > 0) {\n throw new MissingScopesError(missing);\n }\n }\n\n const handler = service.instance[metadata.displayName];\n if (typeof handler !== 'function') {\n throw new Error(`Callable ${metadata.displayName} is not a function on service ${service.definition.name}.`);\n }\n\n const args = this.buildInvocationArguments(handler as (...args: unknown[]) => unknown, ctx);\n const result = await Promise.resolve((handler as (...args: unknown[]) => unknown).apply(service.instance, args));\n await this.sendSuccessResponse(ctx, metadata, result);\n }\n\n private buildInvocationArguments(\n handler: (...args: unknown[]) => unknown,\n ctx: RequestContext,\n ): unknown[] {\n const payload = Array.isArray(ctx.payload)\n ? ctx.payload\n : typeof ctx.payload === 'string'\n ? [ctx.payload]\n : ctx.payload === undefined\n ? []\n : [ctx.payload];\n\n const expectedParams = handler.length;\n\n // Determine if RequestContext should be passed as the first argument\n // Strategy: If handler expects more parameters than payload provides,\n // assume the first parameter is RequestContext\n // This covers common cases:\n // - handler(ctx: RequestContext) with empty payload -> expectedParams=1, payload.length=0 -> include ctx\n // - handler(ctx: RequestContext, param: string) with payload=['value'] -> expectedParams=2, payload.length=1 -> include ctx\n // - handler(param: string) with payload=['value'] -> expectedParams=1, payload.length=1 -> don't include ctx\n if (expectedParams > payload.length) {\n return [ctx, ...payload];\n }\n\n // If handler expects exactly the same number of parameters as payload,\n // don't include ctx (handler doesn't expect it)\n return payload;\n }\n\n private async sendSuccessResponse(ctx: RequestContext, metadata: ServiceCallableMetadata, result: unknown): Promise<void> {\n let body: unknown;\n if (metadata.useLegacySerialization) {\n const serialized = typeof result === 'string' ? result : JSON.stringify(result ?? null);\n body = { payload: serialized };\n } else {\n body = result ?? null;\n }\n\n const response: GatewayResponse = {\n id: ctx.id,\n status: 200,\n body,\n };\n await this.requester.sendResponse(response);\n }\n\n private async sendFederationResponse(ctx: RequestContext, result: unknown): Promise<void> {\n const response: GatewayResponse = {\n id: ctx.id,\n status: 200,\n body: result ?? null,\n };\n await this.requester.sendResponse(response);\n }\n\n private async tryHandleFederationRoute(ctx: RequestContext, service: ServiceInstance): Promise<boolean> {\n // Gateway sends paths with lowercase service names, so we need case-insensitive matching\n // Extract path without query string for routing\n const pathWithoutQuery = this.getPathWithoutQuery(ctx.path);\n const pathLower = pathWithoutQuery.toLowerCase();\n const qualifiedNameLower = service.definition.qualifiedName.toLowerCase();\n const prefixLower = `${qualifiedNameLower}/`;\n if (!pathLower.startsWith(prefixLower)) {\n return false;\n }\n // Extract relative path - use lowercase length since gateway sends lowercase paths\n const relativePath = pathWithoutQuery.substring(qualifiedNameLower.length + 1);\n const handler = service.federationRegistry.resolve(relativePath);\n if (!handler) {\n return false;\n }\n\n const result = await handler.invoke(ctx as FederatedRequestContext);\n await this.sendFederationResponse(ctx, result);\n return true;\n }\n\n private async tryHandleAdminRoute(ctx: RequestContext, service: ServiceInstance): Promise<boolean> {\n // Gateway sends paths with lowercase service names, so we need case-insensitive matching\n // Check if path starts with the admin prefix (case-insensitive)\n // Extract path without query string for routing\n const pathWithoutQuery = this.getPathWithoutQuery(ctx.path);\n const pathLower = pathWithoutQuery.toLowerCase();\n const adminPrefixLower = `${service.definition.qualifiedName.toLowerCase()}/admin/`;\n if (!pathLower.startsWith(adminPrefixLower)) {\n return false;\n }\n\n const options = getServiceOptions(service.definition.ctor) ?? {};\n\n const action = pathLower.substring(adminPrefixLower.length);\n const requiresAdmin = action === 'metadata' || action === 'docs';\n if (requiresAdmin && !scopeSetHas(ctx.scopes, 'admin')) {\n // For portal requests to admin endpoints, the gateway may not send scopes\n // The X-DE-SCOPE header contains CID.PID, not scope values\n // If this is a portal request (has X-DE-SCOPE header), grant admin scope\n const hasPortalHeader = ctx.headers['X-DE-SCOPE'] || ctx.headers['x-de-scope'];\n if (hasPortalHeader) {\n // Grant admin scope for portal requests to admin endpoints\n ctx.scopes.add('admin');\n } else {\n throw new MissingScopesError(['admin']);\n }\n }\n\n switch (action) {\n case 'health':\n case 'healthcheck':\n await this.requester.sendResponse({ id: ctx.id, status: 200, body: 'responsive' });\n return true;\n case 'metadata':\n case 'docs':\n break;\n default:\n if (!scopeSetHas(ctx.scopes, 'admin')) {\n throw new MissingScopesError(['admin']);\n }\n throw new UnknownRouteError(ctx.path);\n }\n\n if (action === 'metadata') {\n const federatedComponents = service.federationRegistry\n .list()\n .map((component) => ({\n federationNamespace: component.federationNamespace,\n federationType: component.federationType,\n }));\n\n await this.requester.sendResponse({\n id: ctx.id,\n status: 200,\n body: {\n serviceName: service.definition.name,\n sdkVersion: this.env.sdkVersionExecution,\n sdkBaseBuildVersion: this.env.sdkVersionExecution,\n sdkExecutionVersion: this.env.sdkVersionExecution,\n useLegacySerialization: Boolean(options.useLegacySerialization),\n disableAllBeamableEvents: Boolean(options.disableAllBeamableEvents),\n enableEagerContentLoading: false,\n instanceId: this.microServiceId,\n routingKey: this.env.routingKey ?? '',\n federatedComponents,\n },\n });\n return true;\n }\n\n if (action === 'docs') {\n try {\n const document = generateOpenApiDocument(\n {\n qualifiedName: service.definition.qualifiedName,\n name: service.definition.name,\n callables: Array.from(service.definition.callables.values()).map((callable) => ({\n name: callable.displayName,\n route: callable.route,\n metadata: callable,\n handler: typeof service.instance[callable.displayName] === 'function'\n ? (service.instance[callable.displayName] as (...args: unknown[]) => unknown)\n : undefined,\n })),\n },\n this.env,\n VERSION,\n );\n\n // Ensure document is valid (not empty)\n if (!document || Object.keys(document).length === 0) {\n this.logger.warn({ serviceName: service.definition.name }, 'Generated OpenAPI document is empty');\n }\n\n await this.requester.sendResponse({\n id: ctx.id,\n status: 200,\n body: document,\n });\n return true;\n } catch (error) {\n this.logger.error({ err: error, serviceName: service.definition.name }, 'Failed to generate or send docs');\n throw error;\n }\n }\n\n return false;\n }\n\n private resolveErrorStatus(error: Error): number {\n if (error instanceof UnauthorizedUserError) {\n return 401;\n }\n if (error instanceof MissingScopesError) {\n return 403;\n }\n if (error instanceof UnknownRouteError) {\n return 404;\n }\n if (error instanceof BeamableRuntimeError) {\n return 500;\n }\n return 500;\n }\n\n private printHelpfulUrls(service?: ServiceInstance): void {\n if (!service) {\n return;\n }\n\n // Only print helpful URLs when IS_LOCAL=1 is set\n // In deployed containers, we want only JSON logs for proper log collection\n if (process.env.IS_LOCAL !== '1' && process.env.IS_LOCAL !== 'true') {\n return;\n }\n\n const docsUrl = buildDocsPortalUrl(this.env, service.definition);\n const endpointBase = buildPostmanBaseUrl(this.env, service.definition);\n\n const green = (text: string) => `\\x1b[32m${text}\\x1b[0m`;\n const yellow = (text: string) => `\\x1b[33m${text}\\x1b[0m`;\n\n const bannerLines = [\n '',\n yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'),\n ` ${green('Beamable Node microservice ready:')} ${green(service.definition.name)}`,\n yellow(' Quick shortcuts'),\n ` ${yellow('Docs:')} ${docsUrl}`,\n ` ${yellow('Endpoint:')} ${endpointBase}`,\n yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'),\n '',\n ];\n\n process.stdout.write(`${bannerLines.join('\\n')}`);\n }\n\n private async initializeDependencyProviders(): Promise<void> {\n // Get StorageService from service manager (it's initialized during serviceManager.initialize())\n const storageService = this.serviceManager.getStorageService();\n \n for (const service of this.services) {\n const builder = new DependencyBuilder();\n builder.tryAddSingletonInstance(LOGGER_TOKEN, service.logger);\n builder.tryAddSingletonInstance(ENVIRONMENT_CONFIG_TOKEN, this.env);\n builder.tryAddSingletonInstance(BeamableServiceManager, this.serviceManager);\n builder.tryAddSingletonInstance(StorageService, storageService);\n builder.tryAddSingletonInstance(service.definition.ctor, service.instance);\n builder.tryAddSingletonInstance(FederationRegistry, service.federationRegistry);\n\n for (const handler of service.configureHandlers) {\n await handler(builder);\n }\n\n const provider = builder.build();\n service.provider = provider;\n\n for (const handler of service.initializeHandlers) {\n await handler(provider);\n }\n\n Object.defineProperty(service.instance, 'provider', {\n value: provider,\n enumerable: false,\n configurable: false,\n writable: false,\n });\n }\n }\n\n private createRequestScope(path: string, service?: ServiceInstance): MutableDependencyScope {\n // Path should already have query string stripped before calling this method\n const targetService = service ?? this.findServiceForPath(path);\n const provider = targetService?.provider ?? new DependencyBuilder().build();\n return provider.createScope();\n }\n\n /**\n * Extracts the path portion without the query string.\n * @param path The full path potentially containing a query string\n * @returns The path without the query string\n */\n private getPathWithoutQuery(path: string): string {\n const [pathWithoutQuery] = path.split('?', 2);\n return pathWithoutQuery;\n }\n}\n\nfunction enforceAccess(access: ServiceAccess, ctx: RequestContext): void {\n switch (access) {\n case 'client':\n if (ctx.userId <= 0) {\n throw new UnauthorizedUserError(ctx.path);\n }\n break;\n case 'server':\n if (!scopeSetHas(ctx.scopes, 'server')) {\n throw new MissingScopesError(['server']);\n }\n break;\n case 'admin':\n if (!scopeSetHas(ctx.scopes, 'admin')) {\n throw new MissingScopesError(['admin']);\n }\n break;\n default:\n break;\n }\n}\n\n/**\n * Conditionally output to console.error only when running locally (not in container).\n * In containers, we want only Pino JSON logs to stdout for proper log collection.\n */\nfunction debugLog(...args: unknown[]): void {\n // Only output debug logs when IS_LOCAL=1 is set\n if (process.env.IS_LOCAL === '1' || process.env.IS_LOCAL === 'true') {\n console.error(...args);\n }\n}\n\n/**\n * Determines if we're running in a deployed container.\n * Used for service registration logic (routing key handling, discovery broadcaster, etc.)\n * \n * Note: For log formatting, use IS_LOCAL env var instead.\n */\nfunction isRunningInContainer(): boolean {\n // Check for Docker container\n try {\n const fs = require('fs');\n if (fs.existsSync('/.dockerenv')) {\n return true;\n }\n } catch {\n // fs might not be available\n }\n \n // Check for Docker container hostname pattern (12 hex chars)\n const hostname = process.env.HOSTNAME || '';\n if (hostname && /^[a-f0-9]{12}$/i.test(hostname)) {\n return true;\n }\n \n // Explicit container indicators\n if (\n process.env.DOTNET_RUNNING_IN_CONTAINER === 'true' ||\n process.env.CONTAINER === 'beamable' ||\n !!process.env.ECS_CONTAINER_METADATA_URI ||\n !!process.env.KUBERNETES_SERVICE_HOST\n ) {\n return true;\n }\n \n // Default: assume local development\n return false;\n}\n\nfunction normalizeScopes(scopes: Array<unknown>): Set<string> {\n const normalized = new Set<string>();\n for (const scope of scopes) {\n if (typeof scope !== 'string' || scope.trim().length === 0) {\n continue;\n }\n normalized.add(scope.trim().toLowerCase());\n }\n return normalized;\n}\n\nfunction scopeSetHas(scopes: Set<string>, scope: string): boolean {\n const normalized = scope.trim().toLowerCase();\n if (normalized.length === 0) {\n return false;\n }\n return scopes.has('*') || scopes.has(normalized);\n}\n\nfunction buildPostmanBaseUrl(env: EnvironmentConfig, service: ServiceDefinition): string {\n const httpHost = hostToHttpUrl(env.host);\n const routingKeyPart = env.routingKey ? env.routingKey : '';\n return `${httpHost}/basic/${env.cid}.${env.pid}.${routingKeyPart}${service.qualifiedName}/`;\n}\n\nfunction buildDocsPortalUrl(env: EnvironmentConfig, service: ServiceDefinition): string {\n const portalHost = hostToPortalUrl(hostToHttpUrl(env.host));\n const queryParams: Record<string, string> = { srcTool: 'node-runtime' };\n if (env.routingKey) {\n queryParams.routingKey = env.routingKey;\n }\n const query = new URLSearchParams(queryParams);\n if (env.refreshToken) {\n query.set('refresh_token', env.refreshToken);\n }\n const beamoId = service.name;\n return `${portalHost}/${env.cid}/games/${env.pid}/realms/${env.pid}/microservices/micro_${beamoId}/docs?${query.toString()}`;\n}\n\nexport async function runMicroservice(): Promise<void> {\n // Immediate console output to verify process is starting (local dev only)\n // In containers, we rely on Pino logger for all output\n debugLog('[BEAMABLE-NODE] Starting microservice...');\n debugLog(`[BEAMABLE-NODE] Node version: ${process.version}`);\n debugLog(`[BEAMABLE-NODE] Working directory: ${process.cwd()}`);\n debugLog(`[BEAMABLE-NODE] Environment: ${JSON.stringify({\n NODE_ENV: process.env.NODE_ENV,\n CID: process.env.CID ? 'SET' : 'NOT SET',\n PID: process.env.PID ? 'SET' : 'NOT SET',\n HOST: process.env.HOST ? 'SET' : 'NOT SET',\n SECRET: process.env.SECRET ? 'SET' : 'NOT SET',\n })}`);\n if (process.env.BEAMABLE_SKIP_RUNTIME === 'true') {\n return;\n }\n const runtime = new MicroserviceRuntime();\n \n // Handle uncaught errors - log them but don't crash immediately\n // This allows the health check server to keep running so we can debug\n // In containers, errors will be logged by the logger in the runtime\n // Locally, use console.error for visibility\n process.on('uncaughtException', (error) => {\n // In containers, the logger will handle this; locally, show in console\n debugLog('Uncaught Exception:', error);\n // Don't exit - let the health check server keep running\n });\n \n process.on('unhandledRejection', (reason, promise) => {\n // In containers, the logger will handle this; locally, show in console\n debugLog('Unhandled Rejection at:', promise, 'reason:', reason);\n // Don't exit - let the health check server keep running\n });\n \n try {\n await runtime.start();\n } catch (error) {\n // Log the error but don't exit - health check server is running\n // This allows the container to stay alive so we can see what went wrong\n // In containers, the logger already logged it in start(); locally, also use console.error\n debugLog('Failed to start microservice runtime:', error);\n // Keep the process alive so health check can continue\n // The health check will return 503 until isReady is true\n }\n \n const shutdownSignals: NodeJS.Signals[] = ['SIGTERM', 'SIGINT'];\n shutdownSignals.forEach((signal) => {\n process.once(signal, async () => {\n await runtime.shutdown();\n process.exit(0);\n });\n });\n}\n\ninterface GenerateOpenApiOptions {\n serviceName?: string;\n}\n\nexport function generateOpenApiDocumentForRegisteredServices(\n overrides: Partial<EnvironmentConfig> = {},\n options: GenerateOpenApiOptions = {},\n): unknown {\n const services = listRegisteredServices();\n if (services.length === 0) {\n throw new Error('No microservices registered. Import your service module before generating documentation.');\n }\n\n const baseEnv = buildEnvironmentConfig(overrides);\n const primary = options.serviceName\n ? services.find((svc) => svc.name === options.serviceName || svc.qualifiedName === options.serviceName)\n : services[0];\n\n if (!primary) {\n throw new Error(`No registered microservice matched '${options.serviceName}'.`);\n }\n\n return generateOpenApiDocument(\n {\n qualifiedName: primary.qualifiedName,\n name: primary.name,\n callables: Array.from(primary.callables.entries()).map(([displayName, metadata]) => ({\n name: displayName,\n route: metadata.route,\n metadata,\n handler: undefined,\n })),\n },\n baseEnv,\n VERSION,\n );\n}\n\nfunction buildEnvironmentConfig(overrides: Partial<EnvironmentConfig>): EnvironmentConfig {\n const merged: Partial<EnvironmentConfig> = { ...overrides };\n\n const ensure = (key: keyof EnvironmentConfig, fallbackEnv?: string) => {\n if (merged[key] !== undefined) {\n return;\n }\n if (fallbackEnv && process.env[fallbackEnv]) {\n merged[key] = process.env[fallbackEnv] as never;\n }\n };\n\n ensure('cid', 'CID');\n ensure('pid', 'PID');\n ensure('host', 'HOST');\n ensure('secret', 'SECRET');\n ensure('refreshToken', 'REFRESH_TOKEN');\n // Routing key is optional for deployed services (in containers)\n // It will be resolved to empty string if not provided and running in container\n if (!merged.routingKey && !isRunningInContainer()) {\n ensure('routingKey', 'NAME_PREFIX');\n }\n\n if (!merged.cid || !merged.pid || !merged.host) {\n throw new Error('CID, PID, and HOST are required to generate documentation.');\n }\n\n const base = loadEnvironmentConfig();\n\n return {\n ...base,\n ...merged,\n routingKey: merged.routingKey ?? base.routingKey,\n };\n}\n"]}
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from 'node:crypto';\nimport { BeamableWebSocket } from './websocket.js';\nimport { GatewayRequester } from './requester.js';\nimport { AuthManager } from './auth.js';\nimport { createLogger } from './logger.js';\nimport { loadEnvironmentConfig } from './env.js';\nimport { loadAndInjectEnvironmentVariables, loadDeveloperEnvVarsSync } from './env-loader.js';\nimport { startCollectorAndWaitForReady } from './collector-manager.js';\nimport pino from 'pino';\n// Removed deasync - using non-blocking async pattern instead\nimport { listRegisteredServices, getServiceOptions, getConfigureServicesHandlers, getInitializeServicesHandlers } from './decorators.js';\nimport { generateOpenApiDocument } from './docs.js';\nimport { VERSION } from './index.js';\nimport { DiscoveryBroadcaster } from './discovery.js';\nimport { BeamableRuntimeError, MissingScopesError, UnauthorizedUserError, UnknownRouteError } from './errors.js';\nimport type {\n EnvironmentConfig,\n RequestContext,\n ServiceDefinition,\n ServiceCallableMetadata,\n GatewayResponse,\n WebsocketEventEnvelope,\n ServiceAccess,\n} from './types.js';\nimport type { Logger } from 'pino';\nimport { BeamableServiceManager } from './services.js';\nimport { StorageService } from './storage.js';\nimport {\n DependencyBuilder,\n LOGGER_TOKEN,\n ENVIRONMENT_CONFIG_TOKEN,\n REQUEST_CONTEXT_TOKEN,\n BEAMABLE_SERVICES_TOKEN,\n ServiceProvider,\n MutableDependencyScope,\n} from './dependency.js';\nimport { hostToHttpUrl, hostToPortalUrl } from './utils/urls.js';\nimport { FederationRegistry, getFederationComponents, getFederatedInventoryMetadata } from './federation.js';\nimport type { FederatedRequestContext } from './federation.js';\nimport { createServer, type Server } from 'node:http';\n\ninterface ServiceInstance {\n definition: ServiceDefinition;\n instance: Record<string, unknown>;\n configureHandlers: ReturnType<typeof getConfigureServicesHandlers>;\n initializeHandlers: ReturnType<typeof getInitializeServicesHandlers>;\n provider?: ServiceProvider;\n logger: Logger;\n federationRegistry: FederationRegistry;\n}\n\nexport class MicroserviceRuntime {\n private readonly env: EnvironmentConfig;\n private logger: Logger; // Mutable to allow upgrading from console logger to structured logger when collector is ready\n private readonly services: ServiceInstance[];\n private readonly webSocket: BeamableWebSocket;\n private readonly requester: GatewayRequester;\n private readonly authManager: AuthManager;\n private readonly discovery?: DiscoveryBroadcaster;\n private readonly microServiceId = randomUUID();\n private readonly serviceManager: BeamableServiceManager;\n private healthCheckServer?: Server;\n private isReady: boolean = false;\n /** False until DI container is built; traffic is rejected with 503 while isReady may already be true. */\n private dependencyProvidersReady: boolean = false;\n\n constructor(env?: EnvironmentConfig) {\n this.env = env ?? loadEnvironmentConfig();\n const envConfig = this.env; // Capture for async IIFE to satisfy TypeScript\n \n // STEP 1: Load developer-defined environment variables SYNCHRONOUSLY before logger creation\n // This ensures BetterStack and other env vars are available when the logger initializes\n loadDeveloperEnvVarsSync();\n \n // STEP 2: Create minimal console logger for startup messages (before collector setup)\n // This ensures we have logging available immediately, even before collector is ready\n const startupLogger = pino({\n name: 'beamable-runtime-startup',\n level: 'info',\n }, process.stdout);\n // Display runtime version at startup (VERSION is imported synchronously at top of file)\n startupLogger.info(`Starting Beamable Node microservice runtime (version: ${VERSION}).`);\n \n // STEP 2.5: Load Beamable Config asynchronously (in background, non-blocking)\n // Developer-defined vars are already loaded synchronously above\n // Beamable Config is fetched via API with a 2-second timeout\n (async () => {\n try {\n startupLogger.info('Loading Beamable Config environment variables...');\n await loadAndInjectEnvironmentVariables(envConfig, undefined, true, 2000);\n startupLogger.info('Beamable Config loading completed');\n } catch (error) {\n startupLogger.warn(`Beamable Config loading completed with warnings: ${error instanceof Error ? error.message : String(error)}`);\n }\n })();\n \n // STEP 2: Get registered services to extract service name\n const registered = listRegisteredServices();\n if (registered.length === 0) {\n throw new Error('No microservices registered. Use the @Microservice decorator to register at least one class.');\n }\n \n // Use the first service's name for the main logger (for CloudWatch filtering and ClickHouse compatibility)\n const primaryService = registered[0];\n const qualifiedServiceName = `micro_${primaryService.qualifiedName}`;\n \n // STEP 3: Create logger immediately (non-blocking pattern, matching C#)\n // Start with minimal console logger, upgrade to structured logger when collector is ready\n // This allows the service to start immediately without blocking\n startupLogger.info('Setting up OpenTelemetry collector in background (non-blocking)...');\n \n // Create logger immediately with console output (no OTLP yet)\n // This ensures we have logging available right away\n this.logger = createLogger(this.env, {\n name: 'beamable-node-microservice',\n serviceName: primaryService.name,\n qualifiedServiceName: qualifiedServiceName,\n // No otlpEndpoint - will use console logger initially\n });\n \n // STEP 4: Start collector setup in background (non-blocking)\n // When collector is ready, upgrade logger to structured logger with OTLP\n // This matches C# pattern: collector starts in background, service starts immediately\n startCollectorAndWaitForReady(this.env)\n .then((endpoint) => {\n if (endpoint) {\n // Collector is ready - upgrade to structured logger with OTLP support\n this.logger.info(`Collector ready at ${endpoint}, upgrading to structured logger for Portal logs...`);\n this.logger = createLogger(this.env, {\n name: 'beamable-node-microservice',\n serviceName: primaryService.name,\n qualifiedServiceName: qualifiedServiceName,\n otlpEndpoint: endpoint, // Collector is ready, Portal logs will now work\n });\n this.logger.info('Portal logs enabled - structured logs will now appear in Beamable Portal');\n } else {\n this.logger.warn('Collector setup completed but no endpoint was returned. Continuing with console logs. Portal logs will not be available.');\n }\n })\n .catch((error) => {\n const errorMsg = error instanceof Error ? error.message : String(error);\n this.logger.error(`Failed to setup collector: ${errorMsg}. Continuing with console logs. Portal logs will not be available.`);\n // Service continues to work with console logger - graceful degradation\n });\n \n // Continue immediately - don't wait for collector!\n // Service can start accepting requests right away\n // Collector setup happens in background, logger upgrades automatically when ready\n this.serviceManager = new BeamableServiceManager(this.env, this.logger);\n\n this.services = registered.map((definition) => {\n const instance = new definition.ctor() as Record<string, unknown>;\n const configureHandlers = getConfigureServicesHandlers(definition.ctor);\n const initializeHandlers = getInitializeServicesHandlers(definition.ctor);\n const logger = this.logger.child({ service: definition.name });\n const federationRegistry = new FederationRegistry(logger);\n const decoratedFederations = getFederationComponents(definition.ctor);\n for (const component of decoratedFederations) {\n federationRegistry.register(component);\n }\n const inventoryMetadata = getFederatedInventoryMetadata(definition.ctor);\n if (inventoryMetadata.length > 0) {\n federationRegistry.registerInventoryHandlers(instance, inventoryMetadata);\n }\n this.serviceManager.registerFederationRegistry(definition.name, federationRegistry);\n return { definition, instance, configureHandlers, initializeHandlers, logger, federationRegistry };\n });\n\n const socketUrl = this.env.host.endsWith('/socket') ? this.env.host : `${this.env.host}/socket`;\n this.webSocket = new BeamableWebSocket({ url: socketUrl, logger: this.logger });\n this.webSocket.on('message', (payload) => {\n this.logger.debug({ payload }, 'Runtime observed websocket frame.');\n });\n this.requester = new GatewayRequester(this.webSocket, this.logger);\n this.authManager = new AuthManager(this.env, this.requester);\n this.requester.on('event', (envelope) => this.handleEvent(envelope));\n\n // Discovery broadcaster only runs in local development (not in containers)\n // This allows the portal to detect that the service is running locally\n if (!isRunningInContainer() && this.services.length > 0) {\n this.discovery = new DiscoveryBroadcaster({\n env: this.env,\n serviceName: this.services[0].definition.name,\n routingKey: this.env.routingKey,\n logger: this.logger.child({ component: 'DiscoveryBroadcaster' }),\n });\n }\n }\n\n async start(): Promise<void> {\n // Immediate console output for container logs (before logger is ready)\n debugLog('[BEAMABLE-NODE] MicroserviceRuntime.start() called');\n debugLog(`[BEAMABLE-NODE] Service count: ${this.services.length}`);\n \n this.printHelpfulUrls(this.services[0]);\n this.logger.info('Starting Beamable Node microservice runtime.');\n \n // Start health check server FIRST - this is critical for container health checks\n // Even if startup fails, the health check server must be running so we can debug\n debugLog('[BEAMABLE-NODE] Starting health check server...');\n await this.startHealthCheckServer();\n debugLog('[BEAMABLE-NODE] Health check server started');\n \n try {\n this.logger.info('Connecting to Beamable gateway...');\n await this.webSocket.connect();\n await new Promise((resolve) => setTimeout(resolve, 250));\n \n this.logger.info('Authenticating with Beamable...');\n await this.authManager.authenticate();\n \n this.logger.info('Initializing Beamable SDK services...');\n // Pass service name to initialize so routing key can be configured for service-level requests\n const primaryServiceName = this.services[0]?.definition.name;\n await this.serviceManager.initialize(primaryServiceName);\n\n this.dependencyProvidersReady = false;\n\n // Register with gateway before building the app DI graph so GET /health can go 200 while\n // ConfigureServices/build still run (large graphs no longer block Portal probes).\n this.logger.info('Registering basic service provider with gateway...');\n await this.registerBasicServiceProvider();\n\n this.isReady = true;\n this.logger.info('Beamable microservice runtime is ready to accept traffic.');\n\n this.logger.info('Initializing dependency providers...');\n await this.initializeDependencyProviders();\n this.dependencyProvidersReady = true;\n\n this.logger.info('Registering event provider with gateway...');\n await this.registerEventServiceProvider();\n\n this.logger.info('Starting discovery broadcaster...');\n await this.discovery?.start();\n } catch (error) {\n // Log the error with full context but don't crash - health check server is running\n // This allows the container to stay alive so we can debug the issue\n // Debug output for local development only (in containers, logger handles this)\n debugLog('[BEAMABLE-NODE] FATAL ERROR during startup:');\n debugLog(`[BEAMABLE-NODE] Error message: ${error instanceof Error ? error.message : String(error)}`);\n debugLog(`[BEAMABLE-NODE] Error stack: ${error instanceof Error ? error.stack : 'No stack trace'}`);\n debugLog(`[BEAMABLE-NODE] isReady: ${this.isReady}`);\n \n this.logger.error(\n { \n err: error,\n errorMessage: error instanceof Error ? error.message : String(error),\n errorStack: error instanceof Error ? error.stack : undefined,\n isReady: this.isReady,\n },\n 'Failed to fully initialize microservice runtime. Health check will continue to return 503 until initialization completes.'\n );\n // DON'T re-throw - keep process alive so health check can show 503\n // This allows us to see the error in logs\n this.isReady = false;\n this.dependencyProvidersReady = false;\n }\n }\n\n async shutdown(): Promise<void> {\n this.logger.info('Shutting down microservice runtime.');\n this.isReady = false; // Mark as not ready during shutdown\n this.dependencyProvidersReady = false;\n this.discovery?.stop();\n await this.stopHealthCheckServer();\n this.requester.dispose();\n await this.webSocket.close();\n }\n\n private async startHealthCheckServer(): Promise<void> {\n // For deployed services, always start health check server (required for container health checks)\n // For local development, only start if healthPort is explicitly set\n // IMPORTANT: Always default to 6565 if HEALTH_PORT env var is not set, as this is the standard port\n const healthPort = this.env.healthPort || 6565;\n \n // Always start health check server if we have a valid port\n // The container orchestrator expects this endpoint to be available\n if (!healthPort || healthPort === 0) {\n // Health check server not needed (local development without explicit port)\n this.logger.debug('Health check server skipped (no healthPort set)');\n return;\n }\n\n this.healthCheckServer = createServer((req, res) => {\n if (req.url === '/health' && req.method === 'GET') {\n // Only return success if service is fully ready (registered and accepting traffic)\n if (this.isReady) {\n res.writeHead(200, { 'Content-Type': 'text/plain' });\n res.end('responsive');\n } else {\n // Service is still starting up\n res.writeHead(503, { 'Content-Type': 'text/plain' });\n res.end('Service Unavailable');\n }\n } else {\n res.writeHead(404, { 'Content-Type': 'text/plain' });\n res.end('Not Found');\n }\n });\n\n return new Promise((resolve, reject) => {\n this.healthCheckServer!.listen(healthPort, '0.0.0.0', () => {\n this.logger.info({ port: healthPort }, 'Health check server started on port');\n resolve();\n });\n this.healthCheckServer!.on('error', (err) => {\n this.logger.error({ err, port: healthPort }, 'Failed to start health check server');\n reject(err);\n });\n });\n }\n\n private async stopHealthCheckServer(): Promise<void> {\n if (!this.healthCheckServer) {\n return;\n }\n\n return new Promise((resolve) => {\n this.healthCheckServer!.close(() => {\n this.logger.info('Health check server stopped');\n resolve();\n });\n });\n }\n\n /**\n * POST basic provider to gateway. In start(), isReady is set immediately after this succeeds,\n * before dependency providers are built; traffic is gated until dependencyProvidersReady.\n */\n private async registerBasicServiceProvider(): Promise<void> {\n const primary = this.services[0]?.definition;\n if (!primary) {\n throw new Error('Unexpected missing service definition during registration.');\n }\n // Match C# exactly: use qualifiedName (preserves case) for name field.\n // The gateway lowercases service names when creating bindings, but uses the original case\n // from the registration request when constructing routing key lookups.\n // This ensures the routing key format matches what the gateway expects.\n // The gateway's binding lookup behavior:\n // - Gateway error shows: \"No binding found for service ...micro_examplenodeservice.basic\"\n // - This means the gateway lowercases the service name for binding storage/lookup\n // - Portal sends requests with mixed case in URL and routing key header\n // - The gateway lowercases the URL path for binding lookup, which should work\n // - But we need to register with the format the gateway expects for the binding key\n // - The gateway constructs binding key as: {cid}.{pid}.{lowercaseServiceName}.{type}\n // - So we register with lowercase to match what the gateway stores in the binding\n // - The portal will still send mixed case, and the gateway will lowercase it for lookup\n // Register with mixed-case qualifiedName to match C# behavior\n // The gateway will lowercase the service name when creating the binding key,\n // but the registration request should use the original case (as C# does)\n // This ensures the service name in the registration matches what the portal expects\n // Register with mixed-case qualifiedName to match C# behavior\n // The gateway's ServiceIdentity.fullNameNoType lowercases the service name when creating bindings,\n // but the registration request should use the original case (as C# does)\n // This ensures the service name in the registration matches what the portal expects\n const isDeployed = isRunningInContainer();\n \n // For deployed services, routingKey should be null/undefined (None in Scala)\n // For local dev, routingKey should be the actual routing key string\n // The backend expects Option[String] = None for deployed services\n // Note: microServiceId is not part of SocketSessionProviderRegisterRequest, so we don't include it\n // All fields except 'type' are Option[T] in Scala, so undefined fields will be omitted from JSON\n // This matches C# behavior where null/None fields are not serialized\n const request: Record<string, unknown> = {\n type: 'basic',\n name: primary.qualifiedName, // Use mixed-case as C# does - gateway handles lowercasing for binding storage\n beamoName: primary.name,\n };\n \n // Only include routingKey and startedById if they have values (for local dev)\n // For deployed services, these should be omitted (undefined) to match None in Scala\n if (!isDeployed && this.env.routingKey) {\n request.routingKey = this.env.routingKey;\n }\n if (!isDeployed && this.env.accountId) {\n request.startedById = this.env.accountId;\n }\n\n // Log registration request to match C# behavior exactly\n // Also log the actual JSON that will be sent (undefined fields will be omitted)\n const serializedRequest = JSON.stringify(request);\n this.logger.debug(\n {\n request: {\n type: request.type,\n name: request.name,\n beamoName: request.beamoName,\n routingKey: request.routingKey,\n startedById: request.startedById,\n },\n serializedJson: serializedRequest,\n isDeployed,\n },\n 'Registering service provider with gateway.',\n );\n\n try {\n await this.requester.request('post', 'gateway/provider', request);\n this.logger.info({ serviceName: primary.qualifiedName }, 'Service provider registered successfully.');\n // Former 2s deployed-only await blocked isReady and Portal \"Deploying\"; gateway still runs\n // binding setup asynchronously — health can go green while that finishes.\n if (isDeployed) {\n this.logger.debug(\n 'Gateway binding setup continues asynchronously (no blocking sleep before health readiness).',\n );\n }\n } catch (error) {\n this.logger.error(\n {\n err: error,\n request,\n serviceName: primary.qualifiedName,\n errorMessage: error instanceof Error ? error.message : String(error),\n },\n 'Failed to register service provider with gateway. This will prevent the service from receiving requests.'\n );\n throw error; // Re-throw so startup fails and we can see the error\n }\n }\n\n private async registerEventServiceProvider(): Promise<void> {\n const primary = this.services[0]?.definition;\n if (!primary) {\n return;\n }\n const options = getServiceOptions(primary.ctor) ?? {};\n if (options.disableAllBeamableEvents) {\n this.logger.info('Beamable events disabled by configuration.');\n return;\n }\n const eventRequest = {\n type: 'event',\n evtWhitelist: ['content.manifest', 'realm.config', 'logging.context'],\n };\n try {\n await this.requester.request('post', 'gateway/provider', eventRequest);\n } catch (error) {\n this.logger.warn({ err: error }, 'Failed to register event provider. Continuing without events.');\n }\n }\n\n private async handleEvent(envelope: WebsocketEventEnvelope): Promise<void> {\n try {\n if (!envelope.path) {\n this.logger.debug({ envelope }, 'Ignoring websocket event without path.');\n return;\n }\n\n if (envelope.path.startsWith('event/')) {\n await this.requester.acknowledge(envelope.id);\n return;\n }\n\n // Avoid dispatch with an empty DI scope while ConfigureServices/build is still running.\n if (!this.dependencyProvidersReady) {\n await this.requester.sendResponse({\n id: envelope.id,\n status: 503,\n body: {\n error: 'ServiceUnavailable',\n message: 'Microservice dependency providers are still initializing.',\n },\n });\n return;\n }\n\n const context = this.toRequestContext(envelope);\n await this.dispatch(context);\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n this.logger.error({ err, envelope }, 'Failed to handle websocket event.');\n const status = this.resolveErrorStatus(err);\n const response: GatewayResponse = {\n id: envelope.id,\n status,\n body: {\n error: err.name,\n message: err.message,\n },\n };\n await this.requester.sendResponse(response);\n }\n }\n\n private toRequestContext(envelope: WebsocketEventEnvelope): RequestContext {\n const path = envelope.path ?? '';\n const method = envelope.method ?? 'post';\n const userId = typeof envelope.from === 'number' ? envelope.from : 0;\n const headers = envelope.headers ?? {};\n \n // Parse query parameters from path (e.g., /service/route?param1=value1&param2=value2)\n const [pathWithoutQuery, queryString] = path.split('?', 2);\n const query = this.parseQueryString(queryString ?? '');\n \n // Extract scopes from envelope.scopes array\n // Note: X-DE-SCOPE header contains CID.PID, not scope values\n // The gateway sends scopes in envelope.scopes array\n // For admin endpoints, the gateway may not send scopes in the envelope,\n // so we infer admin scope from the path if it's an admin route\n const envelopeScopes = envelope.scopes ?? [];\n let scopes = normalizeScopes(envelopeScopes);\n \n // If this is an admin endpoint and no scopes are provided, infer admin scope\n // The gateway may not always send scopes for admin routes\n const pathLower = pathWithoutQuery.toLowerCase();\n if (pathLower.includes('/admin/') && scopes.size === 0) {\n scopes = normalizeScopes(['admin']);\n }\n\n let body: Record<string, unknown> | undefined;\n if (envelope.body && typeof envelope.body === 'string') {\n try {\n body = JSON.parse(envelope.body) as Record<string, unknown>;\n } catch (error) {\n this.logger.warn({ err: error, raw: envelope.body }, 'Failed to parse body string.');\n }\n } else if (envelope.body && typeof envelope.body === 'object') {\n body = envelope.body as Record<string, unknown>;\n }\n\n let payload: unknown;\n if (body && typeof body === 'object' && 'payload' in body) {\n const rawPayload = (body as Record<string, unknown>).payload;\n if (typeof rawPayload === 'string') {\n try {\n payload = JSON.parse(rawPayload) as unknown[];\n } catch (error) {\n this.logger.warn({ err: error }, 'Failed to parse payload JSON.');\n payload = rawPayload;\n }\n } else {\n payload = rawPayload;\n }\n }\n\n const targetService = this.findServiceForPath(pathWithoutQuery);\n const services = this.serviceManager.createFacade(userId, scopes, targetService?.definition.name);\n const provider = this.createRequestScope(pathWithoutQuery, targetService);\n\n const context: RequestContext = {\n id: envelope.id,\n path,\n method,\n status: envelope.status ?? 0,\n userId,\n payload,\n body,\n query,\n scopes,\n headers,\n cid: this.env.cid,\n pid: this.env.pid,\n services,\n throwIfCancelled: () => {},\n isCancelled: () => false,\n hasScopes: (...requiredScopes: string[]) => requiredScopes.every((scope) => scopeSetHas(scopes, scope)),\n requireScopes: (...requiredScopes: string[]) => {\n const missingScopes = requiredScopes.filter((scope) => !scopeSetHas(scopes, scope));\n if (missingScopes.length > 0) {\n throw new MissingScopesError(missingScopes);\n }\n },\n provider,\n };\n\n provider.setInstance(REQUEST_CONTEXT_TOKEN, context);\n provider.setInstance(BEAMABLE_SERVICES_TOKEN, services);\n\n return context;\n }\n\n private findServiceForPath(path: string): ServiceInstance | undefined {\n // Gateway sends paths with lowercase service names, so we need case-insensitive matching\n // Match by comparing lowercase versions to handle gateway's lowercase path format\n // Note: path should already have query string stripped before calling this method\n const pathLower = path.toLowerCase();\n return this.services.find((service) => {\n const qualifiedNameLower = service.definition.qualifiedName.toLowerCase();\n return pathLower.startsWith(`${qualifiedNameLower}/`);\n });\n }\n\n /**\n * Parses a query string into a record of parameter names to values.\n * Handles multiple values for the same parameter name by storing them as an array.\n * @param queryString The query string (without the leading '?')\n * @returns A record mapping parameter names to their values (single string or array of strings)\n */\n private parseQueryString(queryString: string): Record<string, string | string[]> {\n if (!queryString || queryString.trim().length === 0) {\n return {};\n }\n\n const params: Record<string, string | string[]> = {};\n const pairs = queryString.split('&');\n\n for (const pair of pairs) {\n const [key, value = ''] = pair.split('=', 2);\n if (key) {\n const decodedKey = decodeURIComponent(key);\n const decodedValue = decodeURIComponent(value);\n\n // If the key already exists, convert to array or append to existing array\n if (decodedKey in params) {\n const existing = params[decodedKey];\n if (Array.isArray(existing)) {\n existing.push(decodedValue);\n } else {\n params[decodedKey] = [existing, decodedValue];\n }\n } else {\n params[decodedKey] = decodedValue;\n }\n }\n }\n\n return params;\n }\n\n private async dispatch(ctx: RequestContext): Promise<void> {\n // Extract path without query string for routing\n const pathWithoutQuery = this.getPathWithoutQuery(ctx.path);\n const service = this.findServiceForPath(pathWithoutQuery);\n if (!service) {\n throw new UnknownRouteError(ctx.path);\n }\n\n if (await this.tryHandleFederationRoute(ctx, service)) {\n return;\n }\n\n if (await this.tryHandleAdminRoute(ctx, service)) {\n return;\n }\n\n // Extract route from path - handle case-insensitive path matching\n const pathLower = pathWithoutQuery.toLowerCase();\n const qualifiedNameLower = service.definition.qualifiedName.toLowerCase();\n const route = pathLower.substring(qualifiedNameLower.length + 1);\n const metadata = service.definition.callables.get(route);\n if (!metadata) {\n throw new UnknownRouteError(ctx.path);\n }\n\n // For server and admin access, allow userId: 0 if the appropriate scope is present\n // For client access, always require userId > 0\n if (metadata.requireAuth && ctx.userId <= 0) {\n if (metadata.access === 'server' && scopeSetHas(ctx.scopes, 'server')) {\n // Server callables with server scope don't need a user ID\n } else if (metadata.access === 'admin' && scopeSetHas(ctx.scopes, 'admin')) {\n // Admin callables with admin scope don't need a user ID\n } else {\n throw new UnauthorizedUserError(route);\n }\n }\n\n enforceAccess(metadata.access, ctx);\n\n if (metadata.requiredScopes.length > 0) {\n const missing = metadata.requiredScopes.filter((scope) => !scopeSetHas(ctx.scopes, scope));\n if (missing.length > 0) {\n throw new MissingScopesError(missing);\n }\n }\n\n const handler = service.instance[metadata.displayName];\n if (typeof handler !== 'function') {\n throw new Error(`Callable ${metadata.displayName} is not a function on service ${service.definition.name}.`);\n }\n\n const args = this.buildInvocationArguments(handler as (...args: unknown[]) => unknown, ctx);\n const result = await Promise.resolve((handler as (...args: unknown[]) => unknown).apply(service.instance, args));\n await this.sendSuccessResponse(ctx, metadata, result);\n }\n\n private buildInvocationArguments(\n handler: (...args: unknown[]) => unknown,\n ctx: RequestContext,\n ): unknown[] {\n const payload = Array.isArray(ctx.payload)\n ? ctx.payload\n : typeof ctx.payload === 'string'\n ? [ctx.payload]\n : ctx.payload === undefined\n ? []\n : [ctx.payload];\n\n const expectedParams = handler.length;\n\n // Determine if RequestContext should be passed as the first argument\n // Strategy: If handler expects more parameters than payload provides,\n // assume the first parameter is RequestContext\n // This covers common cases:\n // - handler(ctx: RequestContext) with empty payload -> expectedParams=1, payload.length=0 -> include ctx\n // - handler(ctx: RequestContext, param: string) with payload=['value'] -> expectedParams=2, payload.length=1 -> include ctx\n // - handler(param: string) with payload=['value'] -> expectedParams=1, payload.length=1 -> don't include ctx\n if (expectedParams > payload.length) {\n return [ctx, ...payload];\n }\n\n // If handler expects exactly the same number of parameters as payload,\n // don't include ctx (handler doesn't expect it)\n return payload;\n }\n\n private async sendSuccessResponse(ctx: RequestContext, metadata: ServiceCallableMetadata, result: unknown): Promise<void> {\n let body: unknown;\n if (metadata.useLegacySerialization) {\n const serialized = typeof result === 'string' ? result : JSON.stringify(result ?? null);\n body = { payload: serialized };\n } else {\n body = result ?? null;\n }\n\n const response: GatewayResponse = {\n id: ctx.id,\n status: 200,\n body,\n };\n await this.requester.sendResponse(response);\n }\n\n private async sendFederationResponse(ctx: RequestContext, result: unknown): Promise<void> {\n const response: GatewayResponse = {\n id: ctx.id,\n status: 200,\n body: result ?? null,\n };\n await this.requester.sendResponse(response);\n }\n\n private async tryHandleFederationRoute(ctx: RequestContext, service: ServiceInstance): Promise<boolean> {\n // Gateway sends paths with lowercase service names, so we need case-insensitive matching\n // Extract path without query string for routing\n const pathWithoutQuery = this.getPathWithoutQuery(ctx.path);\n const pathLower = pathWithoutQuery.toLowerCase();\n const qualifiedNameLower = service.definition.qualifiedName.toLowerCase();\n const prefixLower = `${qualifiedNameLower}/`;\n if (!pathLower.startsWith(prefixLower)) {\n return false;\n }\n // Extract relative path - use lowercase length since gateway sends lowercase paths\n const relativePath = pathWithoutQuery.substring(qualifiedNameLower.length + 1);\n const handler = service.federationRegistry.resolve(relativePath);\n if (!handler) {\n return false;\n }\n\n const result = await handler.invoke(ctx as FederatedRequestContext);\n await this.sendFederationResponse(ctx, result);\n return true;\n }\n\n private async tryHandleAdminRoute(ctx: RequestContext, service: ServiceInstance): Promise<boolean> {\n // Gateway sends paths with lowercase service names, so we need case-insensitive matching\n // Check if path starts with the admin prefix (case-insensitive)\n // Extract path without query string for routing\n const pathWithoutQuery = this.getPathWithoutQuery(ctx.path);\n const pathLower = pathWithoutQuery.toLowerCase();\n const adminPrefixLower = `${service.definition.qualifiedName.toLowerCase()}/admin/`;\n if (!pathLower.startsWith(adminPrefixLower)) {\n return false;\n }\n\n const options = getServiceOptions(service.definition.ctor) ?? {};\n\n const action = pathLower.substring(adminPrefixLower.length);\n const requiresAdmin = action === 'metadata' || action === 'docs';\n if (requiresAdmin && !scopeSetHas(ctx.scopes, 'admin')) {\n // For portal requests to admin endpoints, the gateway may not send scopes\n // The X-DE-SCOPE header contains CID.PID, not scope values\n // If this is a portal request (has X-DE-SCOPE header), grant admin scope\n const hasPortalHeader = ctx.headers['X-DE-SCOPE'] || ctx.headers['x-de-scope'];\n if (hasPortalHeader) {\n // Grant admin scope for portal requests to admin endpoints\n ctx.scopes.add('admin');\n } else {\n throw new MissingScopesError(['admin']);\n }\n }\n\n switch (action) {\n case 'health':\n case 'healthcheck':\n await this.requester.sendResponse({ id: ctx.id, status: 200, body: 'responsive' });\n return true;\n case 'metadata':\n case 'docs':\n break;\n default:\n if (!scopeSetHas(ctx.scopes, 'admin')) {\n throw new MissingScopesError(['admin']);\n }\n throw new UnknownRouteError(ctx.path);\n }\n\n if (action === 'metadata') {\n const federatedComponents = service.federationRegistry\n .list()\n .map((component) => ({\n federationNamespace: component.federationNamespace,\n federationType: component.federationType,\n }));\n\n await this.requester.sendResponse({\n id: ctx.id,\n status: 200,\n body: {\n serviceName: service.definition.name,\n sdkVersion: this.env.sdkVersionExecution,\n sdkBaseBuildVersion: this.env.sdkVersionExecution,\n sdkExecutionVersion: this.env.sdkVersionExecution,\n useLegacySerialization: Boolean(options.useLegacySerialization),\n disableAllBeamableEvents: Boolean(options.disableAllBeamableEvents),\n enableEagerContentLoading: false,\n instanceId: this.microServiceId,\n routingKey: this.env.routingKey ?? '',\n federatedComponents,\n },\n });\n return true;\n }\n\n if (action === 'docs') {\n try {\n const document = generateOpenApiDocument(\n {\n qualifiedName: service.definition.qualifiedName,\n name: service.definition.name,\n callables: Array.from(service.definition.callables.values()).map((callable) => ({\n name: callable.displayName,\n route: callable.route,\n metadata: callable,\n handler: typeof service.instance[callable.displayName] === 'function'\n ? (service.instance[callable.displayName] as (...args: unknown[]) => unknown)\n : undefined,\n })),\n },\n this.env,\n VERSION,\n );\n\n // Ensure document is valid (not empty)\n if (!document || Object.keys(document).length === 0) {\n this.logger.warn({ serviceName: service.definition.name }, 'Generated OpenAPI document is empty');\n }\n\n await this.requester.sendResponse({\n id: ctx.id,\n status: 200,\n body: document,\n });\n return true;\n } catch (error) {\n this.logger.error({ err: error, serviceName: service.definition.name }, 'Failed to generate or send docs');\n throw error;\n }\n }\n\n return false;\n }\n\n private resolveErrorStatus(error: Error): number {\n if (error instanceof UnauthorizedUserError) {\n return 401;\n }\n if (error instanceof MissingScopesError) {\n return 403;\n }\n if (error instanceof UnknownRouteError) {\n return 404;\n }\n if (error instanceof BeamableRuntimeError) {\n return 500;\n }\n return 500;\n }\n\n private printHelpfulUrls(service?: ServiceInstance): void {\n if (!service) {\n return;\n }\n\n // Only print helpful URLs when IS_LOCAL=1 is set\n // In deployed containers, we want only JSON logs for proper log collection\n if (process.env.IS_LOCAL !== '1' && process.env.IS_LOCAL !== 'true') {\n return;\n }\n\n const docsUrl = buildDocsPortalUrl(this.env, service.definition);\n const endpointBase = buildPostmanBaseUrl(this.env, service.definition);\n\n const green = (text: string) => `\\x1b[32m${text}\\x1b[0m`;\n const yellow = (text: string) => `\\x1b[33m${text}\\x1b[0m`;\n\n const bannerLines = [\n '',\n yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'),\n ` ${green('Beamable Node microservice ready:')} ${green(service.definition.name)}`,\n yellow(' Quick shortcuts'),\n ` ${yellow('Docs:')} ${docsUrl}`,\n ` ${yellow('Endpoint:')} ${endpointBase}`,\n yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'),\n '',\n ];\n\n process.stdout.write(`${bannerLines.join('\\n')}`);\n }\n\n private async initializeDependencyProviders(): Promise<void> {\n // Get StorageService from service manager (it's initialized during serviceManager.initialize())\n const storageService = this.serviceManager.getStorageService();\n \n for (const service of this.services) {\n const builder = new DependencyBuilder();\n builder.tryAddSingletonInstance(LOGGER_TOKEN, service.logger);\n builder.tryAddSingletonInstance(ENVIRONMENT_CONFIG_TOKEN, this.env);\n builder.tryAddSingletonInstance(BeamableServiceManager, this.serviceManager);\n builder.tryAddSingletonInstance(StorageService, storageService);\n builder.tryAddSingletonInstance(service.definition.ctor, service.instance);\n builder.tryAddSingletonInstance(FederationRegistry, service.federationRegistry);\n\n for (const handler of service.configureHandlers) {\n await handler(builder);\n }\n\n const provider = builder.build();\n service.provider = provider;\n\n for (const handler of service.initializeHandlers) {\n await handler(provider);\n }\n\n Object.defineProperty(service.instance, 'provider', {\n value: provider,\n enumerable: false,\n configurable: false,\n writable: false,\n });\n }\n }\n\n private createRequestScope(path: string, service?: ServiceInstance): MutableDependencyScope {\n // Path should already have query string stripped before calling this method\n const targetService = service ?? this.findServiceForPath(path);\n const provider = targetService?.provider ?? new DependencyBuilder().build();\n return provider.createScope();\n }\n\n /**\n * Extracts the path portion without the query string.\n * @param path The full path potentially containing a query string\n * @returns The path without the query string\n */\n private getPathWithoutQuery(path: string): string {\n const [pathWithoutQuery] = path.split('?', 2);\n return pathWithoutQuery;\n }\n}\n\nfunction enforceAccess(access: ServiceAccess, ctx: RequestContext): void {\n switch (access) {\n case 'client':\n if (ctx.userId <= 0) {\n throw new UnauthorizedUserError(ctx.path);\n }\n break;\n case 'server':\n if (!scopeSetHas(ctx.scopes, 'server')) {\n throw new MissingScopesError(['server']);\n }\n break;\n case 'admin':\n if (!scopeSetHas(ctx.scopes, 'admin')) {\n throw new MissingScopesError(['admin']);\n }\n break;\n default:\n break;\n }\n}\n\n/**\n * Conditionally output to console.error only when running locally (not in container).\n * In containers, we want only Pino JSON logs to stdout for proper log collection.\n */\nfunction debugLog(...args: unknown[]): void {\n // Only output debug logs when IS_LOCAL=1 is set\n if (process.env.IS_LOCAL === '1' || process.env.IS_LOCAL === 'true') {\n console.error(...args);\n }\n}\n\n/**\n * Determines if we're running in a deployed container.\n * Used for service registration logic (routing key handling, discovery broadcaster, etc.)\n * \n * Note: For log formatting, use IS_LOCAL env var instead.\n */\nfunction isRunningInContainer(): boolean {\n // Check for Docker container\n try {\n const fs = require('fs');\n if (fs.existsSync('/.dockerenv')) {\n return true;\n }\n } catch {\n // fs might not be available\n }\n \n // Check for Docker container hostname pattern (12 hex chars)\n const hostname = process.env.HOSTNAME || '';\n if (hostname && /^[a-f0-9]{12}$/i.test(hostname)) {\n return true;\n }\n \n // Explicit container indicators\n if (\n process.env.DOTNET_RUNNING_IN_CONTAINER === 'true' ||\n process.env.CONTAINER === 'beamable' ||\n !!process.env.ECS_CONTAINER_METADATA_URI ||\n !!process.env.KUBERNETES_SERVICE_HOST\n ) {\n return true;\n }\n \n // Default: assume local development\n return false;\n}\n\nfunction normalizeScopes(scopes: Array<unknown>): Set<string> {\n const normalized = new Set<string>();\n for (const scope of scopes) {\n if (typeof scope !== 'string' || scope.trim().length === 0) {\n continue;\n }\n normalized.add(scope.trim().toLowerCase());\n }\n return normalized;\n}\n\nfunction scopeSetHas(scopes: Set<string>, scope: string): boolean {\n const normalized = scope.trim().toLowerCase();\n if (normalized.length === 0) {\n return false;\n }\n return scopes.has('*') || scopes.has(normalized);\n}\n\nfunction buildPostmanBaseUrl(env: EnvironmentConfig, service: ServiceDefinition): string {\n const httpHost = hostToHttpUrl(env.host);\n const routingKeyPart = env.routingKey ? env.routingKey : '';\n return `${httpHost}/basic/${env.cid}.${env.pid}.${routingKeyPart}${service.qualifiedName}/`;\n}\n\nfunction buildDocsPortalUrl(env: EnvironmentConfig, service: ServiceDefinition): string {\n const portalHost = hostToPortalUrl(hostToHttpUrl(env.host));\n const queryParams: Record<string, string> = { srcTool: 'node-runtime' };\n if (env.routingKey) {\n queryParams.routingKey = env.routingKey;\n }\n const query = new URLSearchParams(queryParams);\n if (env.refreshToken) {\n query.set('refresh_token', env.refreshToken);\n }\n const beamoId = service.name;\n return `${portalHost}/${env.cid}/games/${env.pid}/realms/${env.pid}/microservices/micro_${beamoId}/docs?${query.toString()}`;\n}\n\nexport async function runMicroservice(): Promise<void> {\n // Immediate console output to verify process is starting (local dev only)\n // In containers, we rely on Pino logger for all output\n debugLog('[BEAMABLE-NODE] Starting microservice...');\n debugLog(`[BEAMABLE-NODE] Node version: ${process.version}`);\n debugLog(`[BEAMABLE-NODE] Working directory: ${process.cwd()}`);\n debugLog(`[BEAMABLE-NODE] Environment: ${JSON.stringify({\n NODE_ENV: process.env.NODE_ENV,\n CID: process.env.CID ? 'SET' : 'NOT SET',\n PID: process.env.PID ? 'SET' : 'NOT SET',\n HOST: process.env.HOST ? 'SET' : 'NOT SET',\n SECRET: process.env.SECRET ? 'SET' : 'NOT SET',\n })}`);\n if (process.env.BEAMABLE_SKIP_RUNTIME === 'true') {\n return;\n }\n const runtime = new MicroserviceRuntime();\n \n // Handle uncaught errors - log them but don't crash immediately\n // This allows the health check server to keep running so we can debug\n // In containers, errors will be logged by the logger in the runtime\n // Locally, use console.error for visibility\n process.on('uncaughtException', (error) => {\n // In containers, the logger will handle this; locally, show in console\n debugLog('Uncaught Exception:', error);\n // Don't exit - let the health check server keep running\n });\n \n process.on('unhandledRejection', (reason, promise) => {\n // In containers, the logger will handle this; locally, show in console\n debugLog('Unhandled Rejection at:', promise, 'reason:', reason);\n // Don't exit - let the health check server keep running\n });\n \n try {\n await runtime.start();\n } catch (error) {\n // Log the error but don't exit - health check server is running\n // This allows the container to stay alive so we can see what went wrong\n // In containers, the logger already logged it in start(); locally, also use console.error\n debugLog('Failed to start microservice runtime:', error);\n // Keep the process alive so health check can continue\n // The health check will return 503 until isReady is true\n }\n \n const shutdownSignals: NodeJS.Signals[] = ['SIGTERM', 'SIGINT'];\n shutdownSignals.forEach((signal) => {\n process.once(signal, async () => {\n await runtime.shutdown();\n process.exit(0);\n });\n });\n}\n\ninterface GenerateOpenApiOptions {\n serviceName?: string;\n}\n\nexport function generateOpenApiDocumentForRegisteredServices(\n overrides: Partial<EnvironmentConfig> = {},\n options: GenerateOpenApiOptions = {},\n): unknown {\n const services = listRegisteredServices();\n if (services.length === 0) {\n throw new Error('No microservices registered. Import your service module before generating documentation.');\n }\n\n const baseEnv = buildEnvironmentConfig(overrides);\n const primary = options.serviceName\n ? services.find((svc) => svc.name === options.serviceName || svc.qualifiedName === options.serviceName)\n : services[0];\n\n if (!primary) {\n throw new Error(`No registered microservice matched '${options.serviceName}'.`);\n }\n\n return generateOpenApiDocument(\n {\n qualifiedName: primary.qualifiedName,\n name: primary.name,\n callables: Array.from(primary.callables.entries()).map(([displayName, metadata]) => ({\n name: displayName,\n route: metadata.route,\n metadata,\n handler: undefined,\n })),\n },\n baseEnv,\n VERSION,\n );\n}\n\nfunction buildEnvironmentConfig(overrides: Partial<EnvironmentConfig>): EnvironmentConfig {\n const merged: Partial<EnvironmentConfig> = { ...overrides };\n\n const ensure = (key: keyof EnvironmentConfig, fallbackEnv?: string) => {\n if (merged[key] !== undefined) {\n return;\n }\n if (fallbackEnv && process.env[fallbackEnv]) {\n merged[key] = process.env[fallbackEnv] as never;\n }\n };\n\n ensure('cid', 'CID');\n ensure('pid', 'PID');\n ensure('host', 'HOST');\n ensure('secret', 'SECRET');\n ensure('refreshToken', 'REFRESH_TOKEN');\n // Routing key is optional for deployed services (in containers)\n // It will be resolved to empty string if not provided and running in container\n if (!merged.routingKey && !isRunningInContainer()) {\n ensure('routingKey', 'NAME_PREFIX');\n }\n\n if (!merged.cid || !merged.pid || !merged.host) {\n throw new Error('CID, PID, and HOST are required to generate documentation.');\n }\n\n const base = loadEnvironmentConfig();\n\n return {\n ...base,\n ...merged,\n routingKey: merged.routingKey ?? base.routingKey,\n };\n}\n"]}
package/dist/storage.cjs CHANGED
@@ -21,6 +21,10 @@ function listRegisteredStorageObjects() {
21
21
  return Array.from(STORAGE_OBJECT_METADATA.values());
22
22
  }
23
23
  const CONNECTION_STRING_ENV_PREFIX = 'STORAGE_CONNSTR_';
24
+ const DEFAULT_MONGODB_MAX_POOL_SIZE = 10;
25
+ const MONGODB_POOL_SIZE_MIN = 1;
26
+ const MONGODB_POOL_SIZE_MAX = 100;
27
+ const MONGODB_MAX_POOL_SIZE_ENV = 'MONGODB_MAX_POOL_SIZE';
24
28
  class StorageService {
25
29
  constructor(dependencies) {
26
30
  this.databaseCache = new Map();
@@ -120,6 +124,22 @@ class StorageService {
120
124
  return connectionString;
121
125
  }
122
126
  }
127
+ getMaxPoolSize() {
128
+ const raw = process.env[MONGODB_MAX_POOL_SIZE_ENV];
129
+ if (raw === undefined || raw === '') {
130
+ return DEFAULT_MONGODB_MAX_POOL_SIZE;
131
+ }
132
+ const n = parseInt(raw, 10);
133
+ if (Number.isNaN(n)) {
134
+ this.logger.warn({ value: raw, envKey: MONGODB_MAX_POOL_SIZE_ENV }, 'Invalid MONGODB_MAX_POOL_SIZE, using default');
135
+ return DEFAULT_MONGODB_MAX_POOL_SIZE;
136
+ }
137
+ const clamped = Math.max(MONGODB_POOL_SIZE_MIN, Math.min(MONGODB_POOL_SIZE_MAX, n));
138
+ if (clamped !== n) {
139
+ this.logger.warn({ value: n, clamped, min: MONGODB_POOL_SIZE_MIN, max: MONGODB_POOL_SIZE_MAX }, 'MONGODB_MAX_POOL_SIZE clamped to allowed range');
140
+ }
141
+ return clamped;
142
+ }
123
143
  async getMongoClient(connectionString) {
124
144
  const normalizedConnectionString = this.normalizeConnectionString(connectionString);
125
145
  if (normalizedConnectionString !== connectionString) {
@@ -128,9 +148,12 @@ class StorageService {
128
148
  if (this.clientCache.has(normalizedConnectionString)) {
129
149
  return this.clientCache.get(normalizedConnectionString);
130
150
  }
151
+ const maxPoolSize = this.getMaxPoolSize();
131
152
  try {
132
153
  const client = new mongodb_1.MongoClient(normalizedConnectionString, {
133
- maxPoolSize: 5,
154
+ maxPoolSize,
155
+ serverSelectionTimeoutMS: 15000,
156
+ connectTimeoutMS: 10000,
134
157
  });
135
158
  await client.connect();
136
159
  this.clientCache.set(normalizedConnectionString, client);
@@ -149,11 +172,14 @@ class StorageService {
149
172
  const variableName = `${CONNECTION_STRING_ENV_PREFIX}${storageName}`;
150
173
  const envValue = process.env[variableName];
151
174
  if (envValue && envValue.trim()) {
175
+ this.logger.debug({ source: 'env', variableName }, 'MongoDB connection string from env');
152
176
  return envValue.trim();
153
177
  }
154
178
  if (this.cachedConnectionString) {
179
+ this.logger.debug('MongoDB connection string from cache');
155
180
  return this.cachedConnectionString;
156
181
  }
182
+ this.logger.debug('MongoDB connection string fetching from Beamable API');
157
183
  const response = await this.fetchConnectionString();
158
184
  if (!response.connectionString || !response.connectionString.trim()) {
159
185
  throw new Error(`Connection string for storage "${storageName}" is empty.`);
@@ -174,16 +200,30 @@ class StorageService {
174
200
  this.logger.debug({ error: error instanceof Error ? error.message : String(error) }, 'beamoGetStorageConnectionBasic failed, falling back to requester');
175
201
  }
176
202
  }
177
- const response = await this.requester.request({
178
- method: 'GET',
179
- url: '/basic/beamo/storage/connection',
180
- withAuth: true,
181
- });
182
- const body = response.body;
183
- if (!body || typeof body.connectionString !== 'string') {
184
- throw new Error('Failed to retrieve Beamable storage connection string.');
203
+ const maxAttempts = 2;
204
+ let lastError;
205
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
206
+ try {
207
+ const response = await this.requester.request({
208
+ method: 'GET',
209
+ url: '/basic/beamo/storage/connection',
210
+ withAuth: true,
211
+ });
212
+ const body = response.body;
213
+ if (!body || typeof body.connectionString !== 'string') {
214
+ throw new Error('Failed to retrieve Beamable storage connection string.');
215
+ }
216
+ return body;
217
+ }
218
+ catch (err) {
219
+ lastError = err;
220
+ this.logger.warn({ attempt, maxAttempts, error: err instanceof Error ? err.message : String(err) }, 'Beamable storage connection fetch failed');
221
+ if (attempt < maxAttempts) {
222
+ await new Promise((r) => setTimeout(r, 1000));
223
+ }
224
+ }
185
225
  }
186
- return body;
226
+ throw lastError;
187
227
  }
188
228
  buildDatabaseName(storageName) {
189
229
  const cid = this.sanitize(this.env.cid);
package/dist/storage.d.ts CHANGED
@@ -42,6 +42,11 @@ export declare class StorageService {
42
42
  * Safely handles already-encoded credentials by decoding first, then re-encoding.
43
43
  */
44
44
  private normalizeConnectionString;
45
+ /**
46
+ * Reads MONGODB_MAX_POOL_SIZE from env, clamped to [1, 100]. Default 10.
47
+ * All game services share the same MongoDB; total connections = sum of each service's pool.
48
+ */
49
+ private getMaxPoolSize;
45
50
  private getMongoClient;
46
51
  private getConnectionString;
47
52
  private fetchConnectionString;
@@ -1 +1 @@
1
- {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,EAAe,KAAK,EAAE,EAAE,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,wBAAyB,SAAQ,wBAAwB;IACxE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;CACrB;AAID,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,CAOjE;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,QAAQ,GAAG,eAAe,GAAG,SAAS,CAEhF;AAED,wBAAgB,4BAA4B,IAAI,eAAe,EAAE,CAEhE;AAED,UAAU,0BAA0B;IAClC,SAAS,EAAE,aAAa,CAAC;IACzB,GAAG,CAAC,EAAE,YAAY,CAAC;IACnB,GAAG,EAAE,iBAAiB,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AAQD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgB;IAC1C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAe;IACpC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoB;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAyB;IACvD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkC;IAC9D,OAAO,CAAC,sBAAsB,CAAC,CAAS;gBAE5B,YAAY,EAAE,0BAA0B;IAO9C,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,wBAA6B,GAAG,OAAO,CAAC,EAAE,CAAC;IAkBrF,cAAc,CAAC,CAAC,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,OAAO,GAAE,wBAA6B,GAAG,OAAO,CAAC,EAAE,CAAC;IAUhG,aAAa,CAAC,SAAS,SAAS,QAAQ,EAC5C,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IAS3B,gBAAgB,CAAC,QAAQ,EAAE,SAAS,SAAS,QAAQ,EACzD,WAAW,EAAE,UAAU,QAAQ,EAC/B,cAAc,EAAE,UAAU,SAAS,EACnC,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IAYjC;;;;;;OAMG;IACH,OAAO,CAAC,yBAAyB;YAqEnB,cAAc;YAkCd,mBAAmB;YAmBnB,qBAAqB;IA+BnC,OAAO,CAAC,iBAAiB;IAOzB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,oBAAoB;IAQtB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAQ/B"}
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,EAAe,KAAK,EAAE,EAAE,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,wBAAyB,SAAQ,wBAAwB;IACxE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;CACrB;AAID,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,CAOjE;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,QAAQ,GAAG,eAAe,GAAG,SAAS,CAEhF;AAED,wBAAgB,4BAA4B,IAAI,eAAe,EAAE,CAEhE;AAED,UAAU,0BAA0B;IAClC,SAAS,EAAE,aAAa,CAAC;IACzB,GAAG,CAAC,EAAE,YAAY,CAAC;IACnB,GAAG,EAAE,iBAAiB,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AAeD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgB;IAC1C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAe;IACpC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoB;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAyB;IACvD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkC;IAC9D,OAAO,CAAC,sBAAsB,CAAC,CAAS;gBAE5B,YAAY,EAAE,0BAA0B;IAO9C,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,wBAA6B,GAAG,OAAO,CAAC,EAAE,CAAC;IAkBrF,cAAc,CAAC,CAAC,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,OAAO,GAAE,wBAA6B,GAAG,OAAO,CAAC,EAAE,CAAC;IAUhG,aAAa,CAAC,SAAS,SAAS,QAAQ,EAC5C,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IAS3B,gBAAgB,CAAC,QAAQ,EAAE,SAAS,SAAS,QAAQ,EACzD,WAAW,EAAE,UAAU,QAAQ,EAC/B,cAAc,EAAE,UAAU,SAAS,EACnC,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IAYjC;;;;;;OAMG;IACH,OAAO,CAAC,yBAAyB;IAqEjC;;;OAGG;IACH,OAAO,CAAC,cAAc;YAuBR,cAAc;YAqCd,mBAAmB;YAsBnB,qBAAqB;IA+CnC,OAAO,CAAC,iBAAiB;IAOzB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,oBAAoB;IAQtB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAQ/B"}
package/dist/storage.js CHANGED
@@ -15,6 +15,12 @@ export function listRegisteredStorageObjects() {
15
15
  return Array.from(STORAGE_OBJECT_METADATA.values());
16
16
  }
17
17
  const CONNECTION_STRING_ENV_PREFIX = 'STORAGE_CONNSTR_';
18
+ /** Default MongoDB pool size when MONGODB_MAX_POOL_SIZE is not set. */
19
+ const DEFAULT_MONGODB_MAX_POOL_SIZE = 10;
20
+ /** Min/max allowed pool size (env is clamped to this range). */
21
+ const MONGODB_POOL_SIZE_MIN = 1;
22
+ const MONGODB_POOL_SIZE_MAX = 100;
23
+ const MONGODB_MAX_POOL_SIZE_ENV = 'MONGODB_MAX_POOL_SIZE';
18
24
  export class StorageService {
19
25
  requester;
20
26
  api; // Optional - not required for operation
@@ -135,6 +141,26 @@ export class StorageService {
135
141
  return connectionString;
136
142
  }
137
143
  }
144
+ /**
145
+ * Reads MONGODB_MAX_POOL_SIZE from env, clamped to [1, 100]. Default 10.
146
+ * All game services share the same MongoDB; total connections = sum of each service's pool.
147
+ */
148
+ getMaxPoolSize() {
149
+ const raw = process.env[MONGODB_MAX_POOL_SIZE_ENV];
150
+ if (raw === undefined || raw === '') {
151
+ return DEFAULT_MONGODB_MAX_POOL_SIZE;
152
+ }
153
+ const n = parseInt(raw, 10);
154
+ if (Number.isNaN(n)) {
155
+ this.logger.warn({ value: raw, envKey: MONGODB_MAX_POOL_SIZE_ENV }, 'Invalid MONGODB_MAX_POOL_SIZE, using default');
156
+ return DEFAULT_MONGODB_MAX_POOL_SIZE;
157
+ }
158
+ const clamped = Math.max(MONGODB_POOL_SIZE_MIN, Math.min(MONGODB_POOL_SIZE_MAX, n));
159
+ if (clamped !== n) {
160
+ this.logger.warn({ value: n, clamped, min: MONGODB_POOL_SIZE_MIN, max: MONGODB_POOL_SIZE_MAX }, 'MONGODB_MAX_POOL_SIZE clamped to allowed range');
161
+ }
162
+ return clamped;
163
+ }
138
164
  async getMongoClient(connectionString) {
139
165
  // Normalize the connection string to ensure username/password are properly encoded
140
166
  const normalizedConnectionString = this.normalizeConnectionString(connectionString);
@@ -145,9 +171,12 @@ export class StorageService {
145
171
  if (this.clientCache.has(normalizedConnectionString)) {
146
172
  return this.clientCache.get(normalizedConnectionString);
147
173
  }
174
+ const maxPoolSize = this.getMaxPoolSize();
148
175
  try {
149
176
  const client = new MongoClient(normalizedConnectionString, {
150
- maxPoolSize: 5,
177
+ maxPoolSize,
178
+ serverSelectionTimeoutMS: 15000,
179
+ connectTimeoutMS: 10000,
151
180
  });
152
181
  await client.connect();
153
182
  this.clientCache.set(normalizedConnectionString, client);
@@ -167,11 +196,14 @@ export class StorageService {
167
196
  const variableName = `${CONNECTION_STRING_ENV_PREFIX}${storageName}`;
168
197
  const envValue = process.env[variableName];
169
198
  if (envValue && envValue.trim()) {
199
+ this.logger.debug({ source: 'env', variableName }, 'MongoDB connection string from env');
170
200
  return envValue.trim();
171
201
  }
172
202
  if (this.cachedConnectionString) {
203
+ this.logger.debug('MongoDB connection string from cache');
173
204
  return this.cachedConnectionString;
174
205
  }
206
+ this.logger.debug('MongoDB connection string fetching from Beamable API');
175
207
  const response = await this.fetchConnectionString();
176
208
  if (!response.connectionString || !response.connectionString.trim()) {
177
209
  throw new Error(`Connection string for storage "${storageName}" is empty.`);
@@ -195,16 +227,30 @@ export class StorageService {
195
227
  }
196
228
  // Fall back to direct requester call (works without request context)
197
229
  // This is the same approach used by C# StorageObjectConnectionProvider
198
- const response = await this.requester.request({
199
- method: 'GET',
200
- url: '/basic/beamo/storage/connection',
201
- withAuth: true,
202
- });
203
- const body = response.body;
204
- if (!body || typeof body.connectionString !== 'string') {
205
- throw new Error('Failed to retrieve Beamable storage connection string.');
206
- }
207
- return body;
230
+ const maxAttempts = 2;
231
+ let lastError;
232
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
233
+ try {
234
+ const response = await this.requester.request({
235
+ method: 'GET',
236
+ url: '/basic/beamo/storage/connection',
237
+ withAuth: true,
238
+ });
239
+ const body = response.body;
240
+ if (!body || typeof body.connectionString !== 'string') {
241
+ throw new Error('Failed to retrieve Beamable storage connection string.');
242
+ }
243
+ return body;
244
+ }
245
+ catch (err) {
246
+ lastError = err;
247
+ this.logger.warn({ attempt, maxAttempts, error: err instanceof Error ? err.message : String(err) }, 'Beamable storage connection fetch failed');
248
+ if (attempt < maxAttempts) {
249
+ await new Promise((r) => setTimeout(r, 1000));
250
+ }
251
+ }
252
+ }
253
+ throw lastError;
208
254
  }
209
255
  buildDatabaseName(storageName) {
210
256
  const cid = this.sanitize(this.env.cid);
@@ -1 +1 @@
1
- {"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Logger } from 'pino';\r\nimport { MongoClient, type Db, type Collection, type Document } from 'mongodb';\r\nimport type { BoundBeamApi } from './services.js';\r\nimport type { EnvironmentConfig } from './types.js';\r\nimport type { HttpRequester } from 'beamable-sdk';\r\n\r\nexport interface StorageConnectionOptions {\r\n useCache?: boolean;\r\n}\r\n\r\nexport interface StorageCollectionOptions extends StorageConnectionOptions {\r\n collectionName?: string;\r\n}\r\n\r\nexport interface StorageMetadata {\r\n storageName: string;\r\n}\r\n\r\nconst STORAGE_OBJECT_METADATA = new Map<Function, StorageMetadata>();\r\n\r\nexport function StorageObject(storageName: string): ClassDecorator {\r\n if (!storageName || !storageName.trim()) {\r\n throw new Error('@StorageObject requires a non-empty storage name.');\r\n }\r\n return (target) => {\r\n STORAGE_OBJECT_METADATA.set(target, { storageName: storageName.trim() });\r\n };\r\n}\r\n\r\nexport function getStorageMetadata(target: Function): StorageMetadata | undefined {\r\n return STORAGE_OBJECT_METADATA.get(target);\r\n}\r\n\r\nexport function listRegisteredStorageObjects(): StorageMetadata[] {\r\n return Array.from(STORAGE_OBJECT_METADATA.values());\r\n}\r\n\r\ninterface StorageServiceDependencies {\r\n requester: HttpRequester;\r\n api?: BoundBeamApi; // Optional - falls back to requester if not provided\r\n env: EnvironmentConfig;\r\n logger: Logger;\r\n}\r\n\r\ninterface ConnectionStringResponse {\r\n connectionString: string;\r\n}\r\n\r\nconst CONNECTION_STRING_ENV_PREFIX = 'STORAGE_CONNSTR_';\r\n\r\nexport class StorageService {\r\n private readonly requester: HttpRequester;\r\n private readonly api?: BoundBeamApi; // Optional - not required for operation\r\n private readonly env: EnvironmentConfig;\r\n private readonly logger: Logger;\r\n private readonly databaseCache = new Map<string, Db>();\r\n private readonly clientCache = new Map<string, MongoClient>();\r\n private cachedConnectionString?: string;\r\n\r\n constructor(dependencies: StorageServiceDependencies) {\r\n this.requester = dependencies.requester;\r\n this.api = dependencies.api; // Optional - can be undefined\r\n this.env = dependencies.env;\r\n this.logger = dependencies.logger.child({ component: 'StorageService' });\r\n }\r\n\r\n async getDatabase(storageName: string, options: StorageConnectionOptions = {}): Promise<Db> {\r\n const normalized = this.normalizeStorageName(storageName);\r\n if (!options.useCache) {\r\n this.databaseCache.delete(normalized);\r\n }\r\n const cached = this.databaseCache.get(normalized);\r\n if (cached) {\r\n return cached;\r\n }\r\n\r\n const connectionString = await this.getConnectionString(normalized);\r\n const client = await this.getMongoClient(connectionString);\r\n const databaseName = this.buildDatabaseName(normalized);\r\n const database = client.db(databaseName);\r\n this.databaseCache.set(normalized, database);\r\n return database;\r\n }\r\n\r\n async getDatabaseFor<T>(storageCtor: new () => T, options: StorageConnectionOptions = {}): Promise<Db> {\r\n const metadata = getStorageMetadata(storageCtor);\r\n if (!metadata) {\r\n throw new Error(\r\n `Storage metadata for ${storageCtor.name} not found. Did you decorate the class with @StorageObject('Name')?`,\r\n );\r\n }\r\n return this.getDatabase(metadata.storageName, options);\r\n }\r\n\r\n async getCollection<TDocument extends Document>(\r\n storageName: string,\r\n options: StorageCollectionOptions = {},\r\n ): Promise<Collection<TDocument>> {\r\n const database = await this.getDatabase(storageName, options);\r\n const collectionName = options.collectionName?.trim();\r\n if (!collectionName) {\r\n throw new Error('Collection name must be provided when using getCollection with raw storage name.');\r\n }\r\n return database.collection<TDocument>(collectionName);\r\n }\r\n\r\n async getCollectionFor<TStorage, TDocument extends Document>(\r\n storageCtor: new () => TStorage,\r\n collectionCtor: new () => TDocument,\r\n options: StorageCollectionOptions = {},\r\n ): Promise<Collection<TDocument>> {\r\n const metadata = getStorageMetadata(storageCtor);\r\n if (!metadata) {\r\n throw new Error(\r\n `Storage metadata for ${storageCtor.name} not found. Did you decorate the class with @StorageObject('Name')?`,\r\n );\r\n }\r\n const collectionName = options.collectionName?.trim() ?? collectionCtor.name;\r\n const database = await this.getDatabase(metadata.storageName, options);\r\n return database.collection<TDocument>(collectionName);\r\n }\r\n\r\n /**\r\n * Normalizes a MongoDB connection string by ensuring username and password are properly URL-encoded.\r\n * This prevents \"Password contains unescaped characters\" errors when passwords contain special characters.\r\n * \r\n * Handles both mongodb:// and mongodb+srv:// formats.\r\n * Safely handles already-encoded credentials by decoding first, then re-encoding.\r\n */\r\n private normalizeConnectionString(connectionString: string): string {\r\n try {\r\n // Match MongoDB connection string format: mongodb:// or mongodb+srv://\r\n // Format: mongodb[+srv]://[username:password@]host[:port][/database][?options]\r\n const mongoUriRegex = /^(mongodb(?:\\+srv)?:\\/\\/)(?:([^:@]+)(?::([^@]+))?@)?([^\\/?]+)(?:\\/([^?]*))?(?:\\?(.*))?$/;\r\n const match = connectionString.match(mongoUriRegex);\r\n \r\n if (!match) {\r\n // If it doesn't match the expected format, return as-is (might be a different format)\r\n this.logger.warn('Connection string does not match expected MongoDB URI format, using as-is');\r\n return connectionString;\r\n }\r\n\r\n const [, protocol, username, password, host, database, options] = match;\r\n \r\n // If no username/password, return as-is\r\n if (!username) {\r\n return connectionString;\r\n }\r\n\r\n // Decode username and password first (in case they're already encoded)\r\n // Then re-encode them properly to ensure special characters are handled\r\n let decodedUsername: string;\r\n let decodedPassword: string | undefined;\r\n \r\n try {\r\n decodedUsername = decodeURIComponent(username);\r\n } catch {\r\n // If decoding fails, assume it's not encoded and use as-is\r\n decodedUsername = username;\r\n }\r\n \r\n if (password) {\r\n try {\r\n decodedPassword = decodeURIComponent(password);\r\n } catch {\r\n // If decoding fails, assume it's not encoded and use as-is\r\n decodedPassword = password;\r\n }\r\n }\r\n\r\n // Now encode them properly (encodeURIComponent handles all special characters)\r\n const encodedUsername = encodeURIComponent(decodedUsername);\r\n const encodedPassword = decodedPassword ? encodeURIComponent(decodedPassword) : undefined;\r\n\r\n // Reconstruct the connection string\r\n let normalized = `${protocol}${encodedUsername}`;\r\n if (encodedPassword) {\r\n normalized += `:${encodedPassword}`;\r\n }\r\n normalized += `@${host}`;\r\n if (database) {\r\n normalized += `/${database}`;\r\n }\r\n if (options) {\r\n normalized += `?${options}`;\r\n }\r\n\r\n return normalized;\r\n } catch (error) {\r\n // If parsing fails, log warning and return original\r\n this.logger.warn(\r\n { error: error instanceof Error ? error.message : String(error) },\r\n 'Failed to normalize MongoDB connection string, using as-is'\r\n );\r\n return connectionString;\r\n }\r\n }\r\n\r\n private async getMongoClient(connectionString: string): Promise<MongoClient> {\r\n // Normalize the connection string to ensure username/password are properly encoded\r\n const normalizedConnectionString = this.normalizeConnectionString(connectionString);\r\n \r\n // Log the normalization for debugging (only log if different to avoid spam)\r\n if (normalizedConnectionString !== connectionString) {\r\n this.logger.debug('MongoDB connection string was normalized (password encoding applied)');\r\n }\r\n \r\n if (this.clientCache.has(normalizedConnectionString)) {\r\n return this.clientCache.get(normalizedConnectionString) as MongoClient;\r\n }\r\n \r\n try {\r\n const client = new MongoClient(normalizedConnectionString, {\r\n maxPoolSize: 5,\r\n });\r\n await client.connect();\r\n this.clientCache.set(normalizedConnectionString, client);\r\n return client;\r\n } catch (error) {\r\n // If connection fails, log the error with connection string details (without password)\r\n const sanitizedConnectionString = normalizedConnectionString.replace(/:([^:@]+)@/, ':****@');\r\n this.logger.error(\r\n { \r\n error: error instanceof Error ? error.message : String(error),\r\n connectionString: sanitizedConnectionString \r\n },\r\n 'Failed to connect to MongoDB'\r\n );\r\n throw error;\r\n }\r\n }\r\n\r\n private async getConnectionString(storageName: string): Promise<string> {\r\n const variableName = `${CONNECTION_STRING_ENV_PREFIX}${storageName}`;\r\n const envValue = process.env[variableName];\r\n if (envValue && envValue.trim()) {\r\n return envValue.trim();\r\n }\r\n\r\n if (this.cachedConnectionString) {\r\n return this.cachedConnectionString;\r\n }\r\n\r\n const response = await this.fetchConnectionString();\r\n if (!response.connectionString || !response.connectionString.trim()) {\r\n throw new Error(`Connection string for storage \"${storageName}\" is empty.`);\r\n }\r\n this.cachedConnectionString = response.connectionString.trim();\r\n return this.cachedConnectionString;\r\n }\r\n\r\n private async fetchConnectionString(): Promise<ConnectionStringResponse> {\r\n // Try using BoundBeamApi if available (for convenience)\r\n if (this.api && typeof this.api.beamoGetStorageConnectionBasic === 'function') {\r\n try {\r\n const result = await this.api.beamoGetStorageConnectionBasic();\r\n if (result && typeof result === 'object' && 'body' in result) {\r\n return (result as { body: ConnectionStringResponse }).body;\r\n }\r\n return result as ConnectionStringResponse;\r\n } catch (error) {\r\n this.logger.debug(\r\n { error: error instanceof Error ? error.message : String(error) },\r\n 'beamoGetStorageConnectionBasic failed, falling back to requester',\r\n );\r\n }\r\n }\r\n\r\n // Fall back to direct requester call (works without request context)\r\n // This is the same approach used by C# StorageObjectConnectionProvider\r\n const response = await this.requester.request({\r\n method: 'GET',\r\n url: '/basic/beamo/storage/connection',\r\n withAuth: true,\r\n });\r\n const body = response.body as ConnectionStringResponse | undefined;\r\n if (!body || typeof body.connectionString !== 'string') {\r\n throw new Error('Failed to retrieve Beamable storage connection string.');\r\n }\r\n return body;\r\n }\r\n\r\n private buildDatabaseName(storageName: string): string {\r\n const cid = this.sanitize(this.env.cid);\r\n const pid = this.sanitize(this.env.pid);\r\n const storage = this.sanitize(storageName);\r\n return `${cid}${pid}_${storage}`;\r\n }\r\n\r\n private sanitize(value: string): string {\r\n return value.replace(/[^A-Za-z0-9_]/g, '_');\r\n }\r\n\r\n private normalizeStorageName(storageName: string): string {\r\n const normalized = storageName.trim();\r\n if (!normalized) {\r\n throw new Error('Storage name cannot be empty.');\r\n }\r\n return normalized;\r\n }\r\n\r\n async dispose(): Promise<void> {\r\n for (const client of this.clientCache.values()) {\r\n await client.close();\r\n }\r\n this.clientCache.clear();\r\n this.databaseCache.clear();\r\n this.cachedConnectionString = undefined;\r\n }\r\n}\r\n\r\n"]}
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Logger } from 'pino';\nimport { MongoClient, type Db, type Collection, type Document } from 'mongodb';\nimport type { BoundBeamApi } from './services.js';\nimport type { EnvironmentConfig } from './types.js';\nimport type { HttpRequester } from 'beamable-sdk';\n\nexport interface StorageConnectionOptions {\n useCache?: boolean;\n}\n\nexport interface StorageCollectionOptions extends StorageConnectionOptions {\n collectionName?: string;\n}\n\nexport interface StorageMetadata {\n storageName: string;\n}\n\nconst STORAGE_OBJECT_METADATA = new Map<Function, StorageMetadata>();\n\nexport function StorageObject(storageName: string): ClassDecorator {\n if (!storageName || !storageName.trim()) {\n throw new Error('@StorageObject requires a non-empty storage name.');\n }\n return (target) => {\n STORAGE_OBJECT_METADATA.set(target, { storageName: storageName.trim() });\n };\n}\n\nexport function getStorageMetadata(target: Function): StorageMetadata | undefined {\n return STORAGE_OBJECT_METADATA.get(target);\n}\n\nexport function listRegisteredStorageObjects(): StorageMetadata[] {\n return Array.from(STORAGE_OBJECT_METADATA.values());\n}\n\ninterface StorageServiceDependencies {\n requester: HttpRequester;\n api?: BoundBeamApi; // Optional - falls back to requester if not provided\n env: EnvironmentConfig;\n logger: Logger;\n}\n\ninterface ConnectionStringResponse {\n connectionString: string;\n}\n\nconst CONNECTION_STRING_ENV_PREFIX = 'STORAGE_CONNSTR_';\n\n/** Default MongoDB pool size when MONGODB_MAX_POOL_SIZE is not set. */\nconst DEFAULT_MONGODB_MAX_POOL_SIZE = 10;\n/** Min/max allowed pool size (env is clamped to this range). */\nconst MONGODB_POOL_SIZE_MIN = 1;\nconst MONGODB_POOL_SIZE_MAX = 100;\nconst MONGODB_MAX_POOL_SIZE_ENV = 'MONGODB_MAX_POOL_SIZE';\n\nexport class StorageService {\n private readonly requester: HttpRequester;\n private readonly api?: BoundBeamApi; // Optional - not required for operation\n private readonly env: EnvironmentConfig;\n private readonly logger: Logger;\n private readonly databaseCache = new Map<string, Db>();\n private readonly clientCache = new Map<string, MongoClient>();\n private cachedConnectionString?: string;\n\n constructor(dependencies: StorageServiceDependencies) {\n this.requester = dependencies.requester;\n this.api = dependencies.api; // Optional - can be undefined\n this.env = dependencies.env;\n this.logger = dependencies.logger.child({ component: 'StorageService' });\n }\n\n async getDatabase(storageName: string, options: StorageConnectionOptions = {}): Promise<Db> {\n const normalized = this.normalizeStorageName(storageName);\n if (!options.useCache) {\n this.databaseCache.delete(normalized);\n }\n const cached = this.databaseCache.get(normalized);\n if (cached) {\n return cached;\n }\n\n const connectionString = await this.getConnectionString(normalized);\n const client = await this.getMongoClient(connectionString);\n const databaseName = this.buildDatabaseName(normalized);\n const database = client.db(databaseName);\n this.databaseCache.set(normalized, database);\n return database;\n }\n\n async getDatabaseFor<T>(storageCtor: new () => T, options: StorageConnectionOptions = {}): Promise<Db> {\n const metadata = getStorageMetadata(storageCtor);\n if (!metadata) {\n throw new Error(\n `Storage metadata for ${storageCtor.name} not found. Did you decorate the class with @StorageObject('Name')?`,\n );\n }\n return this.getDatabase(metadata.storageName, options);\n }\n\n async getCollection<TDocument extends Document>(\n storageName: string,\n options: StorageCollectionOptions = {},\n ): Promise<Collection<TDocument>> {\n const database = await this.getDatabase(storageName, options);\n const collectionName = options.collectionName?.trim();\n if (!collectionName) {\n throw new Error('Collection name must be provided when using getCollection with raw storage name.');\n }\n return database.collection<TDocument>(collectionName);\n }\n\n async getCollectionFor<TStorage, TDocument extends Document>(\n storageCtor: new () => TStorage,\n collectionCtor: new () => TDocument,\n options: StorageCollectionOptions = {},\n ): Promise<Collection<TDocument>> {\n const metadata = getStorageMetadata(storageCtor);\n if (!metadata) {\n throw new Error(\n `Storage metadata for ${storageCtor.name} not found. Did you decorate the class with @StorageObject('Name')?`,\n );\n }\n const collectionName = options.collectionName?.trim() ?? collectionCtor.name;\n const database = await this.getDatabase(metadata.storageName, options);\n return database.collection<TDocument>(collectionName);\n }\n\n /**\n * Normalizes a MongoDB connection string by ensuring username and password are properly URL-encoded.\n * This prevents \"Password contains unescaped characters\" errors when passwords contain special characters.\n * \n * Handles both mongodb:// and mongodb+srv:// formats.\n * Safely handles already-encoded credentials by decoding first, then re-encoding.\n */\n private normalizeConnectionString(connectionString: string): string {\n try {\n // Match MongoDB connection string format: mongodb:// or mongodb+srv://\n // Format: mongodb[+srv]://[username:password@]host[:port][/database][?options]\n const mongoUriRegex = /^(mongodb(?:\\+srv)?:\\/\\/)(?:([^:@]+)(?::([^@]+))?@)?([^\\/?]+)(?:\\/([^?]*))?(?:\\?(.*))?$/;\n const match = connectionString.match(mongoUriRegex);\n \n if (!match) {\n // If it doesn't match the expected format, return as-is (might be a different format)\n this.logger.warn('Connection string does not match expected MongoDB URI format, using as-is');\n return connectionString;\n }\n\n const [, protocol, username, password, host, database, options] = match;\n \n // If no username/password, return as-is\n if (!username) {\n return connectionString;\n }\n\n // Decode username and password first (in case they're already encoded)\n // Then re-encode them properly to ensure special characters are handled\n let decodedUsername: string;\n let decodedPassword: string | undefined;\n \n try {\n decodedUsername = decodeURIComponent(username);\n } catch {\n // If decoding fails, assume it's not encoded and use as-is\n decodedUsername = username;\n }\n \n if (password) {\n try {\n decodedPassword = decodeURIComponent(password);\n } catch {\n // If decoding fails, assume it's not encoded and use as-is\n decodedPassword = password;\n }\n }\n\n // Now encode them properly (encodeURIComponent handles all special characters)\n const encodedUsername = encodeURIComponent(decodedUsername);\n const encodedPassword = decodedPassword ? encodeURIComponent(decodedPassword) : undefined;\n\n // Reconstruct the connection string\n let normalized = `${protocol}${encodedUsername}`;\n if (encodedPassword) {\n normalized += `:${encodedPassword}`;\n }\n normalized += `@${host}`;\n if (database) {\n normalized += `/${database}`;\n }\n if (options) {\n normalized += `?${options}`;\n }\n\n return normalized;\n } catch (error) {\n // If parsing fails, log warning and return original\n this.logger.warn(\n { error: error instanceof Error ? error.message : String(error) },\n 'Failed to normalize MongoDB connection string, using as-is'\n );\n return connectionString;\n }\n }\n\n /**\n * Reads MONGODB_MAX_POOL_SIZE from env, clamped to [1, 100]. Default 10.\n * All game services share the same MongoDB; total connections = sum of each service's pool.\n */\n private getMaxPoolSize(): number {\n const raw = process.env[MONGODB_MAX_POOL_SIZE_ENV];\n if (raw === undefined || raw === '') {\n return DEFAULT_MONGODB_MAX_POOL_SIZE;\n }\n const n = parseInt(raw, 10);\n if (Number.isNaN(n)) {\n this.logger.warn(\n { value: raw, envKey: MONGODB_MAX_POOL_SIZE_ENV },\n 'Invalid MONGODB_MAX_POOL_SIZE, using default'\n );\n return DEFAULT_MONGODB_MAX_POOL_SIZE;\n }\n const clamped = Math.max(MONGODB_POOL_SIZE_MIN, Math.min(MONGODB_POOL_SIZE_MAX, n));\n if (clamped !== n) {\n this.logger.warn(\n { value: n, clamped, min: MONGODB_POOL_SIZE_MIN, max: MONGODB_POOL_SIZE_MAX },\n 'MONGODB_MAX_POOL_SIZE clamped to allowed range'\n );\n }\n return clamped;\n }\n\n private async getMongoClient(connectionString: string): Promise<MongoClient> {\n // Normalize the connection string to ensure username/password are properly encoded\n const normalizedConnectionString = this.normalizeConnectionString(connectionString);\n \n // Log the normalization for debugging (only log if different to avoid spam)\n if (normalizedConnectionString !== connectionString) {\n this.logger.debug('MongoDB connection string was normalized (password encoding applied)');\n }\n \n if (this.clientCache.has(normalizedConnectionString)) {\n return this.clientCache.get(normalizedConnectionString) as MongoClient;\n }\n \n const maxPoolSize = this.getMaxPoolSize();\n try {\n const client = new MongoClient(normalizedConnectionString, {\n maxPoolSize,\n serverSelectionTimeoutMS: 15000,\n connectTimeoutMS: 10000,\n });\n await client.connect();\n this.clientCache.set(normalizedConnectionString, client);\n return client;\n } catch (error) {\n // If connection fails, log the error with connection string details (without password)\n const sanitizedConnectionString = normalizedConnectionString.replace(/:([^:@]+)@/, ':****@');\n this.logger.error(\n { \n error: error instanceof Error ? error.message : String(error),\n connectionString: sanitizedConnectionString \n },\n 'Failed to connect to MongoDB'\n );\n throw error;\n }\n }\n\n private async getConnectionString(storageName: string): Promise<string> {\n const variableName = `${CONNECTION_STRING_ENV_PREFIX}${storageName}`;\n const envValue = process.env[variableName];\n if (envValue && envValue.trim()) {\n this.logger.debug({ source: 'env', variableName }, 'MongoDB connection string from env');\n return envValue.trim();\n }\n\n if (this.cachedConnectionString) {\n this.logger.debug('MongoDB connection string from cache');\n return this.cachedConnectionString;\n }\n\n this.logger.debug('MongoDB connection string fetching from Beamable API');\n const response = await this.fetchConnectionString();\n if (!response.connectionString || !response.connectionString.trim()) {\n throw new Error(`Connection string for storage \"${storageName}\" is empty.`);\n }\n this.cachedConnectionString = response.connectionString.trim();\n return this.cachedConnectionString;\n }\n\n private async fetchConnectionString(): Promise<ConnectionStringResponse> {\n // Try using BoundBeamApi if available (for convenience)\n if (this.api && typeof this.api.beamoGetStorageConnectionBasic === 'function') {\n try {\n const result = await this.api.beamoGetStorageConnectionBasic();\n if (result && typeof result === 'object' && 'body' in result) {\n return (result as { body: ConnectionStringResponse }).body;\n }\n return result as ConnectionStringResponse;\n } catch (error) {\n this.logger.debug(\n { error: error instanceof Error ? error.message : String(error) },\n 'beamoGetStorageConnectionBasic failed, falling back to requester',\n );\n }\n }\n\n // Fall back to direct requester call (works without request context)\n // This is the same approach used by C# StorageObjectConnectionProvider\n const maxAttempts = 2;\n let lastError: unknown;\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n const response = await this.requester.request({\n method: 'GET',\n url: '/basic/beamo/storage/connection',\n withAuth: true,\n });\n const body = response.body as ConnectionStringResponse | undefined;\n if (!body || typeof body.connectionString !== 'string') {\n throw new Error('Failed to retrieve Beamable storage connection string.');\n }\n return body;\n } catch (err) {\n lastError = err;\n this.logger.warn(\n { attempt, maxAttempts, error: err instanceof Error ? err.message : String(err) },\n 'Beamable storage connection fetch failed'\n );\n if (attempt < maxAttempts) {\n await new Promise((r) => setTimeout(r, 1000));\n }\n }\n }\n throw lastError;\n }\n\n private buildDatabaseName(storageName: string): string {\n const cid = this.sanitize(this.env.cid);\n const pid = this.sanitize(this.env.pid);\n const storage = this.sanitize(storageName);\n return `${cid}${pid}_${storage}`;\n }\n\n private sanitize(value: string): string {\n return value.replace(/[^A-Za-z0-9_]/g, '_');\n }\n\n private normalizeStorageName(storageName: string): string {\n const normalized = storageName.trim();\n if (!normalized) {\n throw new Error('Storage name cannot be empty.');\n }\n return normalized;\n }\n\n async dispose(): Promise<void> {\n for (const client of this.clientCache.values()) {\n await client.close();\n }\n this.clientCache.clear();\n this.databaseCache.clear();\n this.cachedConnectionString = undefined;\n }\n}\n\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omen.foundation/node-microservice-runtime",
3
- "version": "0.1.118",
3
+ "version": "0.1.121",
4
4
  "description": "Beamable microservice runtime for Node.js/TypeScript services.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -23,6 +23,7 @@
23
23
  "build": "npm run clean && tsc -p tsconfig.build.json",
24
24
  "build:cjs": "tsc -p tsconfig.cjs.json && node ./scripts/prepare-cjs.mjs",
25
25
  "compile": "npm run build && npm run build:cjs",
26
+ "prepublishOnly": "npm run compile",
26
27
  "dev": "tsx --env-file .env src/dev.ts",
27
28
  "start": "node dist/index.js"
28
29
  },