@omen.foundation/node-microservice-runtime 0.1.6 → 0.1.8
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/env.cjs +14 -2
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +27 -5
- package/dist/env.js.map +1 -1
- package/dist/logger.cjs +13 -4
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +22 -6
- package/dist/logger.js.map +1 -1
- package/dist/runtime.cjs +16 -10
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +23 -15
- package/dist/runtime.js.map +1 -1
- package/package.json +1 -1
- package/src/env.ts +31 -5
- package/src/logger.ts +25 -6
- package/src/runtime.ts +26 -17
package/dist/env.cjs
CHANGED
|
@@ -37,10 +37,22 @@ function resolveRoutingKey() {
|
|
|
37
37
|
if (raw && raw.trim().length > 0) {
|
|
38
38
|
return raw.trim();
|
|
39
39
|
}
|
|
40
|
-
const
|
|
40
|
+
const isExplicitlyInContainer = process.env.DOTNET_RUNNING_IN_CONTAINER === 'true' ||
|
|
41
|
+
process.env.CONTAINER === 'beamable' ||
|
|
42
|
+
!!process.env.ECS_CONTAINER_METADATA_URI ||
|
|
43
|
+
!!process.env.KUBERNETES_SERVICE_HOST;
|
|
44
|
+
if (isExplicitlyInContainer) {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
const hasBeamableEnvVars = !!(process.env.CID && process.env.PID && process.env.HOST && process.env.SECRET);
|
|
41
48
|
const hasNoRoutingKey = !raw || raw.trim().length === 0;
|
|
42
49
|
if (hasBeamableEnvVars && hasNoRoutingKey) {
|
|
43
|
-
|
|
50
|
+
try {
|
|
51
|
+
return (0, routing_js_1.getDefaultRoutingKeyForMachine)();
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
throw new Error(`Unable to determine routing key automatically. Set NAME_PREFIX environment variable. ${error.message}`);
|
|
55
|
+
}
|
|
44
56
|
}
|
|
45
57
|
try {
|
|
46
58
|
return (0, routing_js_1.getDefaultRoutingKeyForMachine)();
|
package/dist/env.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AA+GpD,wBAAgB,qBAAqB,IAAI,iBAAiB,CA4BzD;AAED,wBAAgB,2BAA2B,IAAI,MAAM,CAMpD"}
|
package/dist/env.js
CHANGED
|
@@ -36,15 +36,37 @@ function resolveRoutingKey() {
|
|
|
36
36
|
if (raw && raw.trim().length > 0) {
|
|
37
37
|
return raw.trim();
|
|
38
38
|
}
|
|
39
|
+
// Check for explicit container indicators first (highest priority)
|
|
40
|
+
const isExplicitlyInContainer = process.env.DOTNET_RUNNING_IN_CONTAINER === 'true' ||
|
|
41
|
+
process.env.CONTAINER === 'beamable' ||
|
|
42
|
+
!!process.env.ECS_CONTAINER_METADATA_URI ||
|
|
43
|
+
!!process.env.KUBERNETES_SERVICE_HOST;
|
|
44
|
+
if (isExplicitlyInContainer) {
|
|
45
|
+
// We're definitely in a container - return undefined (becomes None in Scala)
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
39
48
|
// For deployed services (in containers), routing key should be undefined (None in Scala)
|
|
40
49
|
// Beamable sets CID, PID, HOST, SECRET in containers but NOT routing key
|
|
41
|
-
//
|
|
42
|
-
|
|
50
|
+
// BUT: Local dev also has these env vars from .env file, so we need to be more careful
|
|
51
|
+
// If we have the required Beamable env vars but NO routing key AND no explicit container indicators,
|
|
52
|
+
// we might be in a deployed container OR local dev without routing key set
|
|
53
|
+
// For safety, if we're not explicitly in a container, try to generate a routing key for local dev
|
|
54
|
+
const hasBeamableEnvVars = !!(process.env.CID && process.env.PID && process.env.HOST && process.env.SECRET);
|
|
43
55
|
const hasNoRoutingKey = !raw || raw.trim().length === 0;
|
|
56
|
+
// If we have Beamable env vars but no routing key, and we're not explicitly in a container,
|
|
57
|
+
// we might be in local dev - try to generate a routing key
|
|
58
|
+
// Only return undefined if we're explicitly in a container
|
|
44
59
|
if (hasBeamableEnvVars && hasNoRoutingKey) {
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
|
|
60
|
+
// Try to generate a routing key for local dev
|
|
61
|
+
// If this fails, we'll throw an error (which is fine for local dev)
|
|
62
|
+
try {
|
|
63
|
+
return getDefaultRoutingKeyForMachine();
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
// If we can't generate a routing key, we might be in a container
|
|
67
|
+
// But since we don't have explicit container indicators, assume local dev and throw
|
|
68
|
+
throw new Error(`Unable to determine routing key automatically. Set NAME_PREFIX environment variable. ${error.message}`);
|
|
69
|
+
}
|
|
48
70
|
}
|
|
49
71
|
// Local development - try to get a routing key
|
|
50
72
|
try {
|
package/dist/env.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from 'node:fs';\r\nimport { tmpdir } from 'node:os';\r\nimport { join } from 'node:path';\r\nimport type { EnvironmentConfig } from './types.js';\r\nimport { getDefaultRoutingKeyForMachine } from './routing.js';\r\n\r\nfunction getBoolean(name: string, defaultValue = false): boolean {\r\n const raw = process.env[name];\r\n if (!raw) {\r\n return defaultValue;\r\n }\r\n return ['1', 'true', 'yes', 'on'].includes(raw.toLowerCase());\r\n}\r\n\r\nfunction getNumber(name: string, defaultValue: number): number {\r\n const raw = process.env[name];\r\n if (!raw) {\r\n return defaultValue;\r\n }\r\n const parsed = Number(raw);\r\n return Number.isFinite(parsed) ? parsed : defaultValue;\r\n}\r\n\r\nfunction resolveHealthPort(): number {\r\n // Always default to 6565 if HEALTH_PORT is not explicitly set\r\n // This ensures the health check server starts in deployed environments\r\n // even if container detection fails or HEALTH_PORT env var is missing\r\n const preferred = getNumber('HEALTH_PORT', 6565);\r\n if (preferred > 0) {\r\n return preferred;\r\n }\r\n // Use PID-scoped pseudo random port to avoid collisions on dev machines.\r\n const base = 45000;\r\n const span = 2000;\r\n const candidate = base + (process.pid % span);\r\n return candidate;\r\n}\r\n\r\nfunction resolveRoutingKey(): string | undefined {\r\n const raw = process.env.NAME_PREFIX ?? process.env.ROUTING_KEY;\r\n if (raw && raw.trim().length > 0) {\r\n return raw.trim();\r\n }\r\n\r\n // For deployed services (in containers), routing key should be undefined (None in Scala)\r\n // Beamable sets CID, PID, HOST, SECRET in containers but NOT routing key\r\n // If we have the required Beamable env vars but no routing key, we're in a deployed container\r\n const hasBeamableEnvVars = process.env.CID && process.env.PID && process.env.HOST && process.env.SECRET;\r\n const hasNoRoutingKey = !raw || raw.trim().length === 0;\r\n \r\n if (hasBeamableEnvVars && hasNoRoutingKey) {\r\n // We're in a deployed container - return undefined (becomes None in Scala)\r\n // This allows the gateway to find bindings without routing key filter\r\n return undefined;\r\n }\r\n\r\n // Local development - try to get a routing key\r\n try {\r\n return getDefaultRoutingKeyForMachine();\r\n } catch (error) {\r\n throw new Error(\r\n `Unable to determine routing key automatically. Set NAME_PREFIX environment variable. ${(error as Error).message}`,\r\n );\r\n }\r\n}\r\n\r\nfunction resolveSdkVersionExecution(): string {\r\n return process.env.BEAMABLE_SDK_VERSION_EXECUTION ?? '';\r\n}\r\n\r\nfunction resolveLogLevel(): string {\r\n const candidate = process.env.LOG_LEVEL ?? 'info';\r\n const allowed = new Set(['fatal', 'error', 'warn', 'info', 'debug', 'trace']);\r\n return allowed.has(candidate.toLowerCase()) ? candidate.toLowerCase() : 'info';\r\n}\r\n\r\nfunction resolveWatchToken(): boolean {\r\n const value = process.env.WATCH_TOKEN;\r\n if (value === undefined) {\r\n return false;\r\n }\r\n return getBoolean('WATCH_TOKEN', false);\r\n}\r\n\r\nfunction resolveBeamInstanceCount(): number {\r\n return getNumber('BEAM_INSTANCE_COUNT', 1);\r\n}\r\n\r\nexport function loadEnvironmentConfig(): EnvironmentConfig {\r\n const cid = process.env.CID ?? '';\r\n const pid = process.env.PID ?? '';\r\n const host = process.env.HOST ?? '';\r\n\r\n if (!cid || !pid || !host) {\r\n throw new Error('Missing required Beamable environment variables (CID, PID, HOST).');\r\n }\r\n\r\n const config: EnvironmentConfig = {\r\n cid,\r\n pid,\r\n host,\r\n secret: process.env.SECRET ?? undefined,\r\n refreshToken: process.env.REFRESH_TOKEN ?? undefined,\r\n routingKey: resolveRoutingKey(),\r\n accountId: getNumber('USER_ACCOUNT_ID', 0) || undefined,\r\n accountEmail: process.env.USER_EMAIL ?? undefined,\r\n logLevel: resolveLogLevel(),\r\n healthPort: resolveHealthPort(),\r\n disableCustomInitializationHooks: getBoolean('DISABLE_CUSTOM_INITIALIZATION_HOOKS', false),\r\n watchToken: resolveWatchToken(),\r\n sdkVersionExecution: resolveSdkVersionExecution(),\r\n beamInstanceCount: resolveBeamInstanceCount(),\r\n logTruncateLimit: getNumber('LOG_TRUNCATE_LIMIT', 1000),\r\n };\r\n\r\n return config;\r\n}\r\n\r\nexport function ensureWritableTempDirectory(): string {\r\n const candidate = process.env.LOG_PATH ?? join(tmpdir(), 'beamable-node-runtime');\r\n if (!existsSync(candidate)) {\r\n return candidate;\r\n }\r\n return candidate;\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync } from 'node:fs';\r\nimport { tmpdir } from 'node:os';\r\nimport { join } from 'node:path';\r\nimport type { EnvironmentConfig } from './types.js';\r\nimport { getDefaultRoutingKeyForMachine } from './routing.js';\r\n\r\nfunction getBoolean(name: string, defaultValue = false): boolean {\r\n const raw = process.env[name];\r\n if (!raw) {\r\n return defaultValue;\r\n }\r\n return ['1', 'true', 'yes', 'on'].includes(raw.toLowerCase());\r\n}\r\n\r\nfunction getNumber(name: string, defaultValue: number): number {\r\n const raw = process.env[name];\r\n if (!raw) {\r\n return defaultValue;\r\n }\r\n const parsed = Number(raw);\r\n return Number.isFinite(parsed) ? parsed : defaultValue;\r\n}\r\n\r\nfunction resolveHealthPort(): number {\r\n // Always default to 6565 if HEALTH_PORT is not explicitly set\r\n // This ensures the health check server starts in deployed environments\r\n // even if container detection fails or HEALTH_PORT env var is missing\r\n const preferred = getNumber('HEALTH_PORT', 6565);\r\n if (preferred > 0) {\r\n return preferred;\r\n }\r\n // Use PID-scoped pseudo random port to avoid collisions on dev machines.\r\n const base = 45000;\r\n const span = 2000;\r\n const candidate = base + (process.pid % span);\r\n return candidate;\r\n}\r\n\r\nfunction resolveRoutingKey(): string | undefined {\r\n const raw = process.env.NAME_PREFIX ?? process.env.ROUTING_KEY;\r\n if (raw && raw.trim().length > 0) {\r\n return raw.trim();\r\n }\r\n\r\n // Check for explicit container indicators first (highest priority)\r\n const isExplicitlyInContainer = \r\n process.env.DOTNET_RUNNING_IN_CONTAINER === 'true' ||\r\n process.env.CONTAINER === 'beamable' ||\r\n !!process.env.ECS_CONTAINER_METADATA_URI ||\r\n !!process.env.KUBERNETES_SERVICE_HOST;\r\n \r\n if (isExplicitlyInContainer) {\r\n // We're definitely in a container - return undefined (becomes None in Scala)\r\n return undefined;\r\n }\r\n\r\n // For deployed services (in containers), routing key should be undefined (None in Scala)\r\n // Beamable sets CID, PID, HOST, SECRET in containers but NOT routing key\r\n // BUT: Local dev also has these env vars from .env file, so we need to be more careful\r\n // If we have the required Beamable env vars but NO routing key AND no explicit container indicators,\r\n // we might be in a deployed container OR local dev without routing key set\r\n // For safety, if we're not explicitly in a container, try to generate a routing key for local dev\r\n const hasBeamableEnvVars = !!(process.env.CID && process.env.PID && process.env.HOST && process.env.SECRET);\r\n const hasNoRoutingKey = !raw || raw.trim().length === 0;\r\n \r\n // If we have Beamable env vars but no routing key, and we're not explicitly in a container,\r\n // we might be in local dev - try to generate a routing key\r\n // Only return undefined if we're explicitly in a container\r\n if (hasBeamableEnvVars && hasNoRoutingKey) {\r\n // Try to generate a routing key for local dev\r\n // If this fails, we'll throw an error (which is fine for local dev)\r\n try {\r\n return getDefaultRoutingKeyForMachine();\r\n } catch (error) {\r\n // If we can't generate a routing key, we might be in a container\r\n // But since we don't have explicit container indicators, assume local dev and throw\r\n throw new Error(\r\n `Unable to determine routing key automatically. Set NAME_PREFIX environment variable. ${(error as Error).message}`,\r\n );\r\n }\r\n }\r\n\r\n // Local development - try to get a routing key\r\n try {\r\n return getDefaultRoutingKeyForMachine();\r\n } catch (error) {\r\n throw new Error(\r\n `Unable to determine routing key automatically. Set NAME_PREFIX environment variable. ${(error as Error).message}`,\r\n );\r\n }\r\n}\r\n\r\nfunction resolveSdkVersionExecution(): string {\r\n return process.env.BEAMABLE_SDK_VERSION_EXECUTION ?? '';\r\n}\r\n\r\nfunction resolveLogLevel(): string {\r\n const candidate = process.env.LOG_LEVEL ?? 'info';\r\n const allowed = new Set(['fatal', 'error', 'warn', 'info', 'debug', 'trace']);\r\n return allowed.has(candidate.toLowerCase()) ? candidate.toLowerCase() : 'info';\r\n}\r\n\r\nfunction resolveWatchToken(): boolean {\r\n const value = process.env.WATCH_TOKEN;\r\n if (value === undefined) {\r\n return false;\r\n }\r\n return getBoolean('WATCH_TOKEN', false);\r\n}\r\n\r\nfunction resolveBeamInstanceCount(): number {\r\n return getNumber('BEAM_INSTANCE_COUNT', 1);\r\n}\r\n\r\nexport function loadEnvironmentConfig(): EnvironmentConfig {\r\n const cid = process.env.CID ?? '';\r\n const pid = process.env.PID ?? '';\r\n const host = process.env.HOST ?? '';\r\n\r\n if (!cid || !pid || !host) {\r\n throw new Error('Missing required Beamable environment variables (CID, PID, HOST).');\r\n }\r\n\r\n const config: EnvironmentConfig = {\r\n cid,\r\n pid,\r\n host,\r\n secret: process.env.SECRET ?? undefined,\r\n refreshToken: process.env.REFRESH_TOKEN ?? undefined,\r\n routingKey: resolveRoutingKey(),\r\n accountId: getNumber('USER_ACCOUNT_ID', 0) || undefined,\r\n accountEmail: process.env.USER_EMAIL ?? undefined,\r\n logLevel: resolveLogLevel(),\r\n healthPort: resolveHealthPort(),\r\n disableCustomInitializationHooks: getBoolean('DISABLE_CUSTOM_INITIALIZATION_HOOKS', false),\r\n watchToken: resolveWatchToken(),\r\n sdkVersionExecution: resolveSdkVersionExecution(),\r\n beamInstanceCount: resolveBeamInstanceCount(),\r\n logTruncateLimit: getNumber('LOG_TRUNCATE_LIMIT', 1000),\r\n };\r\n\r\n return config;\r\n}\r\n\r\nexport function ensureWritableTempDirectory(): string {\r\n const candidate = process.env.LOG_PATH ?? join(tmpdir(), 'beamable-node-runtime');\r\n if (!existsSync(candidate)) {\r\n return candidate;\r\n }\r\n return candidate;\r\n}\r\n"]}
|
package/dist/logger.cjs
CHANGED
|
@@ -36,15 +36,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.createLogger = createLogger;
|
|
37
37
|
const pino_1 = __importStar(require("pino"));
|
|
38
38
|
const node_stream_1 = require("node:stream");
|
|
39
|
+
const node_module_1 = require("node:module");
|
|
39
40
|
const env_js_1 = require("./env.js");
|
|
40
|
-
function
|
|
41
|
+
function getRequire() {
|
|
42
|
+
if (typeof require !== 'undefined' && typeof require.main !== 'undefined') {
|
|
43
|
+
return require;
|
|
44
|
+
}
|
|
45
|
+
return (0, node_module_1.createRequire)(import.meta.url);
|
|
46
|
+
}
|
|
47
|
+
function isRunningInContainer(env) {
|
|
41
48
|
if (process.env.DOTNET_RUNNING_IN_CONTAINER === 'true' ||
|
|
42
49
|
process.env.CONTAINER === 'beamable' ||
|
|
43
50
|
!!process.env.ECS_CONTAINER_METADATA_URI ||
|
|
44
51
|
!!process.env.KUBERNETES_SERVICE_HOST) {
|
|
45
52
|
return true;
|
|
46
53
|
}
|
|
47
|
-
|
|
54
|
+
const hasRoutingKey = (env === null || env === void 0 ? void 0 : env.routingKey) || process.env.NAME_PREFIX || process.env.ROUTING_KEY;
|
|
55
|
+
if (hasRoutingKey) {
|
|
48
56
|
return false;
|
|
49
57
|
}
|
|
50
58
|
const hasBeamableEnvVars = !!(process.env.CID && process.env.PID && process.env.HOST && process.env.SECRET);
|
|
@@ -145,7 +153,7 @@ function createBeamableLogFormatter() {
|
|
|
145
153
|
function createLogger(env, options = {}) {
|
|
146
154
|
var _a, _b, _c;
|
|
147
155
|
const configuredDestination = (_a = options.destinationPath) !== null && _a !== void 0 ? _a : process.env.LOG_PATH;
|
|
148
|
-
const inContainer = isRunningInContainer();
|
|
156
|
+
const inContainer = isRunningInContainer(env);
|
|
149
157
|
const pinoOptions = {
|
|
150
158
|
name: (_b = options.name) !== null && _b !== void 0 ? _b : 'beamable-node-runtime',
|
|
151
159
|
level: env.logLevel,
|
|
@@ -169,7 +177,8 @@ function createLogger(env, options = {}) {
|
|
|
169
177
|
}
|
|
170
178
|
else {
|
|
171
179
|
try {
|
|
172
|
-
const
|
|
180
|
+
const requireFn = getRequire();
|
|
181
|
+
const pinoPretty = requireFn('pino-pretty');
|
|
173
182
|
const prettyStream = pinoPretty({
|
|
174
183
|
colorize: true,
|
|
175
184
|
translateTime: 'HH:MM:ss.l',
|
package/dist/logger.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAa,EAAe,KAAK,MAAM,EAAsB,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAa,EAAe,KAAK,MAAM,EAAsB,MAAM,MAAM,CAAC;AAI1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAyDpD,UAAU,oBAAoB;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAwHD,wBAAgB,YAAY,CAAC,GAAG,EAAE,iBAAiB,EAAE,OAAO,GAAE,oBAAyB,GAAG,MAAM,CAkE/F"}
|
package/dist/logger.js
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import pino, { destination } from 'pino';
|
|
2
2
|
import { Transform } from 'node:stream';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
3
4
|
import { ensureWritableTempDirectory } from './env.js';
|
|
5
|
+
function getRequire() {
|
|
6
|
+
// Check if we're in CJS context (require.main exists)
|
|
7
|
+
if (typeof require !== 'undefined' && typeof require.main !== 'undefined') {
|
|
8
|
+
// CJS context - use require directly
|
|
9
|
+
return require;
|
|
10
|
+
}
|
|
11
|
+
// ESM context - use createRequire with import.meta.url
|
|
12
|
+
// TypeScript will complain in CJS builds, but this code only runs in ESM
|
|
13
|
+
// @ts-ignore - import.meta is ESM-only, TypeScript error in CJS is expected
|
|
14
|
+
return createRequire(import.meta.url);
|
|
15
|
+
}
|
|
4
16
|
/**
|
|
5
17
|
* Detects if we're running in a container (deployed environment).
|
|
6
18
|
* This is used to determine log format: pretty for local dev, JSON for containers.
|
|
@@ -14,7 +26,7 @@ import { ensureWritableTempDirectory } from './env.js';
|
|
|
14
26
|
* - Has routing key (local dev always has a routing key)
|
|
15
27
|
* - Running via tsx/node directly (not in Docker)
|
|
16
28
|
*/
|
|
17
|
-
function isRunningInContainer() {
|
|
29
|
+
function isRunningInContainer(env) {
|
|
18
30
|
// Explicit container indicators (highest priority)
|
|
19
31
|
if (process.env.DOTNET_RUNNING_IN_CONTAINER === 'true' ||
|
|
20
32
|
process.env.CONTAINER === 'beamable' ||
|
|
@@ -22,9 +34,10 @@ function isRunningInContainer() {
|
|
|
22
34
|
!!process.env.KUBERNETES_SERVICE_HOST) {
|
|
23
35
|
return true;
|
|
24
36
|
}
|
|
25
|
-
// If we have a routing key, we're definitely running locally
|
|
26
|
-
// Local dev always
|
|
27
|
-
|
|
37
|
+
// If we have a routing key (from env object or process.env), we're definitely running locally
|
|
38
|
+
// Local dev always has a routing key (either set explicitly or auto-generated)
|
|
39
|
+
const hasRoutingKey = env?.routingKey || process.env.NAME_PREFIX || process.env.ROUTING_KEY;
|
|
40
|
+
if (hasRoutingKey) {
|
|
28
41
|
return false; // Local development
|
|
29
42
|
}
|
|
30
43
|
// If we have Beamable env vars but NO routing key, we're in a deployed container
|
|
@@ -151,7 +164,7 @@ function createBeamableLogFormatter() {
|
|
|
151
164
|
}
|
|
152
165
|
export function createLogger(env, options = {}) {
|
|
153
166
|
const configuredDestination = options.destinationPath ?? process.env.LOG_PATH;
|
|
154
|
-
const inContainer = isRunningInContainer();
|
|
167
|
+
const inContainer = isRunningInContainer(env);
|
|
155
168
|
const pinoOptions = {
|
|
156
169
|
name: options.name ?? 'beamable-node-runtime',
|
|
157
170
|
level: env.logLevel,
|
|
@@ -183,7 +196,9 @@ export function createLogger(env, options = {}) {
|
|
|
183
196
|
// If not available, fall back to default Pino JSON output
|
|
184
197
|
try {
|
|
185
198
|
// Check if pino-pretty is available
|
|
186
|
-
|
|
199
|
+
// Use getRequire() which handles both CJS and ESM contexts
|
|
200
|
+
const requireFn = getRequire();
|
|
201
|
+
const pinoPretty = requireFn('pino-pretty');
|
|
187
202
|
// Create a pretty stream with formatting options
|
|
188
203
|
const prettyStream = pinoPretty({
|
|
189
204
|
colorize: true,
|
|
@@ -196,6 +211,7 @@ export function createLogger(env, options = {}) {
|
|
|
196
211
|
}
|
|
197
212
|
catch {
|
|
198
213
|
// pino-pretty not available, use default Pino output (JSON but readable)
|
|
214
|
+
// This is expected if pino-pretty isn't installed, so we silently fall back
|
|
199
215
|
return pino(pinoOptions, process.stdout);
|
|
200
216
|
}
|
|
201
217
|
}
|
package/dist/logger.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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","sourcesContent":["import pino, { destination, type Logger, type LoggerOptions } from 'pino';\r\nimport { Transform } from 'node:stream';\r\nimport { ensureWritableTempDirectory } from './env.js';\r\nimport type { EnvironmentConfig } from './types.js';\r\n\r\n/**\r\n * Detects if we're running in a container (deployed environment).\r\n * This is used to determine log format: pretty for local dev, JSON for containers.\r\n * \r\n * Key indicators we're in a container:\r\n * - Has CID, PID, HOST, SECRET (Beamable env vars)\r\n * - NO routing key (deployed services don't use routing keys)\r\n * - OR explicit container indicators (DOTNET_RUNNING_IN_CONTAINER, ECS, K8s)\r\n * \r\n * Key indicators we're running locally:\r\n * - Has routing key (local dev always has a routing key)\r\n * - Running via tsx/node directly (not in Docker)\r\n */\r\nfunction isRunningInContainer(): boolean {\r\n // Explicit container indicators (highest priority)\r\n if (\r\n process.env.DOTNET_RUNNING_IN_CONTAINER === 'true' ||\r\n process.env.CONTAINER === 'beamable' ||\r\n !!process.env.ECS_CONTAINER_METADATA_URI ||\r\n !!process.env.KUBERNETES_SERVICE_HOST\r\n ) {\r\n return true;\r\n }\r\n \r\n // If we have a routing key, we're definitely running locally (not in a deployed container)\r\n // Local dev always sets NAME_PREFIX or ROUTING_KEY\r\n if (process.env.NAME_PREFIX || process.env.ROUTING_KEY) {\r\n return false; // Local development\r\n }\r\n \r\n // If we have Beamable env vars but NO routing key, we're in a deployed container\r\n const hasBeamableEnvVars = !!(process.env.CID && process.env.PID && process.env.HOST && process.env.SECRET);\r\n if (hasBeamableEnvVars) {\r\n return true; // Deployed container (has env vars but no routing key)\r\n }\r\n \r\n // Default: assume local development\r\n return false;\r\n}\r\n\r\ninterface LoggerFactoryOptions {\r\n name?: string;\r\n destinationPath?: string;\r\n}\r\n\r\n/**\r\n * Maps Pino log levels to Beamable log levels\r\n * Pino levels: 10=trace, 20=debug, 30=info, 40=warn, 50=error, 60=fatal\r\n * Beamable levels: Debug, Info, Warning, Error, Fatal\r\n */\r\nfunction mapPinoLevelToBeamableLevel(level: number): string {\r\n switch (level) {\r\n case 10: // trace\r\n return 'Debug';\r\n case 20: // debug\r\n return 'Debug';\r\n case 30: // info\r\n return 'Info';\r\n case 40: // warn\r\n return 'Warning';\r\n case 50: // error\r\n return 'Error';\r\n case 60: // fatal\r\n return 'Fatal';\r\n default:\r\n return 'Info';\r\n }\r\n}\r\n\r\n/**\r\n * Creates a transform stream that converts Pino JSON logs to Beamable's expected format.\r\n * Beamable expects logs with __t (timestamp), __l (level), and __m (message) fields.\r\n * Pino writes JSON strings (one per line) to the stream.\r\n */\r\nfunction createBeamableLogFormatter(): Transform {\r\n return new Transform({\r\n objectMode: false, // Pino writes strings, not objects\r\n transform(chunk: Buffer, _encoding, callback) {\r\n try {\r\n const line = chunk.toString();\r\n // Skip empty lines\r\n if (!line.trim()) {\r\n callback();\r\n return;\r\n }\r\n \r\n // Parse Pino's JSON log line\r\n const pinoLog = JSON.parse(line);\r\n \r\n // Extract timestamp - Pino uses 'time' field (ISO 8601 string or milliseconds)\r\n // Convert to ISO 8601 string for Beamable\r\n let timestamp: string;\r\n if (typeof pinoLog.time === 'string') {\r\n timestamp = pinoLog.time;\r\n } else if (typeof pinoLog.time === 'number') {\r\n timestamp = new Date(pinoLog.time).toISOString();\r\n } else {\r\n timestamp = new Date().toISOString();\r\n }\r\n \r\n // Map Pino level to Beamable level\r\n const level = mapPinoLevelToBeamableLevel(pinoLog.level);\r\n \r\n // Build the message - combine msg with any additional fields\r\n // Pino's 'msg' field contains the log message\r\n const messageParts: string[] = [];\r\n if (pinoLog.msg) {\r\n messageParts.push(pinoLog.msg);\r\n }\r\n \r\n // Include error information if present\r\n if (pinoLog.err) {\r\n const err = pinoLog.err;\r\n const errMsg = err.message || err.msg || 'Error';\r\n const errStack = err.stack ? `\\n${err.stack}` : '';\r\n messageParts.push(`${errMsg}${errStack}`);\r\n }\r\n \r\n // Build the Beamable log format\r\n const beamableLog: Record<string, unknown> = {\r\n __t: timestamp,\r\n __l: level,\r\n __m: messageParts.length > 0 ? messageParts.join(' ') : 'No message',\r\n };\r\n \r\n // Include additional context fields that might be useful\r\n // These are included in the message object but not as top-level fields\r\n const contextFields: Record<string, unknown> = {};\r\n if (pinoLog.cid) contextFields.cid = pinoLog.cid;\r\n if (pinoLog.pid) contextFields.pid = pinoLog.pid;\r\n if (pinoLog.routingKey) contextFields.routingKey = pinoLog.routingKey;\r\n if (pinoLog.service) contextFields.service = pinoLog.service;\r\n if (pinoLog.component) contextFields.component = pinoLog.component;\r\n \r\n // Include any other fields that aren't standard Pino fields\r\n const standardPinoFields = ['level', 'time', 'pid', 'hostname', 'name', 'msg', 'err', 'v', 'cid', 'pid', 'routingKey', 'sdkVersionExecution', 'service', 'component'];\r\n for (const [key, value] of Object.entries(pinoLog)) {\r\n if (!standardPinoFields.includes(key) && value !== undefined && value !== null) {\r\n contextFields[key] = value;\r\n }\r\n }\r\n \r\n // If there are context fields, include them in the log\r\n if (Object.keys(contextFields).length > 0) {\r\n beamableLog.__c = contextFields;\r\n }\r\n \r\n // Output as a single-line JSON string (required for CloudWatch)\r\n const output = JSON.stringify(beamableLog) + '\\n';\r\n callback(null, Buffer.from(output, 'utf8'));\r\n } catch (error) {\r\n // If parsing fails, output a fallback log entry\r\n const fallbackLog = {\r\n __t: new Date().toISOString(),\r\n __l: 'Error',\r\n __m: `Failed to parse log entry: ${chunk.toString().substring(0, 200)}`,\r\n };\r\n callback(null, Buffer.from(JSON.stringify(fallbackLog) + '\\n', 'utf8'));\r\n }\r\n },\r\n });\r\n}\r\n\r\nexport function createLogger(env: EnvironmentConfig, options: LoggerFactoryOptions = {}): Logger {\r\n const configuredDestination = options.destinationPath ?? process.env.LOG_PATH;\r\n const inContainer = isRunningInContainer();\r\n\r\n const pinoOptions: LoggerOptions = {\r\n name: options.name ?? 'beamable-node-runtime',\r\n level: env.logLevel,\r\n base: {\r\n cid: env.cid,\r\n pid: env.pid,\r\n routingKey: env.routingKey ?? null,\r\n sdkVersionExecution: env.sdkVersionExecution,\r\n },\r\n redact: {\r\n paths: ['secret', 'refreshToken'],\r\n censor: '***',\r\n },\r\n // Use timestamp in milliseconds (Pino default) for accurate conversion\r\n timestamp: pino.stdTimeFunctions.isoTime,\r\n };\r\n\r\n // For deployed services, always log to stdout so container orchestrator can collect logs\r\n // For local development, log to stdout unless a specific file path is provided\r\n if (!configuredDestination || configuredDestination === '-' || configuredDestination === 'stdout' || configuredDestination === 'console') {\r\n if (inContainer) {\r\n // In containers: Use Beamable JSON format for CloudWatch collection\r\n const beamableFormatter = createBeamableLogFormatter();\r\n beamableFormatter.pipe(process.stdout);\r\n return pino(pinoOptions, beamableFormatter);\r\n } else {\r\n // Local development: Use Pino's pretty printing for human-readable logs\r\n // Try to use pino-pretty if available (optional dependency)\r\n // If not available, fall back to default Pino JSON output\r\n try {\r\n // Check if pino-pretty is available\r\n const pinoPretty = require('pino-pretty');\r\n // Create a pretty stream with formatting options\r\n const prettyStream = pinoPretty({\r\n colorize: true,\r\n translateTime: 'HH:MM:ss.l',\r\n ignore: 'pid,hostname',\r\n singleLine: false,\r\n });\r\n // Use pino with the pretty stream\r\n return pino(pinoOptions, prettyStream);\r\n } catch {\r\n // pino-pretty not available, use default Pino output (JSON but readable)\r\n return pino(pinoOptions, process.stdout);\r\n }\r\n }\r\n }\r\n\r\n // For file logging: Use Beamable format in containers, default Pino format locally\r\n const resolvedDestination = configuredDestination === 'temp' ? ensureWritableTempDirectory() : configuredDestination;\r\n if (inContainer) {\r\n const beamableFormatter = createBeamableLogFormatter();\r\n const fileStream = destination({ dest: resolvedDestination, mkdir: true, append: true, sync: false });\r\n beamableFormatter.pipe(fileStream as unknown as NodeJS.WritableStream);\r\n return pino(pinoOptions, beamableFormatter);\r\n } else {\r\n const fileStream = destination({ dest: resolvedDestination, mkdir: true, append: true, sync: false });\r\n return pino(pinoOptions, fileStream);\r\n }\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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","sourcesContent":["import pino, { destination, type Logger, type LoggerOptions } from 'pino';\r\nimport { Transform } from 'node:stream';\r\nimport { createRequire } from 'node:module';\r\nimport { ensureWritableTempDirectory } from './env.js';\r\nimport type { EnvironmentConfig } from './types.js';\r\n\r\n// Helper to get require function that works in both CJS and ESM\r\ndeclare const require: any;\r\nfunction getRequire(): any {\r\n // Check if we're in CJS context (require.main exists)\r\n if (typeof require !== 'undefined' && typeof require.main !== 'undefined') {\r\n // CJS context - use require directly\r\n return require;\r\n }\r\n // ESM context - use createRequire with import.meta.url\r\n // TypeScript will complain in CJS builds, but this code only runs in ESM\r\n // @ts-ignore - import.meta is ESM-only, TypeScript error in CJS is expected\r\n return createRequire(import.meta.url);\r\n}\r\n\r\n/**\r\n * Detects if we're running in a container (deployed environment).\r\n * This is used to determine log format: pretty for local dev, JSON for containers.\r\n * \r\n * Key indicators we're in a container:\r\n * - Has CID, PID, HOST, SECRET (Beamable env vars)\r\n * - NO routing key (deployed services don't use routing keys)\r\n * - OR explicit container indicators (DOTNET_RUNNING_IN_CONTAINER, ECS, K8s)\r\n * \r\n * Key indicators we're running locally:\r\n * - Has routing key (local dev always has a routing key)\r\n * - Running via tsx/node directly (not in Docker)\r\n */\r\nfunction isRunningInContainer(env?: EnvironmentConfig): boolean {\r\n // Explicit container indicators (highest priority)\r\n if (\r\n process.env.DOTNET_RUNNING_IN_CONTAINER === 'true' ||\r\n process.env.CONTAINER === 'beamable' ||\r\n !!process.env.ECS_CONTAINER_METADATA_URI ||\r\n !!process.env.KUBERNETES_SERVICE_HOST\r\n ) {\r\n return true;\r\n }\r\n \r\n // If we have a routing key (from env object or process.env), we're definitely running locally\r\n // Local dev always has a routing key (either set explicitly or auto-generated)\r\n const hasRoutingKey = env?.routingKey || process.env.NAME_PREFIX || process.env.ROUTING_KEY;\r\n if (hasRoutingKey) {\r\n return false; // Local development\r\n }\r\n \r\n // If we have Beamable env vars but NO routing key, we're in a deployed container\r\n const hasBeamableEnvVars = !!(process.env.CID && process.env.PID && process.env.HOST && process.env.SECRET);\r\n if (hasBeamableEnvVars) {\r\n return true; // Deployed container (has env vars but no routing key)\r\n }\r\n \r\n // Default: assume local development\r\n return false;\r\n}\r\n\r\ninterface LoggerFactoryOptions {\r\n name?: string;\r\n destinationPath?: string;\r\n}\r\n\r\n/**\r\n * Maps Pino log levels to Beamable log levels\r\n * Pino levels: 10=trace, 20=debug, 30=info, 40=warn, 50=error, 60=fatal\r\n * Beamable levels: Debug, Info, Warning, Error, Fatal\r\n */\r\nfunction mapPinoLevelToBeamableLevel(level: number): string {\r\n switch (level) {\r\n case 10: // trace\r\n return 'Debug';\r\n case 20: // debug\r\n return 'Debug';\r\n case 30: // info\r\n return 'Info';\r\n case 40: // warn\r\n return 'Warning';\r\n case 50: // error\r\n return 'Error';\r\n case 60: // fatal\r\n return 'Fatal';\r\n default:\r\n return 'Info';\r\n }\r\n}\r\n\r\n/**\r\n * Creates a transform stream that converts Pino JSON logs to Beamable's expected format.\r\n * Beamable expects logs with __t (timestamp), __l (level), and __m (message) fields.\r\n * Pino writes JSON strings (one per line) to the stream.\r\n */\r\nfunction createBeamableLogFormatter(): Transform {\r\n return new Transform({\r\n objectMode: false, // Pino writes strings, not objects\r\n transform(chunk: Buffer, _encoding, callback) {\r\n try {\r\n const line = chunk.toString();\r\n // Skip empty lines\r\n if (!line.trim()) {\r\n callback();\r\n return;\r\n }\r\n \r\n // Parse Pino's JSON log line\r\n const pinoLog = JSON.parse(line);\r\n \r\n // Extract timestamp - Pino uses 'time' field (ISO 8601 string or milliseconds)\r\n // Convert to ISO 8601 string for Beamable\r\n let timestamp: string;\r\n if (typeof pinoLog.time === 'string') {\r\n timestamp = pinoLog.time;\r\n } else if (typeof pinoLog.time === 'number') {\r\n timestamp = new Date(pinoLog.time).toISOString();\r\n } else {\r\n timestamp = new Date().toISOString();\r\n }\r\n \r\n // Map Pino level to Beamable level\r\n const level = mapPinoLevelToBeamableLevel(pinoLog.level);\r\n \r\n // Build the message - combine msg with any additional fields\r\n // Pino's 'msg' field contains the log message\r\n const messageParts: string[] = [];\r\n if (pinoLog.msg) {\r\n messageParts.push(pinoLog.msg);\r\n }\r\n \r\n // Include error information if present\r\n if (pinoLog.err) {\r\n const err = pinoLog.err;\r\n const errMsg = err.message || err.msg || 'Error';\r\n const errStack = err.stack ? `\\n${err.stack}` : '';\r\n messageParts.push(`${errMsg}${errStack}`);\r\n }\r\n \r\n // Build the Beamable log format\r\n const beamableLog: Record<string, unknown> = {\r\n __t: timestamp,\r\n __l: level,\r\n __m: messageParts.length > 0 ? messageParts.join(' ') : 'No message',\r\n };\r\n \r\n // Include additional context fields that might be useful\r\n // These are included in the message object but not as top-level fields\r\n const contextFields: Record<string, unknown> = {};\r\n if (pinoLog.cid) contextFields.cid = pinoLog.cid;\r\n if (pinoLog.pid) contextFields.pid = pinoLog.pid;\r\n if (pinoLog.routingKey) contextFields.routingKey = pinoLog.routingKey;\r\n if (pinoLog.service) contextFields.service = pinoLog.service;\r\n if (pinoLog.component) contextFields.component = pinoLog.component;\r\n \r\n // Include any other fields that aren't standard Pino fields\r\n const standardPinoFields = ['level', 'time', 'pid', 'hostname', 'name', 'msg', 'err', 'v', 'cid', 'pid', 'routingKey', 'sdkVersionExecution', 'service', 'component'];\r\n for (const [key, value] of Object.entries(pinoLog)) {\r\n if (!standardPinoFields.includes(key) && value !== undefined && value !== null) {\r\n contextFields[key] = value;\r\n }\r\n }\r\n \r\n // If there are context fields, include them in the log\r\n if (Object.keys(contextFields).length > 0) {\r\n beamableLog.__c = contextFields;\r\n }\r\n \r\n // Output as a single-line JSON string (required for CloudWatch)\r\n const output = JSON.stringify(beamableLog) + '\\n';\r\n callback(null, Buffer.from(output, 'utf8'));\r\n } catch (error) {\r\n // If parsing fails, output a fallback log entry\r\n const fallbackLog = {\r\n __t: new Date().toISOString(),\r\n __l: 'Error',\r\n __m: `Failed to parse log entry: ${chunk.toString().substring(0, 200)}`,\r\n };\r\n callback(null, Buffer.from(JSON.stringify(fallbackLog) + '\\n', 'utf8'));\r\n }\r\n },\r\n });\r\n}\r\n\r\nexport function createLogger(env: EnvironmentConfig, options: LoggerFactoryOptions = {}): Logger {\r\n const configuredDestination = options.destinationPath ?? process.env.LOG_PATH;\r\n const inContainer = isRunningInContainer(env);\r\n\r\n const pinoOptions: LoggerOptions = {\r\n name: options.name ?? 'beamable-node-runtime',\r\n level: env.logLevel,\r\n base: {\r\n cid: env.cid,\r\n pid: env.pid,\r\n routingKey: env.routingKey ?? null,\r\n sdkVersionExecution: env.sdkVersionExecution,\r\n },\r\n redact: {\r\n paths: ['secret', 'refreshToken'],\r\n censor: '***',\r\n },\r\n // Use timestamp in milliseconds (Pino default) for accurate conversion\r\n timestamp: pino.stdTimeFunctions.isoTime,\r\n };\r\n\r\n // For deployed services, always log to stdout so container orchestrator can collect logs\r\n // For local development, log to stdout unless a specific file path is provided\r\n if (!configuredDestination || configuredDestination === '-' || configuredDestination === 'stdout' || configuredDestination === 'console') {\r\n if (inContainer) {\r\n // In containers: Use Beamable JSON format for CloudWatch collection\r\n const beamableFormatter = createBeamableLogFormatter();\r\n beamableFormatter.pipe(process.stdout);\r\n return pino(pinoOptions, beamableFormatter);\r\n } else {\r\n // Local development: Use Pino's pretty printing for human-readable logs\r\n // Try to use pino-pretty if available (optional dependency)\r\n // If not available, fall back to default Pino JSON output\r\n try {\r\n // Check if pino-pretty is available\r\n // Use getRequire() which handles both CJS and ESM contexts\r\n const requireFn = getRequire();\r\n const pinoPretty = requireFn('pino-pretty');\r\n // Create a pretty stream with formatting options\r\n const prettyStream = pinoPretty({\r\n colorize: true,\r\n translateTime: 'HH:MM:ss.l',\r\n ignore: 'pid,hostname',\r\n singleLine: false,\r\n });\r\n // Use pino with the pretty stream\r\n return pino(pinoOptions, prettyStream);\r\n } catch {\r\n // pino-pretty not available, use default Pino output (JSON but readable)\r\n // This is expected if pino-pretty isn't installed, so we silently fall back\r\n return pino(pinoOptions, process.stdout);\r\n }\r\n }\r\n }\r\n\r\n // For file logging: Use Beamable format in containers, default Pino format locally\r\n const resolvedDestination = configuredDestination === 'temp' ? ensureWritableTempDirectory() : configuredDestination;\r\n if (inContainer) {\r\n const beamableFormatter = createBeamableLogFormatter();\r\n const fileStream = destination({ dest: resolvedDestination, mkdir: true, append: true, sync: false });\r\n beamableFormatter.pipe(fileStream as unknown as NodeJS.WritableStream);\r\n return pino(pinoOptions, beamableFormatter);\r\n } else {\r\n const fileStream = destination({ dest: resolvedDestination, mkdir: true, append: true, sync: false });\r\n return pino(pinoOptions, fileStream);\r\n }\r\n}\r\n"]}
|
package/dist/runtime.cjs
CHANGED
|
@@ -54,7 +54,7 @@ class MicroserviceRuntime {
|
|
|
54
54
|
this.requester = new requester_js_1.GatewayRequester(this.webSocket, this.logger);
|
|
55
55
|
this.authManager = new auth_js_1.AuthManager(this.env, this.requester);
|
|
56
56
|
this.requester.on('event', (envelope) => this.handleEvent(envelope));
|
|
57
|
-
if (!isRunningInContainer() && this.services.length > 0) {
|
|
57
|
+
if (!isRunningInContainer(this.env) && this.services.length > 0) {
|
|
58
58
|
this.discovery = new discovery_js_1.DiscoveryBroadcaster({
|
|
59
59
|
env: this.env,
|
|
60
60
|
serviceName: this.services[0].definition.name,
|
|
@@ -163,7 +163,7 @@ class MicroserviceRuntime {
|
|
|
163
163
|
throw new Error('Unexpected missing service definition during registration.');
|
|
164
164
|
}
|
|
165
165
|
const options = (_b = (0, decorators_js_1.getServiceOptions)(primary.ctor)) !== null && _b !== void 0 ? _b : {};
|
|
166
|
-
const isDeployed = isRunningInContainer();
|
|
166
|
+
const isDeployed = isRunningInContainer(this.env);
|
|
167
167
|
const request = {
|
|
168
168
|
type: 'basic',
|
|
169
169
|
name: primary.qualifiedName,
|
|
@@ -531,7 +531,7 @@ class MicroserviceRuntime {
|
|
|
531
531
|
if (!service) {
|
|
532
532
|
return;
|
|
533
533
|
}
|
|
534
|
-
if (isRunningInContainer()) {
|
|
534
|
+
if (isRunningInContainer(this.env)) {
|
|
535
535
|
return;
|
|
536
536
|
}
|
|
537
537
|
const docsUrl = buildDocsPortalUrl(this.env, service.definition);
|
|
@@ -608,16 +608,22 @@ function debugLog(...args) {
|
|
|
608
608
|
console.error(...args);
|
|
609
609
|
}
|
|
610
610
|
}
|
|
611
|
-
function isRunningInContainer() {
|
|
611
|
+
function isRunningInContainer(env) {
|
|
612
|
+
if (process.env.DOTNET_RUNNING_IN_CONTAINER === 'true' ||
|
|
613
|
+
process.env.CONTAINER === 'beamable' ||
|
|
614
|
+
!!process.env.ECS_CONTAINER_METADATA_URI ||
|
|
615
|
+
!!process.env.KUBERNETES_SERVICE_HOST) {
|
|
616
|
+
return true;
|
|
617
|
+
}
|
|
618
|
+
const hasRoutingKey = (env === null || env === void 0 ? void 0 : env.routingKey) || process.env.NAME_PREFIX || process.env.ROUTING_KEY;
|
|
619
|
+
if (hasRoutingKey) {
|
|
620
|
+
return false;
|
|
621
|
+
}
|
|
612
622
|
const hasBeamableEnvVars = !!(process.env.CID && process.env.PID && process.env.HOST && process.env.SECRET);
|
|
613
|
-
|
|
614
|
-
if (hasBeamableEnvVars && hasNoRoutingKey) {
|
|
623
|
+
if (hasBeamableEnvVars) {
|
|
615
624
|
return true;
|
|
616
625
|
}
|
|
617
|
-
return
|
|
618
|
-
process.env.CONTAINER === 'beamable' ||
|
|
619
|
-
!!process.env.ECS_CONTAINER_METADATA_URI ||
|
|
620
|
-
!!process.env.KUBERNETES_SERVICE_HOST);
|
|
626
|
+
return false;
|
|
621
627
|
}
|
|
622
628
|
function normalizeScopes(scopes) {
|
|
623
629
|
const normalized = new Set();
|
package/dist/runtime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,iBAAiB,EAOlB,MAAM,YAAY,CAAC;AA2BpB,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoB;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,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;IA8C7B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA6DtB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;YASjB,sBAAsB;YA2CtB,qBAAqB;YAarB,eAAe;YA+Gf,WAAW;IA8BzB,OAAO,CAAC,gBAAgB;IAkFxB,OAAO,CAAC,kBAAkB;YAUZ,QAAQ;IAsDtB,OAAO,CAAC,wBAAwB;YAmBlB,mBAAmB;YAiBnB,sBAAsB;YAStB,wBAAwB;YAoBxB,mBAAmB;IA0GjC,OAAO,CAAC,kBAAkB;IAgB1B,OAAO,CAAC,gBAAgB;YA+BV,6BAA6B;IA6B3C,OAAO,CAAC,kBAAkB;CAK3B;
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,iBAAiB,EAOlB,MAAM,YAAY,CAAC;AA2BpB,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoB;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,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;IA8C7B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA6DtB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;YASjB,sBAAsB;YA2CtB,qBAAqB;YAarB,eAAe;YA+Gf,WAAW;IA8BzB,OAAO,CAAC,gBAAgB;IAkFxB,OAAO,CAAC,kBAAkB;YAUZ,QAAQ;IAsDtB,OAAO,CAAC,wBAAwB;YAmBlB,mBAAmB;YAiBnB,sBAAsB;YAStB,wBAAwB;YAoBxB,mBAAmB;IA0GjC,OAAO,CAAC,kBAAkB;IAgB1B,OAAO,CAAC,gBAAgB;YA+BV,6BAA6B;IA6B3C,OAAO,CAAC,kBAAkB;CAK3B;AAsGD,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,CA4BT"}
|
package/dist/runtime.js
CHANGED
|
@@ -58,7 +58,7 @@ export class MicroserviceRuntime {
|
|
|
58
58
|
this.requester = new GatewayRequester(this.webSocket, this.logger);
|
|
59
59
|
this.authManager = new AuthManager(this.env, this.requester);
|
|
60
60
|
this.requester.on('event', (envelope) => this.handleEvent(envelope));
|
|
61
|
-
if (!isRunningInContainer() && this.services.length > 0) {
|
|
61
|
+
if (!isRunningInContainer(this.env) && this.services.length > 0) {
|
|
62
62
|
this.discovery = new DiscoveryBroadcaster({
|
|
63
63
|
env: this.env,
|
|
64
64
|
serviceName: this.services[0].definition.name,
|
|
@@ -202,7 +202,7 @@ export class MicroserviceRuntime {
|
|
|
202
202
|
// The gateway's ServiceIdentity.fullNameNoType lowercases the service name when creating bindings,
|
|
203
203
|
// but the registration request should use the original case (as C# does)
|
|
204
204
|
// This ensures the service name in the registration matches what the portal expects
|
|
205
|
-
const isDeployed = isRunningInContainer();
|
|
205
|
+
const isDeployed = isRunningInContainer(this.env);
|
|
206
206
|
// For deployed services, routingKey should be null/undefined (None in Scala)
|
|
207
207
|
// For local dev, routingKey should be the actual routing key string
|
|
208
208
|
// The backend expects Option[String] = None for deployed services
|
|
@@ -609,7 +609,7 @@ export class MicroserviceRuntime {
|
|
|
609
609
|
}
|
|
610
610
|
// Only print helpful URLs in local development (not in containers)
|
|
611
611
|
// In containers, we want only JSON logs for proper log collection
|
|
612
|
-
if (isRunningInContainer()) {
|
|
612
|
+
if (isRunningInContainer(this.env)) {
|
|
613
613
|
return;
|
|
614
614
|
}
|
|
615
615
|
const docsUrl = buildDocsPortalUrl(this.env, service.definition);
|
|
@@ -684,24 +684,32 @@ function enforceAccess(access, ctx) {
|
|
|
684
684
|
* In containers, we want only Pino JSON logs to stdout for proper log collection.
|
|
685
685
|
*/
|
|
686
686
|
function debugLog(...args) {
|
|
687
|
+
// Check process.env directly since this is a standalone function
|
|
687
688
|
if (!isRunningInContainer()) {
|
|
688
689
|
console.error(...args);
|
|
689
690
|
}
|
|
690
691
|
}
|
|
691
|
-
function isRunningInContainer() {
|
|
692
|
-
//
|
|
693
|
-
|
|
694
|
-
// This is more reliable than checking for DOTNET_RUNNING_IN_CONTAINER (which is C#-specific)
|
|
695
|
-
const hasBeamableEnvVars = !!(process.env.CID && process.env.PID && process.env.HOST && process.env.SECRET);
|
|
696
|
-
const hasNoRoutingKey = !process.env.NAME_PREFIX && !process.env.ROUTING_KEY;
|
|
697
|
-
if (hasBeamableEnvVars && hasNoRoutingKey) {
|
|
698
|
-
return true; // Deployed container
|
|
699
|
-
}
|
|
700
|
-
// Fallback checks for other container indicators
|
|
701
|
-
return (process.env.DOTNET_RUNNING_IN_CONTAINER === 'true' ||
|
|
692
|
+
function isRunningInContainer(env) {
|
|
693
|
+
// Explicit container indicators (highest priority)
|
|
694
|
+
if (process.env.DOTNET_RUNNING_IN_CONTAINER === 'true' ||
|
|
702
695
|
process.env.CONTAINER === 'beamable' ||
|
|
703
696
|
!!process.env.ECS_CONTAINER_METADATA_URI ||
|
|
704
|
-
!!process.env.KUBERNETES_SERVICE_HOST)
|
|
697
|
+
!!process.env.KUBERNETES_SERVICE_HOST) {
|
|
698
|
+
return true;
|
|
699
|
+
}
|
|
700
|
+
// If we have a routing key (from env object or process.env), we're definitely running locally
|
|
701
|
+
// Local dev always has a routing key (either set explicitly or auto-generated)
|
|
702
|
+
const hasRoutingKey = env?.routingKey || process.env.NAME_PREFIX || process.env.ROUTING_KEY;
|
|
703
|
+
if (hasRoutingKey) {
|
|
704
|
+
return false; // Local development
|
|
705
|
+
}
|
|
706
|
+
// If we have Beamable env vars but NO routing key, we're in a deployed container
|
|
707
|
+
const hasBeamableEnvVars = !!(process.env.CID && process.env.PID && process.env.HOST && process.env.SECRET);
|
|
708
|
+
if (hasBeamableEnvVars) {
|
|
709
|
+
return true; // Deployed container (has env vars but no routing key)
|
|
710
|
+
}
|
|
711
|
+
// Default: assume local development
|
|
712
|
+
return false;
|
|
705
713
|
}
|
|
706
714
|
function normalizeScopes(scopes) {
|
|
707
715
|
const normalized = new Set();
|
package/dist/runtime.js.map
CHANGED
|
@@ -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';\r\nimport { BeamableWebSocket } from './websocket.js';\r\nimport { GatewayRequester } from './requester.js';\r\nimport { AuthManager } from './auth.js';\r\nimport { createLogger } from './logger.js';\r\nimport { loadEnvironmentConfig } from './env.js';\r\nimport { listRegisteredServices, getServiceOptions, getConfigureServicesHandlers, getInitializeServicesHandlers } from './decorators.js';\r\nimport { generateOpenApiDocument } from './docs.js';\r\nimport { DiscoveryBroadcaster } from './discovery.js';\r\nimport { BeamableRuntimeError, MissingScopesError, UnauthorizedUserError, UnknownRouteError } from './errors.js';\r\nimport type {\r\n EnvironmentConfig,\r\n RequestContext,\r\n ServiceDefinition,\r\n ServiceCallableMetadata,\r\n GatewayResponse,\r\n WebsocketEventEnvelope,\r\n ServiceAccess,\r\n} from './types.js';\r\nimport type { Logger } from 'pino';\r\nimport { BeamableServiceManager } from './services.js';\r\nimport {\r\n DependencyBuilder,\r\n LOGGER_TOKEN,\r\n ENVIRONMENT_CONFIG_TOKEN,\r\n REQUEST_CONTEXT_TOKEN,\r\n BEAMABLE_SERVICES_TOKEN,\r\n ServiceProvider,\r\n MutableDependencyScope,\r\n} from './dependency.js';\r\nimport { hostToHttpUrl, hostToPortalUrl } from './utils/urls.js';\r\nimport { FederationRegistry, getFederationComponents, getFederatedInventoryMetadata } from './federation.js';\r\nimport type { FederatedRequestContext } from './federation.js';\r\nimport { createServer, type Server } from 'node:http';\r\n\r\ninterface ServiceInstance {\r\n definition: ServiceDefinition;\r\n instance: Record<string, unknown>;\r\n configureHandlers: ReturnType<typeof getConfigureServicesHandlers>;\r\n initializeHandlers: ReturnType<typeof getInitializeServicesHandlers>;\r\n provider?: ServiceProvider;\r\n logger: Logger;\r\n federationRegistry: FederationRegistry;\r\n}\r\n\r\nexport class MicroserviceRuntime {\r\n private readonly env: EnvironmentConfig;\r\n private readonly logger: Logger;\r\n private readonly services: ServiceInstance[];\r\n private readonly webSocket: BeamableWebSocket;\r\n private readonly requester: GatewayRequester;\r\n private readonly authManager: AuthManager;\r\n private readonly discovery?: DiscoveryBroadcaster;\r\n private readonly microServiceId = randomUUID();\r\n private readonly serviceManager: BeamableServiceManager;\r\n private healthCheckServer?: Server;\r\n private isReady: boolean = false;\r\n\r\n constructor(env?: EnvironmentConfig) {\r\n this.env = env ?? loadEnvironmentConfig();\r\n this.logger = createLogger(this.env, { name: 'beamable-node-microservice' });\r\n this.serviceManager = new BeamableServiceManager(this.env, this.logger);\r\n\r\n const registered = listRegisteredServices();\r\n if (registered.length === 0) {\r\n throw new Error('No microservices registered. Use the @Microservice decorator to register at least one class.');\r\n }\r\n this.services = registered.map((definition) => {\r\n const instance = new definition.ctor() as Record<string, unknown>;\r\n const configureHandlers = getConfigureServicesHandlers(definition.ctor);\r\n const initializeHandlers = getInitializeServicesHandlers(definition.ctor);\r\n const logger = this.logger.child({ service: definition.name });\r\n const federationRegistry = new FederationRegistry(logger);\r\n const decoratedFederations = getFederationComponents(definition.ctor);\r\n for (const component of decoratedFederations) {\r\n federationRegistry.register(component);\r\n }\r\n const inventoryMetadata = getFederatedInventoryMetadata(definition.ctor);\r\n if (inventoryMetadata.length > 0) {\r\n federationRegistry.registerInventoryHandlers(instance, inventoryMetadata);\r\n }\r\n this.serviceManager.registerFederationRegistry(definition.name, federationRegistry);\r\n return { definition, instance, configureHandlers, initializeHandlers, logger, federationRegistry };\r\n });\r\n\r\n const socketUrl = this.env.host.endsWith('/socket') ? this.env.host : `${this.env.host}/socket`;\r\n this.webSocket = new BeamableWebSocket({ url: socketUrl, logger: this.logger });\r\n this.webSocket.on('message', (payload) => {\r\n this.logger.debug({ payload }, 'Runtime observed websocket frame.');\r\n });\r\n this.requester = new GatewayRequester(this.webSocket, this.logger);\r\n this.authManager = new AuthManager(this.env, this.requester);\r\n this.requester.on('event', (envelope) => this.handleEvent(envelope));\r\n\r\n if (!isRunningInContainer() && this.services.length > 0) {\r\n this.discovery = new DiscoveryBroadcaster({\r\n env: this.env,\r\n serviceName: this.services[0].definition.name,\r\n routingKey: this.env.routingKey,\r\n logger: this.logger.child({ component: 'DiscoveryBroadcaster' }),\r\n });\r\n }\r\n }\r\n\r\n async start(): Promise<void> {\r\n // Immediate console output for container logs (before logger is ready)\r\n debugLog('[BEAMABLE-NODE] MicroserviceRuntime.start() called');\r\n debugLog(`[BEAMABLE-NODE] Service count: ${this.services.length}`);\r\n \r\n this.printHelpfulUrls(this.services[0]);\r\n this.logger.info('Starting Beamable Node microservice runtime.');\r\n \r\n // Start health check server FIRST - this is critical for container health checks\r\n // Even if startup fails, the health check server must be running so we can debug\r\n debugLog('[BEAMABLE-NODE] Starting health check server...');\r\n await this.startHealthCheckServer();\r\n debugLog('[BEAMABLE-NODE] Health check server started');\r\n \r\n try {\r\n this.logger.info('Connecting to Beamable gateway...');\r\n await this.webSocket.connect();\r\n await new Promise((resolve) => setTimeout(resolve, 250));\r\n \r\n this.logger.info('Authenticating with Beamable...');\r\n await this.authManager.authenticate();\r\n \r\n this.logger.info('Initializing Beamable SDK services...');\r\n await this.serviceManager.initialize();\r\n \r\n this.logger.info('Initializing dependency providers...');\r\n await this.initializeDependencyProviders();\r\n \r\n this.logger.info('Registering service with gateway...');\r\n await this.registerService();\r\n \r\n this.logger.info('Starting discovery broadcaster...');\r\n await this.discovery?.start();\r\n \r\n // Mark as ready only after service is fully registered and discovery is started\r\n this.isReady = true;\r\n this.logger.info('Beamable microservice runtime is ready to accept traffic.');\r\n } catch (error) {\r\n // Log the error with full context but don't crash - health check server is running\r\n // This allows the container to stay alive so we can debug the issue\r\n // Debug output for local development only (in containers, logger handles this)\r\n debugLog('[BEAMABLE-NODE] FATAL ERROR during startup:');\r\n debugLog(`[BEAMABLE-NODE] Error message: ${error instanceof Error ? error.message : String(error)}`);\r\n debugLog(`[BEAMABLE-NODE] Error stack: ${error instanceof Error ? error.stack : 'No stack trace'}`);\r\n debugLog(`[BEAMABLE-NODE] isReady: ${this.isReady}`);\r\n \r\n this.logger.error(\r\n { \r\n err: error,\r\n errorMessage: error instanceof Error ? error.message : String(error),\r\n errorStack: error instanceof Error ? error.stack : undefined,\r\n isReady: this.isReady,\r\n },\r\n 'Failed to fully initialize microservice runtime. Health check will continue to return 503 until initialization completes.'\r\n );\r\n // DON'T re-throw - keep process alive so health check can show 503\r\n // This allows us to see the error in logs\r\n this.isReady = false;\r\n }\r\n }\r\n\r\n async shutdown(): Promise<void> {\r\n this.logger.info('Shutting down microservice runtime.');\r\n this.isReady = false; // Mark as not ready during shutdown\r\n this.discovery?.stop();\r\n await this.stopHealthCheckServer();\r\n this.requester.dispose();\r\n await this.webSocket.close();\r\n }\r\n\r\n private async startHealthCheckServer(): Promise<void> {\r\n // For deployed services, always start health check server (required for container health checks)\r\n // For local development, only start if healthPort is explicitly set\r\n // IMPORTANT: Always default to 6565 if HEALTH_PORT env var is not set, as this is the standard port\r\n const healthPort = this.env.healthPort || 6565;\r\n \r\n // Always start health check server if we have a valid port\r\n // The container orchestrator expects this endpoint to be available\r\n if (!healthPort || healthPort === 0) {\r\n // Health check server not needed (local development without explicit port)\r\n this.logger.debug('Health check server skipped (no healthPort set)');\r\n return;\r\n }\r\n\r\n this.healthCheckServer = createServer((req, res) => {\r\n if (req.url === '/health' && req.method === 'GET') {\r\n // Only return success if service is fully ready (registered and accepting traffic)\r\n if (this.isReady) {\r\n res.writeHead(200, { 'Content-Type': 'text/plain' });\r\n res.end('responsive');\r\n } else {\r\n // Service is still starting up\r\n res.writeHead(503, { 'Content-Type': 'text/plain' });\r\n res.end('Service Unavailable');\r\n }\r\n } else {\r\n res.writeHead(404, { 'Content-Type': 'text/plain' });\r\n res.end('Not Found');\r\n }\r\n });\r\n\r\n return new Promise((resolve, reject) => {\r\n this.healthCheckServer!.listen(healthPort, '0.0.0.0', () => {\r\n this.logger.info({ port: healthPort }, 'Health check server started on port');\r\n resolve();\r\n });\r\n this.healthCheckServer!.on('error', (err) => {\r\n this.logger.error({ err, port: healthPort }, 'Failed to start health check server');\r\n reject(err);\r\n });\r\n });\r\n }\r\n\r\n private async stopHealthCheckServer(): Promise<void> {\r\n if (!this.healthCheckServer) {\r\n return;\r\n }\r\n\r\n return new Promise((resolve) => {\r\n this.healthCheckServer!.close(() => {\r\n this.logger.info('Health check server stopped');\r\n resolve();\r\n });\r\n });\r\n }\r\n\r\n private async registerService(): Promise<void> {\r\n const primary = this.services[0]?.definition;\r\n if (!primary) {\r\n throw new Error('Unexpected missing service definition during registration.');\r\n }\r\n const options = getServiceOptions(primary.ctor) ?? {};\r\n // Match C# exactly: use qualifiedName (preserves case) for name field.\r\n // The gateway lowercases service names when creating bindings, but uses the original case\r\n // from the registration request when constructing routing key lookups.\r\n // This ensures the routing key format matches what the gateway expects.\r\n // The gateway's binding lookup behavior:\r\n // - Gateway error shows: \"No binding found for service ...micro_examplenodeservice.basic\"\r\n // - This means the gateway lowercases the service name for binding storage/lookup\r\n // - Portal sends requests with mixed case in URL and routing key header\r\n // - The gateway lowercases the URL path for binding lookup, which should work\r\n // - But we need to register with the format the gateway expects for the binding key\r\n // - The gateway constructs binding key as: {cid}.{pid}.{lowercaseServiceName}.{type}\r\n // - So we register with lowercase to match what the gateway stores in the binding\r\n // - The portal will still send mixed case, and the gateway will lowercase it for lookup\r\n // Register with mixed-case qualifiedName to match C# behavior\r\n // The gateway will lowercase the service name when creating the binding key,\r\n // but the registration request should use the original case (as C# does)\r\n // This ensures the service name in the registration matches what the portal expects\r\n // Register with mixed-case qualifiedName to match C# behavior\r\n // The gateway's ServiceIdentity.fullNameNoType lowercases the service name when creating bindings,\r\n // but the registration request should use the original case (as C# does)\r\n // This ensures the service name in the registration matches what the portal expects\r\n const isDeployed = isRunningInContainer();\r\n \r\n // For deployed services, routingKey should be null/undefined (None in Scala)\r\n // For local dev, routingKey should be the actual routing key string\r\n // The backend expects Option[String] = None for deployed services\r\n // Note: microServiceId is not part of SocketSessionProviderRegisterRequest, so we don't include it\r\n // All fields except 'type' are Option[T] in Scala, so undefined fields will be omitted from JSON\r\n // This matches C# behavior where null/None fields are not serialized\r\n const request: Record<string, unknown> = {\r\n type: 'basic',\r\n name: primary.qualifiedName, // Use mixed-case as C# does - gateway handles lowercasing for binding storage\r\n beamoName: primary.name,\r\n };\r\n \r\n // Only include routingKey and startedById if they have values (for local dev)\r\n // For deployed services, these should be omitted (undefined) to match None in Scala\r\n if (!isDeployed && this.env.routingKey) {\r\n request.routingKey = this.env.routingKey;\r\n }\r\n if (!isDeployed && this.env.accountId) {\r\n request.startedById = this.env.accountId;\r\n }\r\n\r\n // Log registration request to match C# behavior exactly\r\n // Also log the actual JSON that will be sent (undefined fields will be omitted)\r\n const serializedRequest = JSON.stringify(request);\r\n this.logger.debug(\r\n {\r\n request: {\r\n type: request.type,\r\n name: request.name,\r\n beamoName: request.beamoName,\r\n routingKey: request.routingKey,\r\n startedById: request.startedById,\r\n },\r\n serializedJson: serializedRequest,\r\n isDeployed,\r\n },\r\n 'Registering service provider with gateway.',\r\n );\r\n\r\n try {\r\n await this.requester.request('post', 'gateway/provider', request);\r\n this.logger.info({ serviceName: primary.qualifiedName }, 'Service provider registered successfully.');\r\n \r\n // After registration, the gateway's BasicServiceProvider.start() is called asynchronously\r\n // This triggers afterRabbitInit() -> setupDirectServiceCommunication() -> scheduleServiceBindingCheck()\r\n // The first updateBindings() call happens immediately (0.millisecond delay)\r\n // We wait a bit to allow the gateway to set up the HTTP binding and call updateBindings()\r\n // This is especially important for deployed services where the gateway needs to establish\r\n // the external host binding before bindings can be created\r\n if (isDeployed) {\r\n this.logger.debug('Waiting for gateway to establish bindings after registration...');\r\n await new Promise((resolve) => setTimeout(resolve, 2000)); // 2 second wait for deployed services\r\n this.logger.debug('Wait complete, gateway should have established bindings by now.');\r\n }\r\n } catch (error) {\r\n this.logger.error(\r\n {\r\n err: error,\r\n request,\r\n serviceName: primary.qualifiedName,\r\n errorMessage: error instanceof Error ? error.message : String(error),\r\n },\r\n 'Failed to register service provider with gateway. This will prevent the service from receiving requests.'\r\n );\r\n throw error; // Re-throw so startup fails and we can see the error\r\n }\r\n\r\n if (options.disableAllBeamableEvents) {\r\n this.logger.info('Beamable events disabled by configuration.');\r\n } else {\r\n const eventRequest = {\r\n type: 'event',\r\n evtWhitelist: ['content.manifest', 'realm.config', 'logging.context'],\r\n };\r\n try {\r\n await this.requester.request('post', 'gateway/provider', eventRequest);\r\n } catch (error) {\r\n this.logger.warn({ err: error }, 'Failed to register event provider. Continuing without events.');\r\n }\r\n }\r\n }\r\n\r\n private async handleEvent(envelope: WebsocketEventEnvelope): Promise<void> {\r\n try {\r\n if (!envelope.path) {\r\n this.logger.debug({ envelope }, 'Ignoring websocket event without path.');\r\n return;\r\n }\r\n\r\n if (envelope.path.startsWith('event/')) {\r\n await this.requester.acknowledge(envelope.id);\r\n return;\r\n }\r\n\r\n const context = this.toRequestContext(envelope);\r\n await this.dispatch(context);\r\n } catch (error) {\r\n const err = error instanceof Error ? error : new Error(String(error));\r\n this.logger.error({ err, envelope }, 'Failed to handle websocket event.');\r\n const status = this.resolveErrorStatus(err);\r\n const response: GatewayResponse = {\r\n id: envelope.id,\r\n status,\r\n body: {\r\n error: err.name,\r\n message: err.message,\r\n },\r\n };\r\n await this.requester.sendResponse(response);\r\n }\r\n }\r\n\r\n private toRequestContext(envelope: WebsocketEventEnvelope): RequestContext {\r\n const path = envelope.path ?? '';\r\n const method = envelope.method ?? 'post';\r\n const userId = typeof envelope.from === 'number' ? envelope.from : 0;\r\n const headers = envelope.headers ?? {};\r\n \r\n // Extract scopes from envelope.scopes array\r\n // Note: X-DE-SCOPE header contains CID.PID, not scope values\r\n // The gateway sends scopes in envelope.scopes array\r\n // For admin endpoints, the gateway may not send scopes in the envelope,\r\n // so we infer admin scope from the path if it's an admin route\r\n const envelopeScopes = envelope.scopes ?? [];\r\n let scopes = normalizeScopes(envelopeScopes);\r\n \r\n // If this is an admin endpoint and no scopes are provided, infer admin scope\r\n // The gateway may not always send scopes for admin routes\r\n const pathLower = path.toLowerCase();\r\n if (pathLower.includes('/admin/') && scopes.size === 0) {\r\n scopes = normalizeScopes(['admin']);\r\n }\r\n\r\n let body: Record<string, unknown> | undefined;\r\n if (envelope.body && typeof envelope.body === 'string') {\r\n try {\r\n body = JSON.parse(envelope.body) as Record<string, unknown>;\r\n } catch (error) {\r\n this.logger.warn({ err: error, raw: envelope.body }, 'Failed to parse body string.');\r\n }\r\n } else if (envelope.body && typeof envelope.body === 'object') {\r\n body = envelope.body as Record<string, unknown>;\r\n }\r\n\r\n let payload: unknown;\r\n if (body && typeof body === 'object' && 'payload' in body) {\r\n const rawPayload = (body as Record<string, unknown>).payload;\r\n if (typeof rawPayload === 'string') {\r\n try {\r\n payload = JSON.parse(rawPayload) as unknown[];\r\n } catch (error) {\r\n this.logger.warn({ err: error }, 'Failed to parse payload JSON.');\r\n payload = rawPayload;\r\n }\r\n } else {\r\n payload = rawPayload;\r\n }\r\n }\r\n\r\n const targetService = this.findServiceForPath(path);\r\n const services = this.serviceManager.createFacade(userId, scopes, targetService?.definition.name);\r\n const provider = this.createRequestScope(path, targetService);\r\n\r\n const context: RequestContext = {\r\n id: envelope.id,\r\n path,\r\n method,\r\n status: envelope.status ?? 0,\r\n userId,\r\n payload,\r\n body,\r\n scopes,\r\n headers,\r\n cid: this.env.cid,\r\n pid: this.env.pid,\r\n services,\r\n throwIfCancelled: () => {},\r\n isCancelled: () => false,\r\n hasScopes: (...requiredScopes: string[]) => requiredScopes.every((scope) => scopeSetHas(scopes, scope)),\r\n requireScopes: (...requiredScopes: string[]) => {\r\n const missingScopes = requiredScopes.filter((scope) => !scopeSetHas(scopes, scope));\r\n if (missingScopes.length > 0) {\r\n throw new MissingScopesError(missingScopes);\r\n }\r\n },\r\n provider,\r\n };\r\n\r\n provider.setInstance(REQUEST_CONTEXT_TOKEN, context);\r\n provider.setInstance(BEAMABLE_SERVICES_TOKEN, services);\r\n\r\n return context;\r\n }\r\n\r\n private findServiceForPath(path: string): ServiceInstance | undefined {\r\n // Gateway sends paths with lowercase service names, so we need case-insensitive matching\r\n // Match by comparing lowercase versions to handle gateway's lowercase path format\r\n const pathLower = path.toLowerCase();\r\n return this.services.find((service) => {\r\n const qualifiedNameLower = service.definition.qualifiedName.toLowerCase();\r\n return pathLower.startsWith(`${qualifiedNameLower}/`);\r\n });\r\n }\r\n\r\n private async dispatch(ctx: RequestContext): Promise<void> {\r\n const service = this.findServiceForPath(ctx.path);\r\n if (!service) {\r\n throw new UnknownRouteError(ctx.path);\r\n }\r\n\r\n if (await this.tryHandleFederationRoute(ctx, service)) {\r\n return;\r\n }\r\n\r\n if (await this.tryHandleAdminRoute(ctx, service)) {\r\n return;\r\n }\r\n\r\n // Extract route from path - handle case-insensitive path matching\r\n const pathLower = ctx.path.toLowerCase();\r\n const qualifiedNameLower = service.definition.qualifiedName.toLowerCase();\r\n const route = pathLower.substring(qualifiedNameLower.length + 1);\r\n const metadata = service.definition.callables.get(route);\r\n if (!metadata) {\r\n throw new UnknownRouteError(ctx.path);\r\n }\r\n\r\n // For server and admin access, allow userId: 0 if the appropriate scope is present\r\n // For client access, always require userId > 0\r\n if (metadata.requireAuth && ctx.userId <= 0) {\r\n if (metadata.access === 'server' && scopeSetHas(ctx.scopes, 'server')) {\r\n // Server callables with server scope don't need a user ID\r\n } else if (metadata.access === 'admin' && scopeSetHas(ctx.scopes, 'admin')) {\r\n // Admin callables with admin scope don't need a user ID\r\n } else {\r\n throw new UnauthorizedUserError(route);\r\n }\r\n }\r\n\r\n enforceAccess(metadata.access, ctx);\r\n\r\n if (metadata.requiredScopes.length > 0) {\r\n const missing = metadata.requiredScopes.filter((scope) => !scopeSetHas(ctx.scopes, scope));\r\n if (missing.length > 0) {\r\n throw new MissingScopesError(missing);\r\n }\r\n }\r\n\r\n const handler = service.instance[metadata.displayName];\r\n if (typeof handler !== 'function') {\r\n throw new Error(`Callable ${metadata.displayName} is not a function on service ${service.definition.name}.`);\r\n }\r\n\r\n const args = this.buildInvocationArguments(handler as (...args: unknown[]) => unknown, ctx);\r\n const result = await Promise.resolve((handler as (...args: unknown[]) => unknown).apply(service.instance, args));\r\n await this.sendSuccessResponse(ctx, metadata, result);\r\n }\r\n\r\n private buildInvocationArguments(\r\n handler: (...args: unknown[]) => unknown,\r\n ctx: RequestContext,\r\n ): unknown[] {\r\n const payload = Array.isArray(ctx.payload)\r\n ? ctx.payload\r\n : typeof ctx.payload === 'string'\r\n ? [ctx.payload]\r\n : ctx.payload === undefined\r\n ? []\r\n : [ctx.payload];\r\n\r\n const expectedParams = handler.length;\r\n if (expectedParams === payload.length + 1) {\r\n return [ctx, ...payload];\r\n }\r\n return payload;\r\n }\r\n\r\n private async sendSuccessResponse(ctx: RequestContext, metadata: ServiceCallableMetadata, result: unknown): Promise<void> {\r\n let body: unknown;\r\n if (metadata.useLegacySerialization) {\r\n const serialized = typeof result === 'string' ? result : JSON.stringify(result ?? null);\r\n body = { payload: serialized };\r\n } else {\r\n body = result ?? null;\r\n }\r\n\r\n const response: GatewayResponse = {\r\n id: ctx.id,\r\n status: 200,\r\n body,\r\n };\r\n await this.requester.sendResponse(response);\r\n }\r\n\r\n private async sendFederationResponse(ctx: RequestContext, result: unknown): Promise<void> {\r\n const response: GatewayResponse = {\r\n id: ctx.id,\r\n status: 200,\r\n body: result ?? null,\r\n };\r\n await this.requester.sendResponse(response);\r\n }\r\n\r\n private async tryHandleFederationRoute(ctx: RequestContext, service: ServiceInstance): Promise<boolean> {\r\n // Gateway sends paths with lowercase service names, so we need case-insensitive matching\r\n const pathLower = ctx.path.toLowerCase();\r\n const qualifiedNameLower = service.definition.qualifiedName.toLowerCase();\r\n const prefixLower = `${qualifiedNameLower}/`;\r\n if (!pathLower.startsWith(prefixLower)) {\r\n return false;\r\n }\r\n // Extract relative path - use lowercase length since gateway sends lowercase paths\r\n const relativePath = ctx.path.substring(qualifiedNameLower.length + 1);\r\n const handler = service.federationRegistry.resolve(relativePath);\r\n if (!handler) {\r\n return false;\r\n }\r\n\r\n const result = await handler.invoke(ctx as FederatedRequestContext);\r\n await this.sendFederationResponse(ctx, result);\r\n return true;\r\n }\r\n\r\n private async tryHandleAdminRoute(ctx: RequestContext, service: ServiceInstance): Promise<boolean> {\r\n // Gateway sends paths with lowercase service names, so we need case-insensitive matching\r\n // Check if path starts with the admin prefix (case-insensitive)\r\n const pathLower = ctx.path.toLowerCase();\r\n const adminPrefixLower = `${service.definition.qualifiedName.toLowerCase()}/admin/`;\r\n if (!pathLower.startsWith(adminPrefixLower)) {\r\n return false;\r\n }\r\n\r\n const options = getServiceOptions(service.definition.ctor) ?? {};\r\n\r\n const action = pathLower.substring(adminPrefixLower.length);\r\n const requiresAdmin = action === 'metadata' || action === 'docs';\r\n if (requiresAdmin && !scopeSetHas(ctx.scopes, 'admin')) {\r\n // For portal requests to admin endpoints, the gateway may not send scopes\r\n // The X-DE-SCOPE header contains CID.PID, not scope values\r\n // If this is a portal request (has X-DE-SCOPE header), grant admin scope\r\n const hasPortalHeader = ctx.headers['X-DE-SCOPE'] || ctx.headers['x-de-scope'];\r\n if (hasPortalHeader) {\r\n // Grant admin scope for portal requests to admin endpoints\r\n ctx.scopes.add('admin');\r\n } else {\r\n throw new MissingScopesError(['admin']);\r\n }\r\n }\r\n\r\n switch (action) {\r\n case 'health':\r\n case 'healthcheck':\r\n await this.requester.sendResponse({ id: ctx.id, status: 200, body: 'responsive' });\r\n return true;\r\n case 'metadata':\r\n case 'docs':\r\n break;\r\n default:\r\n if (!scopeSetHas(ctx.scopes, 'admin')) {\r\n throw new MissingScopesError(['admin']);\r\n }\r\n throw new UnknownRouteError(ctx.path);\r\n }\r\n\r\n if (action === 'metadata') {\r\n const federatedComponents = service.federationRegistry\r\n .list()\r\n .map((component) => ({\r\n federationNamespace: component.federationNamespace,\r\n federationType: component.federationType,\r\n }));\r\n\r\n await this.requester.sendResponse({\r\n id: ctx.id,\r\n status: 200,\r\n body: {\r\n serviceName: service.definition.name,\r\n sdkVersion: this.env.sdkVersionExecution,\r\n sdkBaseBuildVersion: this.env.sdkVersionExecution,\r\n sdkExecutionVersion: this.env.sdkVersionExecution,\r\n useLegacySerialization: Boolean(options.useLegacySerialization),\r\n disableAllBeamableEvents: Boolean(options.disableAllBeamableEvents),\r\n enableEagerContentLoading: false,\r\n instanceId: this.microServiceId,\r\n routingKey: this.env.routingKey ?? '',\r\n federatedComponents,\r\n },\r\n });\r\n return true;\r\n }\r\n\r\n if (action === 'docs') {\r\n try {\r\n const document = generateOpenApiDocument(\r\n {\r\n qualifiedName: service.definition.qualifiedName,\r\n name: service.definition.name,\r\n callables: Array.from(service.definition.callables.values()).map((callable) => ({\r\n name: callable.displayName,\r\n route: callable.route,\r\n metadata: callable,\r\n handler: typeof service.instance[callable.displayName] === 'function'\r\n ? (service.instance[callable.displayName] as (...args: unknown[]) => unknown)\r\n : undefined,\r\n })),\r\n },\r\n this.env,\r\n );\r\n\r\n // Ensure document is valid (not empty)\r\n if (!document || Object.keys(document).length === 0) {\r\n this.logger.warn({ serviceName: service.definition.name }, 'Generated OpenAPI document is empty');\r\n }\r\n\r\n await this.requester.sendResponse({\r\n id: ctx.id,\r\n status: 200,\r\n body: document,\r\n });\r\n return true;\r\n } catch (error) {\r\n this.logger.error({ err: error, serviceName: service.definition.name }, 'Failed to generate or send docs');\r\n throw error;\r\n }\r\n }\r\n\r\n return false;\r\n }\r\n\r\n private resolveErrorStatus(error: Error): number {\r\n if (error instanceof UnauthorizedUserError) {\r\n return 401;\r\n }\r\n if (error instanceof MissingScopesError) {\r\n return 403;\r\n }\r\n if (error instanceof UnknownRouteError) {\r\n return 404;\r\n }\r\n if (error instanceof BeamableRuntimeError) {\r\n return 500;\r\n }\r\n return 500;\r\n }\r\n\r\n private printHelpfulUrls(service?: ServiceInstance): void {\r\n if (!service) {\r\n return;\r\n }\r\n\r\n // Only print helpful URLs in local development (not in containers)\r\n // In containers, we want only JSON logs for proper log collection\r\n if (isRunningInContainer()) {\r\n return;\r\n }\r\n\r\n const docsUrl = buildDocsPortalUrl(this.env, service.definition);\r\n const endpointBase = buildPostmanBaseUrl(this.env, service.definition);\r\n\r\n const green = (text: string) => `\\x1b[32m${text}\\x1b[0m`;\r\n const yellow = (text: string) => `\\x1b[33m${text}\\x1b[0m`;\r\n\r\n const bannerLines = [\r\n '',\r\n yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'),\r\n ` ${green('Beamable Node microservice ready:')} ${green(service.definition.name)}`,\r\n yellow(' Quick shortcuts'),\r\n ` ${yellow('Docs:')} ${docsUrl}`,\r\n ` ${yellow('Endpoint:')} ${endpointBase}`,\r\n yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'),\r\n '',\r\n ];\r\n\r\n process.stdout.write(`${bannerLines.join('\\n')}`);\r\n }\r\n\r\n private async initializeDependencyProviders(): Promise<void> {\r\n for (const service of this.services) {\r\n const builder = new DependencyBuilder();\r\n builder.tryAddSingletonInstance(LOGGER_TOKEN, service.logger);\r\n builder.tryAddSingletonInstance(ENVIRONMENT_CONFIG_TOKEN, this.env);\r\n builder.tryAddSingletonInstance(BeamableServiceManager, this.serviceManager);\r\n builder.tryAddSingletonInstance(service.definition.ctor, service.instance);\r\n builder.tryAddSingletonInstance(FederationRegistry, service.federationRegistry);\r\n\r\n for (const handler of service.configureHandlers) {\r\n await handler(builder);\r\n }\r\n\r\n const provider = builder.build();\r\n service.provider = provider;\r\n\r\n for (const handler of service.initializeHandlers) {\r\n await handler(provider);\r\n }\r\n\r\n Object.defineProperty(service.instance, 'provider', {\r\n value: provider,\r\n enumerable: false,\r\n configurable: false,\r\n writable: false,\r\n });\r\n }\r\n }\r\n\r\n private createRequestScope(path: string, service?: ServiceInstance): MutableDependencyScope {\r\n const targetService = service ?? this.findServiceForPath(path);\r\n const provider = targetService?.provider ?? new DependencyBuilder().build();\r\n return provider.createScope();\r\n }\r\n}\r\n\r\nfunction enforceAccess(access: ServiceAccess, ctx: RequestContext): void {\r\n switch (access) {\r\n case 'client':\r\n if (ctx.userId <= 0) {\r\n throw new UnauthorizedUserError(ctx.path);\r\n }\r\n break;\r\n case 'server':\r\n if (!scopeSetHas(ctx.scopes, 'server')) {\r\n throw new MissingScopesError(['server']);\r\n }\r\n break;\r\n case 'admin':\r\n if (!scopeSetHas(ctx.scopes, 'admin')) {\r\n throw new MissingScopesError(['admin']);\r\n }\r\n break;\r\n default:\r\n break;\r\n }\r\n}\r\n\r\n/**\r\n * Conditionally output to console.error only when running locally (not in container).\r\n * In containers, we want only Pino JSON logs to stdout for proper log collection.\r\n */\r\nfunction debugLog(...args: unknown[]): void {\r\n if (!isRunningInContainer()) {\r\n console.error(...args);\r\n }\r\n}\r\n\r\nfunction isRunningInContainer(): boolean {\r\n // Beamable sets CID, PID, HOST, SECRET in deployed containers\r\n // If we have these required env vars but NO routing key, we're in a deployed container\r\n // This is more reliable than checking for DOTNET_RUNNING_IN_CONTAINER (which is C#-specific)\r\n const hasBeamableEnvVars = !!(process.env.CID && process.env.PID && process.env.HOST && process.env.SECRET);\r\n const hasNoRoutingKey = !process.env.NAME_PREFIX && !process.env.ROUTING_KEY;\r\n \r\n if (hasBeamableEnvVars && hasNoRoutingKey) {\r\n return true; // Deployed container\r\n }\r\n \r\n // Fallback checks for other container indicators\r\n return (\r\n process.env.DOTNET_RUNNING_IN_CONTAINER === 'true' ||\r\n process.env.CONTAINER === 'beamable' ||\r\n !!process.env.ECS_CONTAINER_METADATA_URI ||\r\n !!process.env.KUBERNETES_SERVICE_HOST\r\n );\r\n}\r\n\r\nfunction normalizeScopes(scopes: Array<unknown>): Set<string> {\r\n const normalized = new Set<string>();\r\n for (const scope of scopes) {\r\n if (typeof scope !== 'string' || scope.trim().length === 0) {\r\n continue;\r\n }\r\n normalized.add(scope.trim().toLowerCase());\r\n }\r\n return normalized;\r\n}\r\n\r\nfunction scopeSetHas(scopes: Set<string>, scope: string): boolean {\r\n const normalized = scope.trim().toLowerCase();\r\n if (normalized.length === 0) {\r\n return false;\r\n }\r\n return scopes.has('*') || scopes.has(normalized);\r\n}\r\n\r\nfunction buildPostmanBaseUrl(env: EnvironmentConfig, service: ServiceDefinition): string {\r\n const httpHost = hostToHttpUrl(env.host);\r\n const routingKeyPart = env.routingKey ? env.routingKey : '';\r\n return `${httpHost}/basic/${env.cid}.${env.pid}.${routingKeyPart}${service.qualifiedName}/`;\r\n}\r\n\r\nfunction buildDocsPortalUrl(env: EnvironmentConfig, service: ServiceDefinition): string {\r\n const portalHost = hostToPortalUrl(hostToHttpUrl(env.host));\r\n const queryParams: Record<string, string> = { srcTool: 'node-runtime' };\r\n if (env.routingKey) {\r\n queryParams.routingKey = env.routingKey;\r\n }\r\n const query = new URLSearchParams(queryParams);\r\n if (env.refreshToken) {\r\n query.set('refresh_token', env.refreshToken);\r\n }\r\n const beamoId = service.name;\r\n return `${portalHost}/${env.cid}/games/${env.pid}/realms/${env.pid}/microservices/micro_${beamoId}/docs?${query.toString()}`;\r\n}\r\n\r\nexport async function runMicroservice(): Promise<void> {\r\n // Immediate console output to verify process is starting (local dev only)\r\n // In containers, we rely on Pino logger for all output\r\n debugLog('[BEAMABLE-NODE] Starting microservice...');\r\n debugLog(`[BEAMABLE-NODE] Node version: ${process.version}`);\r\n debugLog(`[BEAMABLE-NODE] Working directory: ${process.cwd()}`);\r\n debugLog(`[BEAMABLE-NODE] Environment: ${JSON.stringify({\r\n NODE_ENV: process.env.NODE_ENV,\r\n CID: process.env.CID ? 'SET' : 'NOT SET',\r\n PID: process.env.PID ? 'SET' : 'NOT SET',\r\n HOST: process.env.HOST ? 'SET' : 'NOT SET',\r\n SECRET: process.env.SECRET ? 'SET' : 'NOT SET',\r\n })}`);\r\n if (process.env.BEAMABLE_SKIP_RUNTIME === 'true') {\r\n return;\r\n }\r\n const runtime = new MicroserviceRuntime();\r\n \r\n // Handle uncaught errors - log them but don't crash immediately\r\n // This allows the health check server to keep running so we can debug\r\n // In containers, errors will be logged by the logger in the runtime\r\n // Locally, use console.error for visibility\r\n process.on('uncaughtException', (error) => {\r\n // In containers, the logger will handle this; locally, show in console\r\n debugLog('Uncaught Exception:', error);\r\n // Don't exit - let the health check server keep running\r\n });\r\n \r\n process.on('unhandledRejection', (reason, promise) => {\r\n // In containers, the logger will handle this; locally, show in console\r\n debugLog('Unhandled Rejection at:', promise, 'reason:', reason);\r\n // Don't exit - let the health check server keep running\r\n });\r\n \r\n try {\r\n await runtime.start();\r\n } catch (error) {\r\n // Log the error but don't exit - health check server is running\r\n // This allows the container to stay alive so we can see what went wrong\r\n // In containers, the logger already logged it in start(); locally, also use console.error\r\n debugLog('Failed to start microservice runtime:', error);\r\n // Keep the process alive so health check can continue\r\n // The health check will return 503 until isReady is true\r\n }\r\n \r\n const shutdownSignals: NodeJS.Signals[] = ['SIGTERM', 'SIGINT'];\r\n shutdownSignals.forEach((signal) => {\r\n process.once(signal, async () => {\r\n await runtime.shutdown();\r\n process.exit(0);\r\n });\r\n });\r\n}\r\n\r\ninterface GenerateOpenApiOptions {\r\n serviceName?: string;\r\n}\r\n\r\nexport function generateOpenApiDocumentForRegisteredServices(\r\n overrides: Partial<EnvironmentConfig> = {},\r\n options: GenerateOpenApiOptions = {},\r\n): unknown {\r\n const services = listRegisteredServices();\r\n if (services.length === 0) {\r\n throw new Error('No microservices registered. Import your service module before generating documentation.');\r\n }\r\n\r\n const baseEnv = buildEnvironmentConfig(overrides);\r\n const primary = options.serviceName\r\n ? services.find((svc) => svc.name === options.serviceName || svc.qualifiedName === options.serviceName)\r\n : services[0];\r\n\r\n if (!primary) {\r\n throw new Error(`No registered microservice matched '${options.serviceName}'.`);\r\n }\r\n\r\n return generateOpenApiDocument(\r\n {\r\n qualifiedName: primary.qualifiedName,\r\n name: primary.name,\r\n callables: Array.from(primary.callables.entries()).map(([displayName, metadata]) => ({\r\n name: displayName,\r\n route: metadata.route,\r\n metadata,\r\n handler: undefined,\r\n })),\r\n },\r\n baseEnv,\r\n );\r\n}\r\n\r\nfunction buildEnvironmentConfig(overrides: Partial<EnvironmentConfig>): EnvironmentConfig {\r\n const merged: Partial<EnvironmentConfig> = { ...overrides };\r\n\r\n const ensure = (key: keyof EnvironmentConfig, fallbackEnv?: string) => {\r\n if (merged[key] !== undefined) {\r\n return;\r\n }\r\n if (fallbackEnv && process.env[fallbackEnv]) {\r\n merged[key] = process.env[fallbackEnv] as never;\r\n }\r\n };\r\n\r\n ensure('cid', 'CID');\r\n ensure('pid', 'PID');\r\n ensure('host', 'HOST');\r\n ensure('secret', 'SECRET');\r\n ensure('refreshToken', 'REFRESH_TOKEN');\r\n // Routing key is optional for deployed services (in containers)\r\n // It will be resolved to empty string if not provided and running in container\r\n if (!merged.routingKey && !isRunningInContainer()) {\r\n ensure('routingKey', 'NAME_PREFIX');\r\n }\r\n\r\n if (!merged.cid || !merged.pid || !merged.host) {\r\n throw new Error('CID, PID, and HOST are required to generate documentation.');\r\n }\r\n\r\n const base = loadEnvironmentConfig();\r\n\r\n return {\r\n ...base,\r\n ...merged,\r\n routingKey: merged.routingKey ?? base.routingKey,\r\n };\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from 'node:crypto';\r\nimport { BeamableWebSocket } from './websocket.js';\r\nimport { GatewayRequester } from './requester.js';\r\nimport { AuthManager } from './auth.js';\r\nimport { createLogger } from './logger.js';\r\nimport { loadEnvironmentConfig } from './env.js';\r\nimport { listRegisteredServices, getServiceOptions, getConfigureServicesHandlers, getInitializeServicesHandlers } from './decorators.js';\r\nimport { generateOpenApiDocument } from './docs.js';\r\nimport { DiscoveryBroadcaster } from './discovery.js';\r\nimport { BeamableRuntimeError, MissingScopesError, UnauthorizedUserError, UnknownRouteError } from './errors.js';\r\nimport type {\r\n EnvironmentConfig,\r\n RequestContext,\r\n ServiceDefinition,\r\n ServiceCallableMetadata,\r\n GatewayResponse,\r\n WebsocketEventEnvelope,\r\n ServiceAccess,\r\n} from './types.js';\r\nimport type { Logger } from 'pino';\r\nimport { BeamableServiceManager } from './services.js';\r\nimport {\r\n DependencyBuilder,\r\n LOGGER_TOKEN,\r\n ENVIRONMENT_CONFIG_TOKEN,\r\n REQUEST_CONTEXT_TOKEN,\r\n BEAMABLE_SERVICES_TOKEN,\r\n ServiceProvider,\r\n MutableDependencyScope,\r\n} from './dependency.js';\r\nimport { hostToHttpUrl, hostToPortalUrl } from './utils/urls.js';\r\nimport { FederationRegistry, getFederationComponents, getFederatedInventoryMetadata } from './federation.js';\r\nimport type { FederatedRequestContext } from './federation.js';\r\nimport { createServer, type Server } from 'node:http';\r\n\r\ninterface ServiceInstance {\r\n definition: ServiceDefinition;\r\n instance: Record<string, unknown>;\r\n configureHandlers: ReturnType<typeof getConfigureServicesHandlers>;\r\n initializeHandlers: ReturnType<typeof getInitializeServicesHandlers>;\r\n provider?: ServiceProvider;\r\n logger: Logger;\r\n federationRegistry: FederationRegistry;\r\n}\r\n\r\nexport class MicroserviceRuntime {\r\n private readonly env: EnvironmentConfig;\r\n private readonly logger: Logger;\r\n private readonly services: ServiceInstance[];\r\n private readonly webSocket: BeamableWebSocket;\r\n private readonly requester: GatewayRequester;\r\n private readonly authManager: AuthManager;\r\n private readonly discovery?: DiscoveryBroadcaster;\r\n private readonly microServiceId = randomUUID();\r\n private readonly serviceManager: BeamableServiceManager;\r\n private healthCheckServer?: Server;\r\n private isReady: boolean = false;\r\n\r\n constructor(env?: EnvironmentConfig) {\r\n this.env = env ?? loadEnvironmentConfig();\r\n this.logger = createLogger(this.env, { name: 'beamable-node-microservice' });\r\n this.serviceManager = new BeamableServiceManager(this.env, this.logger);\r\n\r\n const registered = listRegisteredServices();\r\n if (registered.length === 0) {\r\n throw new Error('No microservices registered. Use the @Microservice decorator to register at least one class.');\r\n }\r\n this.services = registered.map((definition) => {\r\n const instance = new definition.ctor() as Record<string, unknown>;\r\n const configureHandlers = getConfigureServicesHandlers(definition.ctor);\r\n const initializeHandlers = getInitializeServicesHandlers(definition.ctor);\r\n const logger = this.logger.child({ service: definition.name });\r\n const federationRegistry = new FederationRegistry(logger);\r\n const decoratedFederations = getFederationComponents(definition.ctor);\r\n for (const component of decoratedFederations) {\r\n federationRegistry.register(component);\r\n }\r\n const inventoryMetadata = getFederatedInventoryMetadata(definition.ctor);\r\n if (inventoryMetadata.length > 0) {\r\n federationRegistry.registerInventoryHandlers(instance, inventoryMetadata);\r\n }\r\n this.serviceManager.registerFederationRegistry(definition.name, federationRegistry);\r\n return { definition, instance, configureHandlers, initializeHandlers, logger, federationRegistry };\r\n });\r\n\r\n const socketUrl = this.env.host.endsWith('/socket') ? this.env.host : `${this.env.host}/socket`;\r\n this.webSocket = new BeamableWebSocket({ url: socketUrl, logger: this.logger });\r\n this.webSocket.on('message', (payload) => {\r\n this.logger.debug({ payload }, 'Runtime observed websocket frame.');\r\n });\r\n this.requester = new GatewayRequester(this.webSocket, this.logger);\r\n this.authManager = new AuthManager(this.env, this.requester);\r\n this.requester.on('event', (envelope) => this.handleEvent(envelope));\r\n\r\n if (!isRunningInContainer(this.env) && this.services.length > 0) {\r\n this.discovery = new DiscoveryBroadcaster({\r\n env: this.env,\r\n serviceName: this.services[0].definition.name,\r\n routingKey: this.env.routingKey,\r\n logger: this.logger.child({ component: 'DiscoveryBroadcaster' }),\r\n });\r\n }\r\n }\r\n\r\n async start(): Promise<void> {\r\n // Immediate console output for container logs (before logger is ready)\r\n debugLog('[BEAMABLE-NODE] MicroserviceRuntime.start() called');\r\n debugLog(`[BEAMABLE-NODE] Service count: ${this.services.length}`);\r\n \r\n this.printHelpfulUrls(this.services[0]);\r\n this.logger.info('Starting Beamable Node microservice runtime.');\r\n \r\n // Start health check server FIRST - this is critical for container health checks\r\n // Even if startup fails, the health check server must be running so we can debug\r\n debugLog('[BEAMABLE-NODE] Starting health check server...');\r\n await this.startHealthCheckServer();\r\n debugLog('[BEAMABLE-NODE] Health check server started');\r\n \r\n try {\r\n this.logger.info('Connecting to Beamable gateway...');\r\n await this.webSocket.connect();\r\n await new Promise((resolve) => setTimeout(resolve, 250));\r\n \r\n this.logger.info('Authenticating with Beamable...');\r\n await this.authManager.authenticate();\r\n \r\n this.logger.info('Initializing Beamable SDK services...');\r\n await this.serviceManager.initialize();\r\n \r\n this.logger.info('Initializing dependency providers...');\r\n await this.initializeDependencyProviders();\r\n \r\n this.logger.info('Registering service with gateway...');\r\n await this.registerService();\r\n \r\n this.logger.info('Starting discovery broadcaster...');\r\n await this.discovery?.start();\r\n \r\n // Mark as ready only after service is fully registered and discovery is started\r\n this.isReady = true;\r\n this.logger.info('Beamable microservice runtime is ready to accept traffic.');\r\n } catch (error) {\r\n // Log the error with full context but don't crash - health check server is running\r\n // This allows the container to stay alive so we can debug the issue\r\n // Debug output for local development only (in containers, logger handles this)\r\n debugLog('[BEAMABLE-NODE] FATAL ERROR during startup:');\r\n debugLog(`[BEAMABLE-NODE] Error message: ${error instanceof Error ? error.message : String(error)}`);\r\n debugLog(`[BEAMABLE-NODE] Error stack: ${error instanceof Error ? error.stack : 'No stack trace'}`);\r\n debugLog(`[BEAMABLE-NODE] isReady: ${this.isReady}`);\r\n \r\n this.logger.error(\r\n { \r\n err: error,\r\n errorMessage: error instanceof Error ? error.message : String(error),\r\n errorStack: error instanceof Error ? error.stack : undefined,\r\n isReady: this.isReady,\r\n },\r\n 'Failed to fully initialize microservice runtime. Health check will continue to return 503 until initialization completes.'\r\n );\r\n // DON'T re-throw - keep process alive so health check can show 503\r\n // This allows us to see the error in logs\r\n this.isReady = false;\r\n }\r\n }\r\n\r\n async shutdown(): Promise<void> {\r\n this.logger.info('Shutting down microservice runtime.');\r\n this.isReady = false; // Mark as not ready during shutdown\r\n this.discovery?.stop();\r\n await this.stopHealthCheckServer();\r\n this.requester.dispose();\r\n await this.webSocket.close();\r\n }\r\n\r\n private async startHealthCheckServer(): Promise<void> {\r\n // For deployed services, always start health check server (required for container health checks)\r\n // For local development, only start if healthPort is explicitly set\r\n // IMPORTANT: Always default to 6565 if HEALTH_PORT env var is not set, as this is the standard port\r\n const healthPort = this.env.healthPort || 6565;\r\n \r\n // Always start health check server if we have a valid port\r\n // The container orchestrator expects this endpoint to be available\r\n if (!healthPort || healthPort === 0) {\r\n // Health check server not needed (local development without explicit port)\r\n this.logger.debug('Health check server skipped (no healthPort set)');\r\n return;\r\n }\r\n\r\n this.healthCheckServer = createServer((req, res) => {\r\n if (req.url === '/health' && req.method === 'GET') {\r\n // Only return success if service is fully ready (registered and accepting traffic)\r\n if (this.isReady) {\r\n res.writeHead(200, { 'Content-Type': 'text/plain' });\r\n res.end('responsive');\r\n } else {\r\n // Service is still starting up\r\n res.writeHead(503, { 'Content-Type': 'text/plain' });\r\n res.end('Service Unavailable');\r\n }\r\n } else {\r\n res.writeHead(404, { 'Content-Type': 'text/plain' });\r\n res.end('Not Found');\r\n }\r\n });\r\n\r\n return new Promise((resolve, reject) => {\r\n this.healthCheckServer!.listen(healthPort, '0.0.0.0', () => {\r\n this.logger.info({ port: healthPort }, 'Health check server started on port');\r\n resolve();\r\n });\r\n this.healthCheckServer!.on('error', (err) => {\r\n this.logger.error({ err, port: healthPort }, 'Failed to start health check server');\r\n reject(err);\r\n });\r\n });\r\n }\r\n\r\n private async stopHealthCheckServer(): Promise<void> {\r\n if (!this.healthCheckServer) {\r\n return;\r\n }\r\n\r\n return new Promise((resolve) => {\r\n this.healthCheckServer!.close(() => {\r\n this.logger.info('Health check server stopped');\r\n resolve();\r\n });\r\n });\r\n }\r\n\r\n private async registerService(): Promise<void> {\r\n const primary = this.services[0]?.definition;\r\n if (!primary) {\r\n throw new Error('Unexpected missing service definition during registration.');\r\n }\r\n const options = getServiceOptions(primary.ctor) ?? {};\r\n // Match C# exactly: use qualifiedName (preserves case) for name field.\r\n // The gateway lowercases service names when creating bindings, but uses the original case\r\n // from the registration request when constructing routing key lookups.\r\n // This ensures the routing key format matches what the gateway expects.\r\n // The gateway's binding lookup behavior:\r\n // - Gateway error shows: \"No binding found for service ...micro_examplenodeservice.basic\"\r\n // - This means the gateway lowercases the service name for binding storage/lookup\r\n // - Portal sends requests with mixed case in URL and routing key header\r\n // - The gateway lowercases the URL path for binding lookup, which should work\r\n // - But we need to register with the format the gateway expects for the binding key\r\n // - The gateway constructs binding key as: {cid}.{pid}.{lowercaseServiceName}.{type}\r\n // - So we register with lowercase to match what the gateway stores in the binding\r\n // - The portal will still send mixed case, and the gateway will lowercase it for lookup\r\n // Register with mixed-case qualifiedName to match C# behavior\r\n // The gateway will lowercase the service name when creating the binding key,\r\n // but the registration request should use the original case (as C# does)\r\n // This ensures the service name in the registration matches what the portal expects\r\n // Register with mixed-case qualifiedName to match C# behavior\r\n // The gateway's ServiceIdentity.fullNameNoType lowercases the service name when creating bindings,\r\n // but the registration request should use the original case (as C# does)\r\n // This ensures the service name in the registration matches what the portal expects\r\n const isDeployed = isRunningInContainer(this.env);\r\n \r\n // For deployed services, routingKey should be null/undefined (None in Scala)\r\n // For local dev, routingKey should be the actual routing key string\r\n // The backend expects Option[String] = None for deployed services\r\n // Note: microServiceId is not part of SocketSessionProviderRegisterRequest, so we don't include it\r\n // All fields except 'type' are Option[T] in Scala, so undefined fields will be omitted from JSON\r\n // This matches C# behavior where null/None fields are not serialized\r\n const request: Record<string, unknown> = {\r\n type: 'basic',\r\n name: primary.qualifiedName, // Use mixed-case as C# does - gateway handles lowercasing for binding storage\r\n beamoName: primary.name,\r\n };\r\n \r\n // Only include routingKey and startedById if they have values (for local dev)\r\n // For deployed services, these should be omitted (undefined) to match None in Scala\r\n if (!isDeployed && this.env.routingKey) {\r\n request.routingKey = this.env.routingKey;\r\n }\r\n if (!isDeployed && this.env.accountId) {\r\n request.startedById = this.env.accountId;\r\n }\r\n\r\n // Log registration request to match C# behavior exactly\r\n // Also log the actual JSON that will be sent (undefined fields will be omitted)\r\n const serializedRequest = JSON.stringify(request);\r\n this.logger.debug(\r\n {\r\n request: {\r\n type: request.type,\r\n name: request.name,\r\n beamoName: request.beamoName,\r\n routingKey: request.routingKey,\r\n startedById: request.startedById,\r\n },\r\n serializedJson: serializedRequest,\r\n isDeployed,\r\n },\r\n 'Registering service provider with gateway.',\r\n );\r\n\r\n try {\r\n await this.requester.request('post', 'gateway/provider', request);\r\n this.logger.info({ serviceName: primary.qualifiedName }, 'Service provider registered successfully.');\r\n \r\n // After registration, the gateway's BasicServiceProvider.start() is called asynchronously\r\n // This triggers afterRabbitInit() -> setupDirectServiceCommunication() -> scheduleServiceBindingCheck()\r\n // The first updateBindings() call happens immediately (0.millisecond delay)\r\n // We wait a bit to allow the gateway to set up the HTTP binding and call updateBindings()\r\n // This is especially important for deployed services where the gateway needs to establish\r\n // the external host binding before bindings can be created\r\n if (isDeployed) {\r\n this.logger.debug('Waiting for gateway to establish bindings after registration...');\r\n await new Promise((resolve) => setTimeout(resolve, 2000)); // 2 second wait for deployed services\r\n this.logger.debug('Wait complete, gateway should have established bindings by now.');\r\n }\r\n } catch (error) {\r\n this.logger.error(\r\n {\r\n err: error,\r\n request,\r\n serviceName: primary.qualifiedName,\r\n errorMessage: error instanceof Error ? error.message : String(error),\r\n },\r\n 'Failed to register service provider with gateway. This will prevent the service from receiving requests.'\r\n );\r\n throw error; // Re-throw so startup fails and we can see the error\r\n }\r\n\r\n if (options.disableAllBeamableEvents) {\r\n this.logger.info('Beamable events disabled by configuration.');\r\n } else {\r\n const eventRequest = {\r\n type: 'event',\r\n evtWhitelist: ['content.manifest', 'realm.config', 'logging.context'],\r\n };\r\n try {\r\n await this.requester.request('post', 'gateway/provider', eventRequest);\r\n } catch (error) {\r\n this.logger.warn({ err: error }, 'Failed to register event provider. Continuing without events.');\r\n }\r\n }\r\n }\r\n\r\n private async handleEvent(envelope: WebsocketEventEnvelope): Promise<void> {\r\n try {\r\n if (!envelope.path) {\r\n this.logger.debug({ envelope }, 'Ignoring websocket event without path.');\r\n return;\r\n }\r\n\r\n if (envelope.path.startsWith('event/')) {\r\n await this.requester.acknowledge(envelope.id);\r\n return;\r\n }\r\n\r\n const context = this.toRequestContext(envelope);\r\n await this.dispatch(context);\r\n } catch (error) {\r\n const err = error instanceof Error ? error : new Error(String(error));\r\n this.logger.error({ err, envelope }, 'Failed to handle websocket event.');\r\n const status = this.resolveErrorStatus(err);\r\n const response: GatewayResponse = {\r\n id: envelope.id,\r\n status,\r\n body: {\r\n error: err.name,\r\n message: err.message,\r\n },\r\n };\r\n await this.requester.sendResponse(response);\r\n }\r\n }\r\n\r\n private toRequestContext(envelope: WebsocketEventEnvelope): RequestContext {\r\n const path = envelope.path ?? '';\r\n const method = envelope.method ?? 'post';\r\n const userId = typeof envelope.from === 'number' ? envelope.from : 0;\r\n const headers = envelope.headers ?? {};\r\n \r\n // Extract scopes from envelope.scopes array\r\n // Note: X-DE-SCOPE header contains CID.PID, not scope values\r\n // The gateway sends scopes in envelope.scopes array\r\n // For admin endpoints, the gateway may not send scopes in the envelope,\r\n // so we infer admin scope from the path if it's an admin route\r\n const envelopeScopes = envelope.scopes ?? [];\r\n let scopes = normalizeScopes(envelopeScopes);\r\n \r\n // If this is an admin endpoint and no scopes are provided, infer admin scope\r\n // The gateway may not always send scopes for admin routes\r\n const pathLower = path.toLowerCase();\r\n if (pathLower.includes('/admin/') && scopes.size === 0) {\r\n scopes = normalizeScopes(['admin']);\r\n }\r\n\r\n let body: Record<string, unknown> | undefined;\r\n if (envelope.body && typeof envelope.body === 'string') {\r\n try {\r\n body = JSON.parse(envelope.body) as Record<string, unknown>;\r\n } catch (error) {\r\n this.logger.warn({ err: error, raw: envelope.body }, 'Failed to parse body string.');\r\n }\r\n } else if (envelope.body && typeof envelope.body === 'object') {\r\n body = envelope.body as Record<string, unknown>;\r\n }\r\n\r\n let payload: unknown;\r\n if (body && typeof body === 'object' && 'payload' in body) {\r\n const rawPayload = (body as Record<string, unknown>).payload;\r\n if (typeof rawPayload === 'string') {\r\n try {\r\n payload = JSON.parse(rawPayload) as unknown[];\r\n } catch (error) {\r\n this.logger.warn({ err: error }, 'Failed to parse payload JSON.');\r\n payload = rawPayload;\r\n }\r\n } else {\r\n payload = rawPayload;\r\n }\r\n }\r\n\r\n const targetService = this.findServiceForPath(path);\r\n const services = this.serviceManager.createFacade(userId, scopes, targetService?.definition.name);\r\n const provider = this.createRequestScope(path, targetService);\r\n\r\n const context: RequestContext = {\r\n id: envelope.id,\r\n path,\r\n method,\r\n status: envelope.status ?? 0,\r\n userId,\r\n payload,\r\n body,\r\n scopes,\r\n headers,\r\n cid: this.env.cid,\r\n pid: this.env.pid,\r\n services,\r\n throwIfCancelled: () => {},\r\n isCancelled: () => false,\r\n hasScopes: (...requiredScopes: string[]) => requiredScopes.every((scope) => scopeSetHas(scopes, scope)),\r\n requireScopes: (...requiredScopes: string[]) => {\r\n const missingScopes = requiredScopes.filter((scope) => !scopeSetHas(scopes, scope));\r\n if (missingScopes.length > 0) {\r\n throw new MissingScopesError(missingScopes);\r\n }\r\n },\r\n provider,\r\n };\r\n\r\n provider.setInstance(REQUEST_CONTEXT_TOKEN, context);\r\n provider.setInstance(BEAMABLE_SERVICES_TOKEN, services);\r\n\r\n return context;\r\n }\r\n\r\n private findServiceForPath(path: string): ServiceInstance | undefined {\r\n // Gateway sends paths with lowercase service names, so we need case-insensitive matching\r\n // Match by comparing lowercase versions to handle gateway's lowercase path format\r\n const pathLower = path.toLowerCase();\r\n return this.services.find((service) => {\r\n const qualifiedNameLower = service.definition.qualifiedName.toLowerCase();\r\n return pathLower.startsWith(`${qualifiedNameLower}/`);\r\n });\r\n }\r\n\r\n private async dispatch(ctx: RequestContext): Promise<void> {\r\n const service = this.findServiceForPath(ctx.path);\r\n if (!service) {\r\n throw new UnknownRouteError(ctx.path);\r\n }\r\n\r\n if (await this.tryHandleFederationRoute(ctx, service)) {\r\n return;\r\n }\r\n\r\n if (await this.tryHandleAdminRoute(ctx, service)) {\r\n return;\r\n }\r\n\r\n // Extract route from path - handle case-insensitive path matching\r\n const pathLower = ctx.path.toLowerCase();\r\n const qualifiedNameLower = service.definition.qualifiedName.toLowerCase();\r\n const route = pathLower.substring(qualifiedNameLower.length + 1);\r\n const metadata = service.definition.callables.get(route);\r\n if (!metadata) {\r\n throw new UnknownRouteError(ctx.path);\r\n }\r\n\r\n // For server and admin access, allow userId: 0 if the appropriate scope is present\r\n // For client access, always require userId > 0\r\n if (metadata.requireAuth && ctx.userId <= 0) {\r\n if (metadata.access === 'server' && scopeSetHas(ctx.scopes, 'server')) {\r\n // Server callables with server scope don't need a user ID\r\n } else if (metadata.access === 'admin' && scopeSetHas(ctx.scopes, 'admin')) {\r\n // Admin callables with admin scope don't need a user ID\r\n } else {\r\n throw new UnauthorizedUserError(route);\r\n }\r\n }\r\n\r\n enforceAccess(metadata.access, ctx);\r\n\r\n if (metadata.requiredScopes.length > 0) {\r\n const missing = metadata.requiredScopes.filter((scope) => !scopeSetHas(ctx.scopes, scope));\r\n if (missing.length > 0) {\r\n throw new MissingScopesError(missing);\r\n }\r\n }\r\n\r\n const handler = service.instance[metadata.displayName];\r\n if (typeof handler !== 'function') {\r\n throw new Error(`Callable ${metadata.displayName} is not a function on service ${service.definition.name}.`);\r\n }\r\n\r\n const args = this.buildInvocationArguments(handler as (...args: unknown[]) => unknown, ctx);\r\n const result = await Promise.resolve((handler as (...args: unknown[]) => unknown).apply(service.instance, args));\r\n await this.sendSuccessResponse(ctx, metadata, result);\r\n }\r\n\r\n private buildInvocationArguments(\r\n handler: (...args: unknown[]) => unknown,\r\n ctx: RequestContext,\r\n ): unknown[] {\r\n const payload = Array.isArray(ctx.payload)\r\n ? ctx.payload\r\n : typeof ctx.payload === 'string'\r\n ? [ctx.payload]\r\n : ctx.payload === undefined\r\n ? []\r\n : [ctx.payload];\r\n\r\n const expectedParams = handler.length;\r\n if (expectedParams === payload.length + 1) {\r\n return [ctx, ...payload];\r\n }\r\n return payload;\r\n }\r\n\r\n private async sendSuccessResponse(ctx: RequestContext, metadata: ServiceCallableMetadata, result: unknown): Promise<void> {\r\n let body: unknown;\r\n if (metadata.useLegacySerialization) {\r\n const serialized = typeof result === 'string' ? result : JSON.stringify(result ?? null);\r\n body = { payload: serialized };\r\n } else {\r\n body = result ?? null;\r\n }\r\n\r\n const response: GatewayResponse = {\r\n id: ctx.id,\r\n status: 200,\r\n body,\r\n };\r\n await this.requester.sendResponse(response);\r\n }\r\n\r\n private async sendFederationResponse(ctx: RequestContext, result: unknown): Promise<void> {\r\n const response: GatewayResponse = {\r\n id: ctx.id,\r\n status: 200,\r\n body: result ?? null,\r\n };\r\n await this.requester.sendResponse(response);\r\n }\r\n\r\n private async tryHandleFederationRoute(ctx: RequestContext, service: ServiceInstance): Promise<boolean> {\r\n // Gateway sends paths with lowercase service names, so we need case-insensitive matching\r\n const pathLower = ctx.path.toLowerCase();\r\n const qualifiedNameLower = service.definition.qualifiedName.toLowerCase();\r\n const prefixLower = `${qualifiedNameLower}/`;\r\n if (!pathLower.startsWith(prefixLower)) {\r\n return false;\r\n }\r\n // Extract relative path - use lowercase length since gateway sends lowercase paths\r\n const relativePath = ctx.path.substring(qualifiedNameLower.length + 1);\r\n const handler = service.federationRegistry.resolve(relativePath);\r\n if (!handler) {\r\n return false;\r\n }\r\n\r\n const result = await handler.invoke(ctx as FederatedRequestContext);\r\n await this.sendFederationResponse(ctx, result);\r\n return true;\r\n }\r\n\r\n private async tryHandleAdminRoute(ctx: RequestContext, service: ServiceInstance): Promise<boolean> {\r\n // Gateway sends paths with lowercase service names, so we need case-insensitive matching\r\n // Check if path starts with the admin prefix (case-insensitive)\r\n const pathLower = ctx.path.toLowerCase();\r\n const adminPrefixLower = `${service.definition.qualifiedName.toLowerCase()}/admin/`;\r\n if (!pathLower.startsWith(adminPrefixLower)) {\r\n return false;\r\n }\r\n\r\n const options = getServiceOptions(service.definition.ctor) ?? {};\r\n\r\n const action = pathLower.substring(adminPrefixLower.length);\r\n const requiresAdmin = action === 'metadata' || action === 'docs';\r\n if (requiresAdmin && !scopeSetHas(ctx.scopes, 'admin')) {\r\n // For portal requests to admin endpoints, the gateway may not send scopes\r\n // The X-DE-SCOPE header contains CID.PID, not scope values\r\n // If this is a portal request (has X-DE-SCOPE header), grant admin scope\r\n const hasPortalHeader = ctx.headers['X-DE-SCOPE'] || ctx.headers['x-de-scope'];\r\n if (hasPortalHeader) {\r\n // Grant admin scope for portal requests to admin endpoints\r\n ctx.scopes.add('admin');\r\n } else {\r\n throw new MissingScopesError(['admin']);\r\n }\r\n }\r\n\r\n switch (action) {\r\n case 'health':\r\n case 'healthcheck':\r\n await this.requester.sendResponse({ id: ctx.id, status: 200, body: 'responsive' });\r\n return true;\r\n case 'metadata':\r\n case 'docs':\r\n break;\r\n default:\r\n if (!scopeSetHas(ctx.scopes, 'admin')) {\r\n throw new MissingScopesError(['admin']);\r\n }\r\n throw new UnknownRouteError(ctx.path);\r\n }\r\n\r\n if (action === 'metadata') {\r\n const federatedComponents = service.federationRegistry\r\n .list()\r\n .map((component) => ({\r\n federationNamespace: component.federationNamespace,\r\n federationType: component.federationType,\r\n }));\r\n\r\n await this.requester.sendResponse({\r\n id: ctx.id,\r\n status: 200,\r\n body: {\r\n serviceName: service.definition.name,\r\n sdkVersion: this.env.sdkVersionExecution,\r\n sdkBaseBuildVersion: this.env.sdkVersionExecution,\r\n sdkExecutionVersion: this.env.sdkVersionExecution,\r\n useLegacySerialization: Boolean(options.useLegacySerialization),\r\n disableAllBeamableEvents: Boolean(options.disableAllBeamableEvents),\r\n enableEagerContentLoading: false,\r\n instanceId: this.microServiceId,\r\n routingKey: this.env.routingKey ?? '',\r\n federatedComponents,\r\n },\r\n });\r\n return true;\r\n }\r\n\r\n if (action === 'docs') {\r\n try {\r\n const document = generateOpenApiDocument(\r\n {\r\n qualifiedName: service.definition.qualifiedName,\r\n name: service.definition.name,\r\n callables: Array.from(service.definition.callables.values()).map((callable) => ({\r\n name: callable.displayName,\r\n route: callable.route,\r\n metadata: callable,\r\n handler: typeof service.instance[callable.displayName] === 'function'\r\n ? (service.instance[callable.displayName] as (...args: unknown[]) => unknown)\r\n : undefined,\r\n })),\r\n },\r\n this.env,\r\n );\r\n\r\n // Ensure document is valid (not empty)\r\n if (!document || Object.keys(document).length === 0) {\r\n this.logger.warn({ serviceName: service.definition.name }, 'Generated OpenAPI document is empty');\r\n }\r\n\r\n await this.requester.sendResponse({\r\n id: ctx.id,\r\n status: 200,\r\n body: document,\r\n });\r\n return true;\r\n } catch (error) {\r\n this.logger.error({ err: error, serviceName: service.definition.name }, 'Failed to generate or send docs');\r\n throw error;\r\n }\r\n }\r\n\r\n return false;\r\n }\r\n\r\n private resolveErrorStatus(error: Error): number {\r\n if (error instanceof UnauthorizedUserError) {\r\n return 401;\r\n }\r\n if (error instanceof MissingScopesError) {\r\n return 403;\r\n }\r\n if (error instanceof UnknownRouteError) {\r\n return 404;\r\n }\r\n if (error instanceof BeamableRuntimeError) {\r\n return 500;\r\n }\r\n return 500;\r\n }\r\n\r\n private printHelpfulUrls(service?: ServiceInstance): void {\r\n if (!service) {\r\n return;\r\n }\r\n\r\n // Only print helpful URLs in local development (not in containers)\r\n // In containers, we want only JSON logs for proper log collection\r\n if (isRunningInContainer(this.env)) {\r\n return;\r\n }\r\n\r\n const docsUrl = buildDocsPortalUrl(this.env, service.definition);\r\n const endpointBase = buildPostmanBaseUrl(this.env, service.definition);\r\n\r\n const green = (text: string) => `\\x1b[32m${text}\\x1b[0m`;\r\n const yellow = (text: string) => `\\x1b[33m${text}\\x1b[0m`;\r\n\r\n const bannerLines = [\r\n '',\r\n yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'),\r\n ` ${green('Beamable Node microservice ready:')} ${green(service.definition.name)}`,\r\n yellow(' Quick shortcuts'),\r\n ` ${yellow('Docs:')} ${docsUrl}`,\r\n ` ${yellow('Endpoint:')} ${endpointBase}`,\r\n yellow('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'),\r\n '',\r\n ];\r\n\r\n process.stdout.write(`${bannerLines.join('\\n')}`);\r\n }\r\n\r\n private async initializeDependencyProviders(): Promise<void> {\r\n for (const service of this.services) {\r\n const builder = new DependencyBuilder();\r\n builder.tryAddSingletonInstance(LOGGER_TOKEN, service.logger);\r\n builder.tryAddSingletonInstance(ENVIRONMENT_CONFIG_TOKEN, this.env);\r\n builder.tryAddSingletonInstance(BeamableServiceManager, this.serviceManager);\r\n builder.tryAddSingletonInstance(service.definition.ctor, service.instance);\r\n builder.tryAddSingletonInstance(FederationRegistry, service.federationRegistry);\r\n\r\n for (const handler of service.configureHandlers) {\r\n await handler(builder);\r\n }\r\n\r\n const provider = builder.build();\r\n service.provider = provider;\r\n\r\n for (const handler of service.initializeHandlers) {\r\n await handler(provider);\r\n }\r\n\r\n Object.defineProperty(service.instance, 'provider', {\r\n value: provider,\r\n enumerable: false,\r\n configurable: false,\r\n writable: false,\r\n });\r\n }\r\n }\r\n\r\n private createRequestScope(path: string, service?: ServiceInstance): MutableDependencyScope {\r\n const targetService = service ?? this.findServiceForPath(path);\r\n const provider = targetService?.provider ?? new DependencyBuilder().build();\r\n return provider.createScope();\r\n }\r\n}\r\n\r\nfunction enforceAccess(access: ServiceAccess, ctx: RequestContext): void {\r\n switch (access) {\r\n case 'client':\r\n if (ctx.userId <= 0) {\r\n throw new UnauthorizedUserError(ctx.path);\r\n }\r\n break;\r\n case 'server':\r\n if (!scopeSetHas(ctx.scopes, 'server')) {\r\n throw new MissingScopesError(['server']);\r\n }\r\n break;\r\n case 'admin':\r\n if (!scopeSetHas(ctx.scopes, 'admin')) {\r\n throw new MissingScopesError(['admin']);\r\n }\r\n break;\r\n default:\r\n break;\r\n }\r\n}\r\n\r\n/**\r\n * Conditionally output to console.error only when running locally (not in container).\r\n * In containers, we want only Pino JSON logs to stdout for proper log collection.\r\n */\r\nfunction debugLog(...args: unknown[]): void {\r\n // Check process.env directly since this is a standalone function\r\n if (!isRunningInContainer()) {\r\n console.error(...args);\r\n }\r\n}\r\n\r\nfunction isRunningInContainer(env?: EnvironmentConfig): boolean {\r\n // Explicit container indicators (highest priority)\r\n if (\r\n process.env.DOTNET_RUNNING_IN_CONTAINER === 'true' ||\r\n process.env.CONTAINER === 'beamable' ||\r\n !!process.env.ECS_CONTAINER_METADATA_URI ||\r\n !!process.env.KUBERNETES_SERVICE_HOST\r\n ) {\r\n return true;\r\n }\r\n \r\n // If we have a routing key (from env object or process.env), we're definitely running locally\r\n // Local dev always has a routing key (either set explicitly or auto-generated)\r\n const hasRoutingKey = env?.routingKey || process.env.NAME_PREFIX || process.env.ROUTING_KEY;\r\n if (hasRoutingKey) {\r\n return false; // Local development\r\n }\r\n \r\n // If we have Beamable env vars but NO routing key, we're in a deployed container\r\n const hasBeamableEnvVars = !!(process.env.CID && process.env.PID && process.env.HOST && process.env.SECRET);\r\n if (hasBeamableEnvVars) {\r\n return true; // Deployed container (has env vars but no routing key)\r\n }\r\n \r\n // Default: assume local development\r\n return false;\r\n}\r\n\r\nfunction normalizeScopes(scopes: Array<unknown>): Set<string> {\r\n const normalized = new Set<string>();\r\n for (const scope of scopes) {\r\n if (typeof scope !== 'string' || scope.trim().length === 0) {\r\n continue;\r\n }\r\n normalized.add(scope.trim().toLowerCase());\r\n }\r\n return normalized;\r\n}\r\n\r\nfunction scopeSetHas(scopes: Set<string>, scope: string): boolean {\r\n const normalized = scope.trim().toLowerCase();\r\n if (normalized.length === 0) {\r\n return false;\r\n }\r\n return scopes.has('*') || scopes.has(normalized);\r\n}\r\n\r\nfunction buildPostmanBaseUrl(env: EnvironmentConfig, service: ServiceDefinition): string {\r\n const httpHost = hostToHttpUrl(env.host);\r\n const routingKeyPart = env.routingKey ? env.routingKey : '';\r\n return `${httpHost}/basic/${env.cid}.${env.pid}.${routingKeyPart}${service.qualifiedName}/`;\r\n}\r\n\r\nfunction buildDocsPortalUrl(env: EnvironmentConfig, service: ServiceDefinition): string {\r\n const portalHost = hostToPortalUrl(hostToHttpUrl(env.host));\r\n const queryParams: Record<string, string> = { srcTool: 'node-runtime' };\r\n if (env.routingKey) {\r\n queryParams.routingKey = env.routingKey;\r\n }\r\n const query = new URLSearchParams(queryParams);\r\n if (env.refreshToken) {\r\n query.set('refresh_token', env.refreshToken);\r\n }\r\n const beamoId = service.name;\r\n return `${portalHost}/${env.cid}/games/${env.pid}/realms/${env.pid}/microservices/micro_${beamoId}/docs?${query.toString()}`;\r\n}\r\n\r\nexport async function runMicroservice(): Promise<void> {\r\n // Immediate console output to verify process is starting (local dev only)\r\n // In containers, we rely on Pino logger for all output\r\n debugLog('[BEAMABLE-NODE] Starting microservice...');\r\n debugLog(`[BEAMABLE-NODE] Node version: ${process.version}`);\r\n debugLog(`[BEAMABLE-NODE] Working directory: ${process.cwd()}`);\r\n debugLog(`[BEAMABLE-NODE] Environment: ${JSON.stringify({\r\n NODE_ENV: process.env.NODE_ENV,\r\n CID: process.env.CID ? 'SET' : 'NOT SET',\r\n PID: process.env.PID ? 'SET' : 'NOT SET',\r\n HOST: process.env.HOST ? 'SET' : 'NOT SET',\r\n SECRET: process.env.SECRET ? 'SET' : 'NOT SET',\r\n })}`);\r\n if (process.env.BEAMABLE_SKIP_RUNTIME === 'true') {\r\n return;\r\n }\r\n const runtime = new MicroserviceRuntime();\r\n \r\n // Handle uncaught errors - log them but don't crash immediately\r\n // This allows the health check server to keep running so we can debug\r\n // In containers, errors will be logged by the logger in the runtime\r\n // Locally, use console.error for visibility\r\n process.on('uncaughtException', (error) => {\r\n // In containers, the logger will handle this; locally, show in console\r\n debugLog('Uncaught Exception:', error);\r\n // Don't exit - let the health check server keep running\r\n });\r\n \r\n process.on('unhandledRejection', (reason, promise) => {\r\n // In containers, the logger will handle this; locally, show in console\r\n debugLog('Unhandled Rejection at:', promise, 'reason:', reason);\r\n // Don't exit - let the health check server keep running\r\n });\r\n \r\n try {\r\n await runtime.start();\r\n } catch (error) {\r\n // Log the error but don't exit - health check server is running\r\n // This allows the container to stay alive so we can see what went wrong\r\n // In containers, the logger already logged it in start(); locally, also use console.error\r\n debugLog('Failed to start microservice runtime:', error);\r\n // Keep the process alive so health check can continue\r\n // The health check will return 503 until isReady is true\r\n }\r\n \r\n const shutdownSignals: NodeJS.Signals[] = ['SIGTERM', 'SIGINT'];\r\n shutdownSignals.forEach((signal) => {\r\n process.once(signal, async () => {\r\n await runtime.shutdown();\r\n process.exit(0);\r\n });\r\n });\r\n}\r\n\r\ninterface GenerateOpenApiOptions {\r\n serviceName?: string;\r\n}\r\n\r\nexport function generateOpenApiDocumentForRegisteredServices(\r\n overrides: Partial<EnvironmentConfig> = {},\r\n options: GenerateOpenApiOptions = {},\r\n): unknown {\r\n const services = listRegisteredServices();\r\n if (services.length === 0) {\r\n throw new Error('No microservices registered. Import your service module before generating documentation.');\r\n }\r\n\r\n const baseEnv = buildEnvironmentConfig(overrides);\r\n const primary = options.serviceName\r\n ? services.find((svc) => svc.name === options.serviceName || svc.qualifiedName === options.serviceName)\r\n : services[0];\r\n\r\n if (!primary) {\r\n throw new Error(`No registered microservice matched '${options.serviceName}'.`);\r\n }\r\n\r\n return generateOpenApiDocument(\r\n {\r\n qualifiedName: primary.qualifiedName,\r\n name: primary.name,\r\n callables: Array.from(primary.callables.entries()).map(([displayName, metadata]) => ({\r\n name: displayName,\r\n route: metadata.route,\r\n metadata,\r\n handler: undefined,\r\n })),\r\n },\r\n baseEnv,\r\n );\r\n}\r\n\r\nfunction buildEnvironmentConfig(overrides: Partial<EnvironmentConfig>): EnvironmentConfig {\r\n const merged: Partial<EnvironmentConfig> = { ...overrides };\r\n\r\n const ensure = (key: keyof EnvironmentConfig, fallbackEnv?: string) => {\r\n if (merged[key] !== undefined) {\r\n return;\r\n }\r\n if (fallbackEnv && process.env[fallbackEnv]) {\r\n merged[key] = process.env[fallbackEnv] as never;\r\n }\r\n };\r\n\r\n ensure('cid', 'CID');\r\n ensure('pid', 'PID');\r\n ensure('host', 'HOST');\r\n ensure('secret', 'SECRET');\r\n ensure('refreshToken', 'REFRESH_TOKEN');\r\n // Routing key is optional for deployed services (in containers)\r\n // It will be resolved to empty string if not provided and running in container\r\n if (!merged.routingKey && !isRunningInContainer()) {\r\n ensure('routingKey', 'NAME_PREFIX');\r\n }\r\n\r\n if (!merged.cid || !merged.pid || !merged.host) {\r\n throw new Error('CID, PID, and HOST are required to generate documentation.');\r\n }\r\n\r\n const base = loadEnvironmentConfig();\r\n\r\n return {\r\n ...base,\r\n ...merged,\r\n routingKey: merged.routingKey ?? base.routingKey,\r\n };\r\n}\r\n"]}
|
package/package.json
CHANGED
package/src/env.ts
CHANGED
|
@@ -42,16 +42,42 @@ function resolveRoutingKey(): string | undefined {
|
|
|
42
42
|
return raw.trim();
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
// Check for explicit container indicators first (highest priority)
|
|
46
|
+
const isExplicitlyInContainer =
|
|
47
|
+
process.env.DOTNET_RUNNING_IN_CONTAINER === 'true' ||
|
|
48
|
+
process.env.CONTAINER === 'beamable' ||
|
|
49
|
+
!!process.env.ECS_CONTAINER_METADATA_URI ||
|
|
50
|
+
!!process.env.KUBERNETES_SERVICE_HOST;
|
|
51
|
+
|
|
52
|
+
if (isExplicitlyInContainer) {
|
|
53
|
+
// We're definitely in a container - return undefined (becomes None in Scala)
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
|
|
45
57
|
// For deployed services (in containers), routing key should be undefined (None in Scala)
|
|
46
58
|
// Beamable sets CID, PID, HOST, SECRET in containers but NOT routing key
|
|
47
|
-
//
|
|
48
|
-
|
|
59
|
+
// BUT: Local dev also has these env vars from .env file, so we need to be more careful
|
|
60
|
+
// If we have the required Beamable env vars but NO routing key AND no explicit container indicators,
|
|
61
|
+
// we might be in a deployed container OR local dev without routing key set
|
|
62
|
+
// For safety, if we're not explicitly in a container, try to generate a routing key for local dev
|
|
63
|
+
const hasBeamableEnvVars = !!(process.env.CID && process.env.PID && process.env.HOST && process.env.SECRET);
|
|
49
64
|
const hasNoRoutingKey = !raw || raw.trim().length === 0;
|
|
50
65
|
|
|
66
|
+
// If we have Beamable env vars but no routing key, and we're not explicitly in a container,
|
|
67
|
+
// we might be in local dev - try to generate a routing key
|
|
68
|
+
// Only return undefined if we're explicitly in a container
|
|
51
69
|
if (hasBeamableEnvVars && hasNoRoutingKey) {
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
|
|
70
|
+
// Try to generate a routing key for local dev
|
|
71
|
+
// If this fails, we'll throw an error (which is fine for local dev)
|
|
72
|
+
try {
|
|
73
|
+
return getDefaultRoutingKeyForMachine();
|
|
74
|
+
} catch (error) {
|
|
75
|
+
// If we can't generate a routing key, we might be in a container
|
|
76
|
+
// But since we don't have explicit container indicators, assume local dev and throw
|
|
77
|
+
throw new Error(
|
|
78
|
+
`Unable to determine routing key automatically. Set NAME_PREFIX environment variable. ${(error as Error).message}`,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
55
81
|
}
|
|
56
82
|
|
|
57
83
|
// Local development - try to get a routing key
|
package/src/logger.ts
CHANGED
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
import pino, { destination, type Logger, type LoggerOptions } from 'pino';
|
|
2
2
|
import { Transform } from 'node:stream';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
3
4
|
import { ensureWritableTempDirectory } from './env.js';
|
|
4
5
|
import type { EnvironmentConfig } from './types.js';
|
|
5
6
|
|
|
7
|
+
// Helper to get require function that works in both CJS and ESM
|
|
8
|
+
declare const require: any;
|
|
9
|
+
function getRequire(): any {
|
|
10
|
+
// Check if we're in CJS context (require.main exists)
|
|
11
|
+
if (typeof require !== 'undefined' && typeof require.main !== 'undefined') {
|
|
12
|
+
// CJS context - use require directly
|
|
13
|
+
return require;
|
|
14
|
+
}
|
|
15
|
+
// ESM context - use createRequire with import.meta.url
|
|
16
|
+
// TypeScript will complain in CJS builds, but this code only runs in ESM
|
|
17
|
+
// @ts-ignore - import.meta is ESM-only, TypeScript error in CJS is expected
|
|
18
|
+
return createRequire(import.meta.url);
|
|
19
|
+
}
|
|
20
|
+
|
|
6
21
|
/**
|
|
7
22
|
* Detects if we're running in a container (deployed environment).
|
|
8
23
|
* This is used to determine log format: pretty for local dev, JSON for containers.
|
|
@@ -16,7 +31,7 @@ import type { EnvironmentConfig } from './types.js';
|
|
|
16
31
|
* - Has routing key (local dev always has a routing key)
|
|
17
32
|
* - Running via tsx/node directly (not in Docker)
|
|
18
33
|
*/
|
|
19
|
-
function isRunningInContainer(): boolean {
|
|
34
|
+
function isRunningInContainer(env?: EnvironmentConfig): boolean {
|
|
20
35
|
// Explicit container indicators (highest priority)
|
|
21
36
|
if (
|
|
22
37
|
process.env.DOTNET_RUNNING_IN_CONTAINER === 'true' ||
|
|
@@ -27,9 +42,10 @@ function isRunningInContainer(): boolean {
|
|
|
27
42
|
return true;
|
|
28
43
|
}
|
|
29
44
|
|
|
30
|
-
// If we have a routing key, we're definitely running locally
|
|
31
|
-
// Local dev always
|
|
32
|
-
|
|
45
|
+
// If we have a routing key (from env object or process.env), we're definitely running locally
|
|
46
|
+
// Local dev always has a routing key (either set explicitly or auto-generated)
|
|
47
|
+
const hasRoutingKey = env?.routingKey || process.env.NAME_PREFIX || process.env.ROUTING_KEY;
|
|
48
|
+
if (hasRoutingKey) {
|
|
33
49
|
return false; // Local development
|
|
34
50
|
}
|
|
35
51
|
|
|
@@ -168,7 +184,7 @@ function createBeamableLogFormatter(): Transform {
|
|
|
168
184
|
|
|
169
185
|
export function createLogger(env: EnvironmentConfig, options: LoggerFactoryOptions = {}): Logger {
|
|
170
186
|
const configuredDestination = options.destinationPath ?? process.env.LOG_PATH;
|
|
171
|
-
const inContainer = isRunningInContainer();
|
|
187
|
+
const inContainer = isRunningInContainer(env);
|
|
172
188
|
|
|
173
189
|
const pinoOptions: LoggerOptions = {
|
|
174
190
|
name: options.name ?? 'beamable-node-runtime',
|
|
@@ -201,7 +217,9 @@ export function createLogger(env: EnvironmentConfig, options: LoggerFactoryOptio
|
|
|
201
217
|
// If not available, fall back to default Pino JSON output
|
|
202
218
|
try {
|
|
203
219
|
// Check if pino-pretty is available
|
|
204
|
-
|
|
220
|
+
// Use getRequire() which handles both CJS and ESM contexts
|
|
221
|
+
const requireFn = getRequire();
|
|
222
|
+
const pinoPretty = requireFn('pino-pretty');
|
|
205
223
|
// Create a pretty stream with formatting options
|
|
206
224
|
const prettyStream = pinoPretty({
|
|
207
225
|
colorize: true,
|
|
@@ -213,6 +231,7 @@ export function createLogger(env: EnvironmentConfig, options: LoggerFactoryOptio
|
|
|
213
231
|
return pino(pinoOptions, prettyStream);
|
|
214
232
|
} catch {
|
|
215
233
|
// pino-pretty not available, use default Pino output (JSON but readable)
|
|
234
|
+
// This is expected if pino-pretty isn't installed, so we silently fall back
|
|
216
235
|
return pino(pinoOptions, process.stdout);
|
|
217
236
|
}
|
|
218
237
|
}
|
package/src/runtime.ts
CHANGED
|
@@ -92,7 +92,7 @@ export class MicroserviceRuntime {
|
|
|
92
92
|
this.authManager = new AuthManager(this.env, this.requester);
|
|
93
93
|
this.requester.on('event', (envelope) => this.handleEvent(envelope));
|
|
94
94
|
|
|
95
|
-
if (!isRunningInContainer() && this.services.length > 0) {
|
|
95
|
+
if (!isRunningInContainer(this.env) && this.services.length > 0) {
|
|
96
96
|
this.discovery = new DiscoveryBroadcaster({
|
|
97
97
|
env: this.env,
|
|
98
98
|
serviceName: this.services[0].definition.name,
|
|
@@ -255,7 +255,7 @@ export class MicroserviceRuntime {
|
|
|
255
255
|
// The gateway's ServiceIdentity.fullNameNoType lowercases the service name when creating bindings,
|
|
256
256
|
// but the registration request should use the original case (as C# does)
|
|
257
257
|
// This ensures the service name in the registration matches what the portal expects
|
|
258
|
-
const isDeployed = isRunningInContainer();
|
|
258
|
+
const isDeployed = isRunningInContainer(this.env);
|
|
259
259
|
|
|
260
260
|
// For deployed services, routingKey should be null/undefined (None in Scala)
|
|
261
261
|
// For local dev, routingKey should be the actual routing key string
|
|
@@ -709,7 +709,7 @@ export class MicroserviceRuntime {
|
|
|
709
709
|
|
|
710
710
|
// Only print helpful URLs in local development (not in containers)
|
|
711
711
|
// In containers, we want only JSON logs for proper log collection
|
|
712
|
-
if (isRunningInContainer()) {
|
|
712
|
+
if (isRunningInContainer(this.env)) {
|
|
713
713
|
return;
|
|
714
714
|
}
|
|
715
715
|
|
|
@@ -796,29 +796,38 @@ function enforceAccess(access: ServiceAccess, ctx: RequestContext): void {
|
|
|
796
796
|
* In containers, we want only Pino JSON logs to stdout for proper log collection.
|
|
797
797
|
*/
|
|
798
798
|
function debugLog(...args: unknown[]): void {
|
|
799
|
+
// Check process.env directly since this is a standalone function
|
|
799
800
|
if (!isRunningInContainer()) {
|
|
800
801
|
console.error(...args);
|
|
801
802
|
}
|
|
802
803
|
}
|
|
803
804
|
|
|
804
|
-
function isRunningInContainer(): boolean {
|
|
805
|
-
//
|
|
806
|
-
|
|
807
|
-
// This is more reliable than checking for DOTNET_RUNNING_IN_CONTAINER (which is C#-specific)
|
|
808
|
-
const hasBeamableEnvVars = !!(process.env.CID && process.env.PID && process.env.HOST && process.env.SECRET);
|
|
809
|
-
const hasNoRoutingKey = !process.env.NAME_PREFIX && !process.env.ROUTING_KEY;
|
|
810
|
-
|
|
811
|
-
if (hasBeamableEnvVars && hasNoRoutingKey) {
|
|
812
|
-
return true; // Deployed container
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
// Fallback checks for other container indicators
|
|
816
|
-
return (
|
|
805
|
+
function isRunningInContainer(env?: EnvironmentConfig): boolean {
|
|
806
|
+
// Explicit container indicators (highest priority)
|
|
807
|
+
if (
|
|
817
808
|
process.env.DOTNET_RUNNING_IN_CONTAINER === 'true' ||
|
|
818
809
|
process.env.CONTAINER === 'beamable' ||
|
|
819
810
|
!!process.env.ECS_CONTAINER_METADATA_URI ||
|
|
820
811
|
!!process.env.KUBERNETES_SERVICE_HOST
|
|
821
|
-
)
|
|
812
|
+
) {
|
|
813
|
+
return true;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
// If we have a routing key (from env object or process.env), we're definitely running locally
|
|
817
|
+
// Local dev always has a routing key (either set explicitly or auto-generated)
|
|
818
|
+
const hasRoutingKey = env?.routingKey || process.env.NAME_PREFIX || process.env.ROUTING_KEY;
|
|
819
|
+
if (hasRoutingKey) {
|
|
820
|
+
return false; // Local development
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// If we have Beamable env vars but NO routing key, we're in a deployed container
|
|
824
|
+
const hasBeamableEnvVars = !!(process.env.CID && process.env.PID && process.env.HOST && process.env.SECRET);
|
|
825
|
+
if (hasBeamableEnvVars) {
|
|
826
|
+
return true; // Deployed container (has env vars but no routing key)
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
// Default: assume local development
|
|
830
|
+
return false;
|
|
822
831
|
}
|
|
823
832
|
|
|
824
833
|
function normalizeScopes(scopes: Array<unknown>): Set<string> {
|