@omen.foundation/node-microservice-runtime 0.1.101 → 0.1.102

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.
@@ -71,29 +71,42 @@ function loadDeveloperEnvVars(beamEnvPath) {
71
71
  }
72
72
  async function fetchBeamableConfig(env, timeoutMs = 2000) {
73
73
  const configVars = {};
74
- if (!env.secret || !env.cid || !env.pid) {
75
- console.log('[EnvLoader] Skipping Beamable Config fetch - missing SECRET, CID, or PID');
74
+ if (!env.cid || !env.pid) {
75
+ console.log('[EnvLoader] Skipping Beamable Config fetch - missing CID or PID');
76
+ return configVars;
77
+ }
78
+ const accessToken = process.env.ACCESS_TOKEN || process.env.BEAMABLE_TOKEN;
79
+ const hasSecret = !!env.secret;
80
+ if (!accessToken && !hasSecret) {
81
+ console.log('[EnvLoader] Skipping Beamable Config fetch - missing ACCESS_TOKEN/BEAMABLE_TOKEN or SECRET');
76
82
  return configVars;
77
83
  }
78
84
  try {
79
85
  const apiUrl = (0, urls_js_1.hostToHttpUrl)(env.host);
80
86
  const uriPath = process.env.BEAM_CONFIG_API_PATH || '/basic/realms/config';
81
87
  const configUrl = new node_url_1.URL(uriPath, apiUrl).toString();
82
- const pathAndQuery = uriPath;
83
- const signature = calculateSignature(env.pid, env.secret, pathAndQuery, null, '1');
88
+ const headers = {
89
+ 'Content-Type': 'application/json',
90
+ Accept: 'application/json',
91
+ 'X-BEAM-SCOPE': `${env.cid}.${env.pid}`,
92
+ };
93
+ if (accessToken) {
94
+ headers['Authorization'] = `Bearer ${accessToken}`;
95
+ }
96
+ else if (hasSecret && env.secret) {
97
+ const pathAndQuery = uriPath;
98
+ const signature = calculateSignature(env.pid, env.secret, pathAndQuery, null, '1');
99
+ headers['X-BEAM-SIGNATURE'] = signature;
100
+ }
84
101
  console.log(`[EnvLoader] Fetching Beamable Config from ${configUrl}...`);
102
+ console.log(`[EnvLoader] Using ${accessToken ? 'token-based' : 'signature-based'} authentication`);
85
103
  const controller = new AbortController();
86
104
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
87
105
  let response;
88
106
  try {
89
107
  response = await fetch(configUrl, {
90
108
  method: 'GET',
91
- headers: {
92
- 'Content-Type': 'application/json',
93
- Accept: 'application/json',
94
- 'X-BEAM-SCOPE': `${env.cid}.${env.pid}`,
95
- 'X-BEAM-SIGNATURE': signature,
96
- },
109
+ headers,
97
110
  signal: controller.signal,
98
111
  });
99
112
  clearTimeout(timeoutId);
@@ -101,13 +114,25 @@ async function fetchBeamableConfig(env, timeoutMs = 2000) {
101
114
  catch (error) {
102
115
  clearTimeout(timeoutId);
103
116
  if (error instanceof Error && error.name === 'AbortError') {
117
+ console.warn(`[EnvLoader] Beamable Config fetch timed out after ${timeoutMs}ms`);
118
+ console.warn(`[EnvLoader] URL: ${configUrl}`);
119
+ console.warn(`[EnvLoader] Auth method: ${accessToken ? 'token-based' : 'signature-based'}`);
104
120
  throw new Error(`Beamable Config fetch timed out after ${timeoutMs}ms`);
105
121
  }
106
122
  throw error;
107
123
  }
108
124
  if (!response.ok) {
109
125
  const errorText = await response.text().catch(() => 'Unknown error');
110
- console.warn(`[EnvLoader] Failed to fetch Beamable Config: ${response.status} ${response.statusText} - ${errorText.substring(0, 200)}`);
126
+ console.warn(`[EnvLoader] Failed to fetch Beamable Config: ${response.status} ${response.statusText}`);
127
+ console.warn(`[EnvLoader] URL: ${configUrl}`);
128
+ console.warn(`[EnvLoader] Auth method: ${accessToken ? 'token-based' : 'signature-based'}`);
129
+ if (response.status === 401 || response.status === 403) {
130
+ console.warn(`[EnvLoader] Authentication failed. This endpoint requires token-based auth (ACCESS_TOKEN or BEAMABLE_TOKEN).`);
131
+ if (!accessToken) {
132
+ console.warn(`[EnvLoader] Tip: Set ACCESS_TOKEN or BEAMABLE_TOKEN in your environment to enable Beamable Config fetching.`);
133
+ }
134
+ }
135
+ console.warn(`[EnvLoader] Response: ${errorText.substring(0, 500)}`);
111
136
  return configVars;
112
137
  }
113
138
  const data = (await response.json());
@@ -1 +1 @@
1
- {"version":3,"file":"env-loader.d.ts","sourceRoot":"","sources":["../src/env-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAsBpD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAenE;AA4LD;;;;;;;;;;;;GAYG;AACH,wBAAsB,iCAAiC,CACrD,GAAG,EAAE,iBAAiB,EACtB,WAAW,CAAC,EAAE,MAAM,EACpB,aAAa,GAAE,OAAc,EAC7B,SAAS,GAAE,MAAa,GACvB,OAAO,CAAC,IAAI,CAAC,CAoEf"}
1
+ {"version":3,"file":"env-loader.d.ts","sourceRoot":"","sources":["../src/env-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAsBpD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAenE;AA0ND;;;;;;;;;;;;GAYG;AACH,wBAAsB,iCAAiC,CACrD,GAAG,EAAE,iBAAiB,EACtB,WAAW,CAAC,EAAE,MAAM,EACpB,aAAa,GAAE,OAAc,EAC7B,SAAS,GAAE,MAAa,GACvB,OAAO,CAAC,IAAI,CAAC,CAoEf"}
@@ -113,8 +113,15 @@ function loadDeveloperEnvVars(beamEnvPath) {
113
113
  async function fetchBeamableConfig(env, timeoutMs = 2000) {
114
114
  const configVars = {};
115
115
  // Skip if we don't have required credentials
116
- if (!env.secret || !env.cid || !env.pid) {
117
- console.log('[EnvLoader] Skipping Beamable Config fetch - missing SECRET, CID, or PID');
116
+ if (!env.cid || !env.pid) {
117
+ console.log('[EnvLoader] Skipping Beamable Config fetch - missing CID or PID');
118
+ return configVars;
119
+ }
120
+ // Check if we have an access token (preferred) or secret (fallback)
121
+ const accessToken = process.env.ACCESS_TOKEN || process.env.BEAMABLE_TOKEN;
122
+ const hasSecret = !!env.secret;
123
+ if (!accessToken && !hasSecret) {
124
+ console.log('[EnvLoader] Skipping Beamable Config fetch - missing ACCESS_TOKEN/BEAMABLE_TOKEN or SECRET');
118
125
  return configVars;
119
126
  }
120
127
  try {
@@ -123,10 +130,24 @@ async function fetchBeamableConfig(env, timeoutMs = 2000) {
123
130
  // Default endpoint matches C# SDK and Portal: /basic/realms/config (not /api/basic/realms/config)
124
131
  const uriPath = process.env.BEAM_CONFIG_API_PATH || '/basic/realms/config';
125
132
  const configUrl = new URL(uriPath, apiUrl).toString();
126
- // Calculate signature for signed request
127
- const pathAndQuery = uriPath;
128
- const signature = calculateSignature(env.pid, env.secret, pathAndQuery, null, '1');
133
+ // Build headers - prefer token-based auth if available, fallback to signature-based
134
+ const headers = {
135
+ 'Content-Type': 'application/json',
136
+ Accept: 'application/json',
137
+ 'X-BEAM-SCOPE': `${env.cid}.${env.pid}`,
138
+ };
139
+ if (accessToken) {
140
+ // Use token-based authentication (matches Portal's $beamable.get())
141
+ headers['Authorization'] = `Bearer ${accessToken}`;
142
+ }
143
+ else if (hasSecret && env.secret) {
144
+ // Fallback to signature-based authentication
145
+ const pathAndQuery = uriPath;
146
+ const signature = calculateSignature(env.pid, env.secret, pathAndQuery, null, '1');
147
+ headers['X-BEAM-SIGNATURE'] = signature;
148
+ }
129
149
  console.log(`[EnvLoader] Fetching Beamable Config from ${configUrl}...`);
150
+ console.log(`[EnvLoader] Using ${accessToken ? 'token-based' : 'signature-based'} authentication`);
130
151
  // Add timeout to fetch to prevent hanging requests
131
152
  const controller = new AbortController();
132
153
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
@@ -134,12 +155,7 @@ async function fetchBeamableConfig(env, timeoutMs = 2000) {
134
155
  try {
135
156
  response = await fetch(configUrl, {
136
157
  method: 'GET',
137
- headers: {
138
- 'Content-Type': 'application/json',
139
- Accept: 'application/json',
140
- 'X-BEAM-SCOPE': `${env.cid}.${env.pid}`,
141
- 'X-BEAM-SIGNATURE': signature,
142
- },
158
+ headers,
143
159
  signal: controller.signal,
144
160
  });
145
161
  clearTimeout(timeoutId);
@@ -147,13 +163,25 @@ async function fetchBeamableConfig(env, timeoutMs = 2000) {
147
163
  catch (error) {
148
164
  clearTimeout(timeoutId);
149
165
  if (error instanceof Error && error.name === 'AbortError') {
166
+ console.warn(`[EnvLoader] Beamable Config fetch timed out after ${timeoutMs}ms`);
167
+ console.warn(`[EnvLoader] URL: ${configUrl}`);
168
+ console.warn(`[EnvLoader] Auth method: ${accessToken ? 'token-based' : 'signature-based'}`);
150
169
  throw new Error(`Beamable Config fetch timed out after ${timeoutMs}ms`);
151
170
  }
152
171
  throw error;
153
172
  }
154
173
  if (!response.ok) {
155
174
  const errorText = await response.text().catch(() => 'Unknown error');
156
- console.warn(`[EnvLoader] Failed to fetch Beamable Config: ${response.status} ${response.statusText} - ${errorText.substring(0, 200)}`);
175
+ console.warn(`[EnvLoader] Failed to fetch Beamable Config: ${response.status} ${response.statusText}`);
176
+ console.warn(`[EnvLoader] URL: ${configUrl}`);
177
+ console.warn(`[EnvLoader] Auth method: ${accessToken ? 'token-based' : 'signature-based'}`);
178
+ if (response.status === 401 || response.status === 403) {
179
+ console.warn(`[EnvLoader] Authentication failed. This endpoint requires token-based auth (ACCESS_TOKEN or BEAMABLE_TOKEN).`);
180
+ if (!accessToken) {
181
+ console.warn(`[EnvLoader] Tip: Set ACCESS_TOKEN or BEAMABLE_TOKEN in your environment to enable Beamable Config fetching.`);
182
+ }
183
+ }
184
+ console.warn(`[EnvLoader] Response: ${errorText.substring(0, 500)}`);
157
185
  return configVars;
158
186
  }
159
187
  const data = (await response.json());
@@ -1 +1 @@
1
- {"version":3,"file":"env-loader.js","sourceRoot":"","sources":["../src/env-loader.ts"],"names":[],"mappings":"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","sourcesContent":["/**\r\n * Environment Variable Loader\r\n * \r\n * This module handles loading environment variables from two sources:\r\n * 1. Developer-defined variables (from beam.env file)\r\n * 2. Beamable Config values (from API: namespace -> environment -> key-value pairs)\r\n * \r\n * All variables are injected into process.env before service initialization.\r\n */\r\n\r\nimport { existsSync, readFileSync } from 'node:fs';\r\nimport { join } from 'node:path';\r\nimport { createHash } from 'node:crypto';\r\nimport { URL } from 'node:url';\r\nimport type { EnvironmentConfig } from './types.js';\r\nimport { hostToHttpUrl } from './utils/urls.js';\r\n\r\n/**\r\n * Calculates Beamable signature for signed requests\r\n * Signature format: MD5(secret + pid + version + uriPathAndQuery + body) as Base64\r\n */\r\nfunction calculateSignature(\r\n pid: string,\r\n secret: string,\r\n uriPathAndQuery: string,\r\n body: string | null = null,\r\n version: string = '1'\r\n): string {\r\n let dataToSign = `${secret}${pid}${version}${uriPathAndQuery}`;\r\n if (body) {\r\n dataToSign += body;\r\n }\r\n const hash = createHash('md5').update(dataToSign, 'utf8').digest('base64');\r\n return hash;\r\n}\r\n\r\n/**\r\n * Loads developer-defined environment variables from beam.env file\r\n * Supports standard .env format: KEY=value\r\n * \r\n * This is exported for synchronous loading before logger initialization\r\n */\r\nexport function loadDeveloperEnvVarsSync(beamEnvPath?: string): void {\r\n const envVars = loadDeveloperEnvVars(beamEnvPath);\r\n \r\n // Inject immediately into process.env\r\n let injectedCount = 0;\r\n for (const [key, value] of Object.entries(envVars)) {\r\n if (!(key in process.env)) {\r\n process.env[key] = value;\r\n injectedCount++;\r\n }\r\n }\r\n \r\n if (injectedCount > 0) {\r\n console.log(`[EnvLoader] Synchronously injected ${injectedCount} developer-defined variables from beam.env`);\r\n }\r\n}\r\n\r\n/**\r\n * Loads developer-defined environment variables from beam.env file\r\n * Supports standard .env format: KEY=value\r\n */\r\nfunction loadDeveloperEnvVars(beamEnvPath?: string): Record<string, string> {\r\n const envVars: Record<string, string> = {};\r\n \r\n // Try to find beam.env file\r\n const possiblePaths = [\r\n beamEnvPath,\r\n join(process.cwd(), 'beam.env'),\r\n join(process.cwd(), '.beam.env'),\r\n '/beam/service/beam.env', // Container path\r\n '/beam/service/.beam.env',\r\n ].filter((path): path is string => !!path && existsSync(path));\r\n\r\n if (possiblePaths.length === 0) {\r\n return envVars;\r\n }\r\n\r\n const envFilePath = possiblePaths[0];\r\n try {\r\n const content = readFileSync(envFilePath, 'utf-8');\r\n const lines = content.split('\\n');\r\n \r\n for (const line of lines) {\r\n const trimmed = line.trim();\r\n // Skip empty lines and comments\r\n if (!trimmed || trimmed.startsWith('#')) {\r\n continue;\r\n }\r\n \r\n // Parse KEY=value format\r\n const match = trimmed.match(/^([^=#]+)=(.*)$/);\r\n if (match) {\r\n const key = match[1].trim();\r\n let value = match[2].trim();\r\n \r\n // Remove quotes if present\r\n if ((value.startsWith('\"') && value.endsWith('\"')) || \r\n (value.startsWith(\"'\") && value.endsWith(\"'\"))) {\r\n value = value.slice(1, -1);\r\n }\r\n \r\n if (key) {\r\n envVars[key] = value;\r\n }\r\n }\r\n }\r\n \r\n console.log(`[EnvLoader] Loaded ${Object.keys(envVars).length} variables from ${envFilePath}`);\r\n } catch (error) {\r\n console.warn(`[EnvLoader] Failed to load beam.env from ${envFilePath}:`, error instanceof Error ? error.message : String(error));\r\n }\r\n \r\n return envVars;\r\n}\r\n\r\n/**\r\n * Beamable Config API response structure\r\n */\r\ninterface BeamableConfigResponse {\r\n config?: {\r\n [key: string]: {\r\n [environment: string]: string | number | boolean | null;\r\n };\r\n };\r\n [key: string]: unknown;\r\n}\r\n\r\n/**\r\n * Fetches Beamable Config values from the API\r\n * \r\n * The endpoint can be configured via BEAM_CONFIG_API_PATH environment variable.\r\n * Default: /api/basic/realms/config\r\n * \r\n * Note: The exact endpoint format may vary. The API should return a structure like:\r\n * {\r\n * \"config\": {\r\n * \"key1\": {\r\n * \"environment\": \"value1\"\r\n * }\r\n * }\r\n * }\r\n * \r\n * Returns key-value pairs from the current realm's config namespace/environment\r\n */\r\nasync function fetchBeamableConfig(\r\n env: EnvironmentConfig,\r\n timeoutMs: number = 2000\r\n): Promise<Record<string, string>> {\r\n const configVars: Record<string, string> = {};\r\n \r\n // Skip if we don't have required credentials\r\n if (!env.secret || !env.cid || !env.pid) {\r\n console.log('[EnvLoader] Skipping Beamable Config fetch - missing SECRET, CID, or PID');\r\n return configVars;\r\n }\r\n\r\n try {\r\n const apiUrl = hostToHttpUrl(env.host);\r\n // Allow endpoint to be configured via environment variable (for flexibility)\r\n // Default endpoint matches C# SDK and Portal: /basic/realms/config (not /api/basic/realms/config)\r\n const uriPath = process.env.BEAM_CONFIG_API_PATH || '/basic/realms/config';\r\n const configUrl = new URL(uriPath, apiUrl).toString();\r\n \r\n // Calculate signature for signed request\r\n const pathAndQuery = uriPath;\r\n const signature = calculateSignature(env.pid, env.secret, pathAndQuery, null, '1');\r\n \r\n console.log(`[EnvLoader] Fetching Beamable Config from ${configUrl}...`);\r\n \r\n // Add timeout to fetch to prevent hanging requests\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\r\n \r\n let response: Response;\r\n try {\r\n response = await fetch(configUrl, {\r\n method: 'GET',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n Accept: 'application/json',\r\n 'X-BEAM-SCOPE': `${env.cid}.${env.pid}`,\r\n 'X-BEAM-SIGNATURE': signature,\r\n },\r\n signal: controller.signal,\r\n });\r\n clearTimeout(timeoutId);\r\n } catch (error) {\r\n clearTimeout(timeoutId);\r\n if (error instanceof Error && error.name === 'AbortError') {\r\n throw new Error(`Beamable Config fetch timed out after ${timeoutMs}ms`);\r\n }\r\n throw error;\r\n }\r\n\r\n if (!response.ok) {\r\n const errorText = await response.text().catch(() => 'Unknown error');\r\n console.warn(`[EnvLoader] Failed to fetch Beamable Config: ${response.status} ${response.statusText} - ${errorText.substring(0, 200)}`);\r\n return configVars;\r\n }\r\n\r\n const data = (await response.json()) as BeamableConfigResponse;\r\n \r\n // Parse config structure: config[key][environment] = value\r\n // We need to determine the current environment - typically it's the PID or a specific env var\r\n const currentEnvironment = process.env.BEAM_ENVIRONMENT || process.env.ENVIRONMENT || env.pid;\r\n \r\n if (data.config) {\r\n for (const [key, environments] of Object.entries(data.config)) {\r\n // Try to get value for current environment, fallback to first available\r\n let value: string | number | boolean | null | undefined;\r\n \r\n if (environments && typeof environments === 'object') {\r\n value = environments[currentEnvironment];\r\n \r\n // If no value for current environment, try common environment names\r\n if (value === undefined) {\r\n value = environments['production'] || \r\n environments['staging'] || \r\n environments['development'] ||\r\n environments['dev'] ||\r\n Object.values(environments)[0];\r\n }\r\n }\r\n \r\n // Convert value to string and add to env vars\r\n if (value !== undefined && value !== null) {\r\n // Use BEAM_CONFIG_ prefix to avoid conflicts\r\n const envKey = `BEAM_CONFIG_${key.toUpperCase()}`;\r\n configVars[envKey] = String(value);\r\n }\r\n }\r\n \r\n console.log(`[EnvLoader] Loaded ${Object.keys(configVars).length} variables from Beamable Config`);\r\n } else {\r\n console.log('[EnvLoader] Beamable Config response contains no config object');\r\n }\r\n } catch (error) {\r\n console.warn('[EnvLoader] Failed to fetch Beamable Config:', error instanceof Error ? error.message : String(error));\r\n }\r\n \r\n return configVars;\r\n}\r\n\r\n/**\r\n * Loads all environment variables from both sources and injects them into process.env\r\n * \r\n * Priority (highest to lowest):\r\n * 1. Existing process.env values (never overwrite)\r\n * 2. Developer-defined variables (beam.env)\r\n * 3. Beamable Config values (API)\r\n * \r\n * @param env - Environment configuration (CID, PID, HOST, SECRET)\r\n * @param beamEnvPath - Optional path to beam.env file\r\n * @param waitForConfig - If true, waits up to timeoutMs for Beamable Config. If false, returns immediately after developer vars.\r\n * @param timeoutMs - Maximum time to wait for Beamable Config (default: 2000ms)\r\n */\r\nexport async function loadAndInjectEnvironmentVariables(\r\n env: EnvironmentConfig,\r\n beamEnvPath?: string,\r\n waitForConfig: boolean = true,\r\n timeoutMs: number = 2000\r\n): Promise<void> {\r\n console.log('[EnvLoader] Starting environment variable loading...');\r\n \r\n // Load developer-defined variables from beam.env (synchronous, immediate)\r\n const developerVars = loadDeveloperEnvVars(beamEnvPath);\r\n \r\n // Inject developer vars immediately\r\n let injectedCount = 0;\r\n for (const [key, value] of Object.entries(developerVars)) {\r\n if (!(key in process.env)) {\r\n process.env[key] = value;\r\n injectedCount++;\r\n }\r\n }\r\n \r\n if (injectedCount > 0) {\r\n console.log(`[EnvLoader] Injected ${injectedCount} developer-defined variables from beam.env`);\r\n }\r\n \r\n // Fetch Beamable Config values (async, with optional timeout)\r\n if (waitForConfig) {\r\n try {\r\n const configPromise = fetchBeamableConfig(env, timeoutMs);\r\n const timeoutPromise = new Promise<Record<string, string>>((resolve) => {\r\n setTimeout(() => {\r\n console.warn(`[EnvLoader] Beamable Config fetch timed out after ${timeoutMs}ms, continuing without it`);\r\n resolve({});\r\n }, timeoutMs);\r\n });\r\n \r\n const beamableConfigVars = await Promise.race([configPromise, timeoutPromise]);\r\n \r\n // Inject Beamable Config vars\r\n let configInjectedCount = 0;\r\n for (const [key, value] of Object.entries(beamableConfigVars)) {\r\n if (!(key in process.env)) {\r\n process.env[key] = value;\r\n configInjectedCount++;\r\n }\r\n }\r\n \r\n if (configInjectedCount > 0) {\r\n console.log(`[EnvLoader] Injected ${configInjectedCount} variables from Beamable Config`);\r\n }\r\n } catch (error) {\r\n console.warn('[EnvLoader] Error loading Beamable Config (continuing anyway):', error instanceof Error ? error.message : String(error));\r\n }\r\n } else {\r\n // Start fetch in background (non-blocking)\r\n fetchBeamableConfig(env)\r\n .then((beamableConfigVars) => {\r\n let configInjectedCount = 0;\r\n for (const [key, value] of Object.entries(beamableConfigVars)) {\r\n if (!(key in process.env)) {\r\n process.env[key] = value;\r\n configInjectedCount++;\r\n }\r\n }\r\n if (configInjectedCount > 0) {\r\n console.log(`[EnvLoader] Injected ${configInjectedCount} variables from Beamable Config (loaded asynchronously)`);\r\n }\r\n })\r\n .catch((error) => {\r\n console.warn('[EnvLoader] Failed to load Beamable Config in background:', error instanceof Error ? error.message : String(error));\r\n });\r\n }\r\n \r\n console.log('[EnvLoader] Environment variable loading completed');\r\n}\r\n\r\n"]}
1
+ {"version":3,"file":"env-loader.js","sourceRoot":"","sources":["../src/env-loader.ts"],"names":[],"mappings":"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","sourcesContent":["/**\r\n * Environment Variable Loader\r\n * \r\n * This module handles loading environment variables from two sources:\r\n * 1. Developer-defined variables (from beam.env file)\r\n * 2. Beamable Config values (from API: namespace -> environment -> key-value pairs)\r\n * \r\n * All variables are injected into process.env before service initialization.\r\n */\r\n\r\nimport { existsSync, readFileSync } from 'node:fs';\r\nimport { join } from 'node:path';\r\nimport { createHash } from 'node:crypto';\r\nimport { URL } from 'node:url';\r\nimport type { EnvironmentConfig } from './types.js';\r\nimport { hostToHttpUrl } from './utils/urls.js';\r\n\r\n/**\r\n * Calculates Beamable signature for signed requests\r\n * Signature format: MD5(secret + pid + version + uriPathAndQuery + body) as Base64\r\n */\r\nfunction calculateSignature(\r\n pid: string,\r\n secret: string,\r\n uriPathAndQuery: string,\r\n body: string | null = null,\r\n version: string = '1'\r\n): string {\r\n let dataToSign = `${secret}${pid}${version}${uriPathAndQuery}`;\r\n if (body) {\r\n dataToSign += body;\r\n }\r\n const hash = createHash('md5').update(dataToSign, 'utf8').digest('base64');\r\n return hash;\r\n}\r\n\r\n/**\r\n * Loads developer-defined environment variables from beam.env file\r\n * Supports standard .env format: KEY=value\r\n * \r\n * This is exported for synchronous loading before logger initialization\r\n */\r\nexport function loadDeveloperEnvVarsSync(beamEnvPath?: string): void {\r\n const envVars = loadDeveloperEnvVars(beamEnvPath);\r\n \r\n // Inject immediately into process.env\r\n let injectedCount = 0;\r\n for (const [key, value] of Object.entries(envVars)) {\r\n if (!(key in process.env)) {\r\n process.env[key] = value;\r\n injectedCount++;\r\n }\r\n }\r\n \r\n if (injectedCount > 0) {\r\n console.log(`[EnvLoader] Synchronously injected ${injectedCount} developer-defined variables from beam.env`);\r\n }\r\n}\r\n\r\n/**\r\n * Loads developer-defined environment variables from beam.env file\r\n * Supports standard .env format: KEY=value\r\n */\r\nfunction loadDeveloperEnvVars(beamEnvPath?: string): Record<string, string> {\r\n const envVars: Record<string, string> = {};\r\n \r\n // Try to find beam.env file\r\n const possiblePaths = [\r\n beamEnvPath,\r\n join(process.cwd(), 'beam.env'),\r\n join(process.cwd(), '.beam.env'),\r\n '/beam/service/beam.env', // Container path\r\n '/beam/service/.beam.env',\r\n ].filter((path): path is string => !!path && existsSync(path));\r\n\r\n if (possiblePaths.length === 0) {\r\n return envVars;\r\n }\r\n\r\n const envFilePath = possiblePaths[0];\r\n try {\r\n const content = readFileSync(envFilePath, 'utf-8');\r\n const lines = content.split('\\n');\r\n \r\n for (const line of lines) {\r\n const trimmed = line.trim();\r\n // Skip empty lines and comments\r\n if (!trimmed || trimmed.startsWith('#')) {\r\n continue;\r\n }\r\n \r\n // Parse KEY=value format\r\n const match = trimmed.match(/^([^=#]+)=(.*)$/);\r\n if (match) {\r\n const key = match[1].trim();\r\n let value = match[2].trim();\r\n \r\n // Remove quotes if present\r\n if ((value.startsWith('\"') && value.endsWith('\"')) || \r\n (value.startsWith(\"'\") && value.endsWith(\"'\"))) {\r\n value = value.slice(1, -1);\r\n }\r\n \r\n if (key) {\r\n envVars[key] = value;\r\n }\r\n }\r\n }\r\n \r\n console.log(`[EnvLoader] Loaded ${Object.keys(envVars).length} variables from ${envFilePath}`);\r\n } catch (error) {\r\n console.warn(`[EnvLoader] Failed to load beam.env from ${envFilePath}:`, error instanceof Error ? error.message : String(error));\r\n }\r\n \r\n return envVars;\r\n}\r\n\r\n/**\r\n * Beamable Config API response structure\r\n */\r\ninterface BeamableConfigResponse {\r\n config?: {\r\n [key: string]: {\r\n [environment: string]: string | number | boolean | null;\r\n };\r\n };\r\n [key: string]: unknown;\r\n}\r\n\r\n/**\r\n * Fetches Beamable Config values from the API\r\n * \r\n * The endpoint can be configured via BEAM_CONFIG_API_PATH environment variable.\r\n * Default: /api/basic/realms/config\r\n * \r\n * Note: The exact endpoint format may vary. The API should return a structure like:\r\n * {\r\n * \"config\": {\r\n * \"key1\": {\r\n * \"environment\": \"value1\"\r\n * }\r\n * }\r\n * }\r\n * \r\n * Returns key-value pairs from the current realm's config namespace/environment\r\n */\r\nasync function fetchBeamableConfig(\r\n env: EnvironmentConfig,\r\n timeoutMs: number = 2000\r\n): Promise<Record<string, string>> {\r\n const configVars: Record<string, string> = {};\r\n \r\n // Skip if we don't have required credentials\r\n if (!env.cid || !env.pid) {\r\n console.log('[EnvLoader] Skipping Beamable Config fetch - missing CID or PID');\r\n return configVars;\r\n }\r\n\r\n // Check if we have an access token (preferred) or secret (fallback)\r\n const accessToken = process.env.ACCESS_TOKEN || process.env.BEAMABLE_TOKEN;\r\n const hasSecret = !!env.secret;\r\n \r\n if (!accessToken && !hasSecret) {\r\n console.log('[EnvLoader] Skipping Beamable Config fetch - missing ACCESS_TOKEN/BEAMABLE_TOKEN or SECRET');\r\n return configVars;\r\n }\r\n\r\n try {\r\n const apiUrl = hostToHttpUrl(env.host);\r\n // Allow endpoint to be configured via environment variable (for flexibility)\r\n // Default endpoint matches C# SDK and Portal: /basic/realms/config (not /api/basic/realms/config)\r\n const uriPath = process.env.BEAM_CONFIG_API_PATH || '/basic/realms/config';\r\n const configUrl = new URL(uriPath, apiUrl).toString();\r\n \r\n // Build headers - prefer token-based auth if available, fallback to signature-based\r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n Accept: 'application/json',\r\n 'X-BEAM-SCOPE': `${env.cid}.${env.pid}`,\r\n };\r\n \r\n if (accessToken) {\r\n // Use token-based authentication (matches Portal's $beamable.get())\r\n headers['Authorization'] = `Bearer ${accessToken}`;\r\n } else if (hasSecret && env.secret) {\r\n // Fallback to signature-based authentication\r\n const pathAndQuery = uriPath;\r\n const signature = calculateSignature(env.pid, env.secret, pathAndQuery, null, '1');\r\n headers['X-BEAM-SIGNATURE'] = signature;\r\n }\r\n \r\n console.log(`[EnvLoader] Fetching Beamable Config from ${configUrl}...`);\r\n console.log(`[EnvLoader] Using ${accessToken ? 'token-based' : 'signature-based'} authentication`);\r\n \r\n // Add timeout to fetch to prevent hanging requests\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\r\n \r\n let response: Response;\r\n try {\r\n response = await fetch(configUrl, {\r\n method: 'GET',\r\n headers,\r\n signal: controller.signal,\r\n });\r\n clearTimeout(timeoutId);\r\n } catch (error) {\r\n clearTimeout(timeoutId);\r\n if (error instanceof Error && error.name === 'AbortError') {\r\n console.warn(`[EnvLoader] Beamable Config fetch timed out after ${timeoutMs}ms`);\r\n console.warn(`[EnvLoader] URL: ${configUrl}`);\r\n console.warn(`[EnvLoader] Auth method: ${accessToken ? 'token-based' : 'signature-based'}`);\r\n throw new Error(`Beamable Config fetch timed out after ${timeoutMs}ms`);\r\n }\r\n throw error;\r\n }\r\n\r\n if (!response.ok) {\r\n const errorText = await response.text().catch(() => 'Unknown error');\r\n console.warn(`[EnvLoader] Failed to fetch Beamable Config: ${response.status} ${response.statusText}`);\r\n console.warn(`[EnvLoader] URL: ${configUrl}`);\r\n console.warn(`[EnvLoader] Auth method: ${accessToken ? 'token-based' : 'signature-based'}`);\r\n if (response.status === 401 || response.status === 403) {\r\n console.warn(`[EnvLoader] Authentication failed. This endpoint requires token-based auth (ACCESS_TOKEN or BEAMABLE_TOKEN).`);\r\n if (!accessToken) {\r\n console.warn(`[EnvLoader] Tip: Set ACCESS_TOKEN or BEAMABLE_TOKEN in your environment to enable Beamable Config fetching.`);\r\n }\r\n }\r\n console.warn(`[EnvLoader] Response: ${errorText.substring(0, 500)}`);\r\n return configVars;\r\n }\r\n\r\n const data = (await response.json()) as BeamableConfigResponse;\r\n \r\n // Parse config structure: config[key][environment] = value\r\n // We need to determine the current environment - typically it's the PID or a specific env var\r\n const currentEnvironment = process.env.BEAM_ENVIRONMENT || process.env.ENVIRONMENT || env.pid;\r\n \r\n if (data.config) {\r\n for (const [key, environments] of Object.entries(data.config)) {\r\n // Try to get value for current environment, fallback to first available\r\n let value: string | number | boolean | null | undefined;\r\n \r\n if (environments && typeof environments === 'object') {\r\n value = environments[currentEnvironment];\r\n \r\n // If no value for current environment, try common environment names\r\n if (value === undefined) {\r\n value = environments['production'] || \r\n environments['staging'] || \r\n environments['development'] ||\r\n environments['dev'] ||\r\n Object.values(environments)[0];\r\n }\r\n }\r\n \r\n // Convert value to string and add to env vars\r\n if (value !== undefined && value !== null) {\r\n // Use BEAM_CONFIG_ prefix to avoid conflicts\r\n const envKey = `BEAM_CONFIG_${key.toUpperCase()}`;\r\n configVars[envKey] = String(value);\r\n }\r\n }\r\n \r\n console.log(`[EnvLoader] Loaded ${Object.keys(configVars).length} variables from Beamable Config`);\r\n } else {\r\n console.log('[EnvLoader] Beamable Config response contains no config object');\r\n }\r\n } catch (error) {\r\n console.warn('[EnvLoader] Failed to fetch Beamable Config:', error instanceof Error ? error.message : String(error));\r\n }\r\n \r\n return configVars;\r\n}\r\n\r\n/**\r\n * Loads all environment variables from both sources and injects them into process.env\r\n * \r\n * Priority (highest to lowest):\r\n * 1. Existing process.env values (never overwrite)\r\n * 2. Developer-defined variables (beam.env)\r\n * 3. Beamable Config values (API)\r\n * \r\n * @param env - Environment configuration (CID, PID, HOST, SECRET)\r\n * @param beamEnvPath - Optional path to beam.env file\r\n * @param waitForConfig - If true, waits up to timeoutMs for Beamable Config. If false, returns immediately after developer vars.\r\n * @param timeoutMs - Maximum time to wait for Beamable Config (default: 2000ms)\r\n */\r\nexport async function loadAndInjectEnvironmentVariables(\r\n env: EnvironmentConfig,\r\n beamEnvPath?: string,\r\n waitForConfig: boolean = true,\r\n timeoutMs: number = 2000\r\n): Promise<void> {\r\n console.log('[EnvLoader] Starting environment variable loading...');\r\n \r\n // Load developer-defined variables from beam.env (synchronous, immediate)\r\n const developerVars = loadDeveloperEnvVars(beamEnvPath);\r\n \r\n // Inject developer vars immediately\r\n let injectedCount = 0;\r\n for (const [key, value] of Object.entries(developerVars)) {\r\n if (!(key in process.env)) {\r\n process.env[key] = value;\r\n injectedCount++;\r\n }\r\n }\r\n \r\n if (injectedCount > 0) {\r\n console.log(`[EnvLoader] Injected ${injectedCount} developer-defined variables from beam.env`);\r\n }\r\n \r\n // Fetch Beamable Config values (async, with optional timeout)\r\n if (waitForConfig) {\r\n try {\r\n const configPromise = fetchBeamableConfig(env, timeoutMs);\r\n const timeoutPromise = new Promise<Record<string, string>>((resolve) => {\r\n setTimeout(() => {\r\n console.warn(`[EnvLoader] Beamable Config fetch timed out after ${timeoutMs}ms, continuing without it`);\r\n resolve({});\r\n }, timeoutMs);\r\n });\r\n \r\n const beamableConfigVars = await Promise.race([configPromise, timeoutPromise]);\r\n \r\n // Inject Beamable Config vars\r\n let configInjectedCount = 0;\r\n for (const [key, value] of Object.entries(beamableConfigVars)) {\r\n if (!(key in process.env)) {\r\n process.env[key] = value;\r\n configInjectedCount++;\r\n }\r\n }\r\n \r\n if (configInjectedCount > 0) {\r\n console.log(`[EnvLoader] Injected ${configInjectedCount} variables from Beamable Config`);\r\n }\r\n } catch (error) {\r\n console.warn('[EnvLoader] Error loading Beamable Config (continuing anyway):', error instanceof Error ? error.message : String(error));\r\n }\r\n } else {\r\n // Start fetch in background (non-blocking)\r\n fetchBeamableConfig(env)\r\n .then((beamableConfigVars) => {\r\n let configInjectedCount = 0;\r\n for (const [key, value] of Object.entries(beamableConfigVars)) {\r\n if (!(key in process.env)) {\r\n process.env[key] = value;\r\n configInjectedCount++;\r\n }\r\n }\r\n if (configInjectedCount > 0) {\r\n console.log(`[EnvLoader] Injected ${configInjectedCount} variables from Beamable Config (loaded asynchronously)`);\r\n }\r\n })\r\n .catch((error) => {\r\n console.warn('[EnvLoader] Failed to load Beamable Config in background:', error instanceof Error ? error.message : String(error));\r\n });\r\n }\r\n \r\n console.log('[EnvLoader] Environment variable loading completed');\r\n}\r\n\r\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omen.foundation/node-microservice-runtime",
3
- "version": "0.1.101",
3
+ "version": "0.1.102",
4
4
  "description": "Beamable microservice runtime for Node.js/TypeScript services.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",