@omen.foundation/node-microservice-runtime 0.1.72 → 0.1.75
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/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +41 -3
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/env-loader.d.ts +25 -0
- package/dist/env-loader.d.ts.map +1 -0
- package/dist/env-loader.js +236 -0
- package/dist/env-loader.js.map +1 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +104 -4
- package/dist/logger.js.map +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +20 -0
- package/dist/runtime.js.map +1 -1
- package/package.json +1 -1
- package/scripts/publish-service.mjs +19 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/scaffold.ts"],"names":[],"mappings":"AAAA;;GAEG;AAeH;;GAEG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/scaffold.ts"],"names":[],"mappings":"AAAA;;GAEG;AAeH;;GAEG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAkElE"}
|
|
@@ -57,8 +57,9 @@ export async function handleScaffold(args) {
|
|
|
57
57
|
console.log(`\nNext steps:`);
|
|
58
58
|
console.log(` 1. cd ${serviceName}`);
|
|
59
59
|
console.log(` 2. npm install`);
|
|
60
|
-
console.log(` 3. Copy .env.sample to .env and configure your credentials`);
|
|
61
|
-
console.log(` 4.
|
|
60
|
+
console.log(` 3. Copy .env.sample to .env and configure your Beamable credentials`);
|
|
61
|
+
console.log(` 4. (Optional) Copy beam.env.example to beam.env for custom environment variables`);
|
|
62
|
+
console.log(` 5. npm run dev`);
|
|
62
63
|
}
|
|
63
64
|
/**
|
|
64
65
|
* Generates the complete project structure.
|
|
@@ -73,6 +74,7 @@ async function generateProjectStructure(targetDir, options) {
|
|
|
73
74
|
await fs.writeFile(path.join(targetDir, 'package.json'), generatePackageJson(options));
|
|
74
75
|
await fs.writeFile(path.join(targetDir, 'tsconfig.json'), generateTsConfig());
|
|
75
76
|
await fs.writeFile(path.join(targetDir, '.env.sample'), generateEnvSample());
|
|
77
|
+
await fs.writeFile(path.join(targetDir, 'beam.env.example'), generateBeamEnvExample());
|
|
76
78
|
await fs.writeFile(path.join(targetDir, '.gitignore'), generateGitIgnore());
|
|
77
79
|
await fs.writeFile(path.join(targetDir, 'src', 'main.ts'), generateMainTs(options));
|
|
78
80
|
await fs.writeFile(path.join(targetDir, 'src', `${toPascalCase(options.serviceName)}Service.ts`), generateServiceTs(options));
|
|
@@ -134,7 +136,11 @@ function generateTsConfig() {
|
|
|
134
136
|
* Generates .env.sample content.
|
|
135
137
|
*/
|
|
136
138
|
function generateEnvSample() {
|
|
137
|
-
return
|
|
139
|
+
return `# Beamable Authentication & Configuration
|
|
140
|
+
# This file is for Beamable-specific credentials (CID, PID, SECRET, etc.)
|
|
141
|
+
# For custom environment variables, use beam.env instead (see beam.env.example)
|
|
142
|
+
|
|
143
|
+
CID=
|
|
138
144
|
PID=
|
|
139
145
|
HOST=wss://api.beamable.com/socket
|
|
140
146
|
SECRET=
|
|
@@ -145,6 +151,35 @@ BEAMABLE_API_HOST=https://api.beamable.com
|
|
|
145
151
|
BEAMABLE_GAME_PID=
|
|
146
152
|
`;
|
|
147
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Generates beam.env.example content.
|
|
156
|
+
*/
|
|
157
|
+
function generateBeamEnvExample() {
|
|
158
|
+
return `# Beamable Microservice Environment Variables
|
|
159
|
+
#
|
|
160
|
+
# This file defines developer-specific environment variables that will be
|
|
161
|
+
# loaded into the container at runtime.
|
|
162
|
+
#
|
|
163
|
+
# These variables take priority over Beamable Config values but will NOT
|
|
164
|
+
# overwrite existing process.env values.
|
|
165
|
+
#
|
|
166
|
+
# Format: KEY=value (one per line)
|
|
167
|
+
# Comments start with #
|
|
168
|
+
# Empty lines are ignored
|
|
169
|
+
|
|
170
|
+
# Example: BetterStack Logging Configuration
|
|
171
|
+
# BEAM_BETTERSTACK_ENABLED=true
|
|
172
|
+
# BEAM_BETTERSTACK_TOKEN=your-token-here
|
|
173
|
+
|
|
174
|
+
# Example: Custom configuration
|
|
175
|
+
# MY_API_KEY=your-api-key-here
|
|
176
|
+
# MY_FEATURE_FLAG=true
|
|
177
|
+
|
|
178
|
+
# Example: Service-specific settings
|
|
179
|
+
# LOG_LEVEL=debug
|
|
180
|
+
# MAX_CONNECTIONS=100
|
|
181
|
+
`;
|
|
182
|
+
}
|
|
148
183
|
/**
|
|
149
184
|
* Generates .gitignore content.
|
|
150
185
|
*/
|
|
@@ -152,6 +187,9 @@ function generateGitIgnore() {
|
|
|
152
187
|
return `node_modules/
|
|
153
188
|
dist/
|
|
154
189
|
.env
|
|
190
|
+
.env.local
|
|
191
|
+
beam.env
|
|
192
|
+
.beam.env
|
|
155
193
|
*.log
|
|
156
194
|
.DS_Store
|
|
157
195
|
`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../../src/cli/commands/scaffold.ts"],"names":[],"mappings":"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","sourcesContent":["/**\r\n * Command handler for scaffolding new microservice projects.\r\n */\r\n\r\nimport fs from 'node:fs/promises';\r\nimport path from 'node:path';\r\nimport { toPascalCase, toKebabCase, validateServiceName } from '../utils/name-utils.js';\r\nimport { fetchLatestRuntimeVersion } from '../utils/version-utils.js';\r\nimport { promptInput } from '../utils/prompt-utils.js';\r\n\r\ninterface ScaffoldOptions {\r\n serviceName: string;\r\n displayName?: string;\r\n beamoId?: string;\r\n runtimeVersion: string;\r\n}\r\n\r\n/**\r\n * Handles the scaffold command.\r\n */\r\nexport async function handleScaffold(args: string[]): Promise<void> {\r\n let serviceName = args[0];\r\n\r\n // Interactive prompt for service name if not provided\r\n if (!serviceName) {\r\n serviceName = await promptInput('Enter microservice name (alphanumeric and underscores only)');\r\n }\r\n\r\n // Validate service name\r\n if (!serviceName || !validateServiceName(serviceName)) {\r\n throw new Error(\r\n `Invalid microservice name \"${serviceName}\". Only alphanumeric and underscore characters are allowed.`,\r\n );\r\n }\r\n\r\n // Fetch latest runtime version\r\n console.log('Fetching latest runtime version...');\r\n const runtimeVersion = await fetchLatestRuntimeVersion();\r\n console.log(`Using @omen.foundation/node-microservice-runtime version ${runtimeVersion}`);\r\n\r\n // Prompt for optional values\r\n const displayName = await promptInput('Enter display name for the service (optional)', serviceName);\r\n const beamoId = await promptInput('Enter Beamable Beamo ID (optional, defaults to service name)', serviceName);\r\n\r\n // Validate beamoId\r\n if (beamoId && !validateServiceName(beamoId)) {\r\n throw new Error(\r\n `Invalid Beamo ID \"${beamoId}\". Only alphanumeric and underscore characters are allowed.`,\r\n );\r\n }\r\n\r\n const options: ScaffoldOptions = {\r\n serviceName,\r\n displayName: displayName || serviceName,\r\n beamoId: beamoId || serviceName,\r\n runtimeVersion,\r\n };\r\n\r\n // Check if directory already exists\r\n const targetDir = path.resolve(process.cwd(), serviceName);\r\n try {\r\n await fs.access(targetDir);\r\n const overwrite = await promptInput(\r\n `Directory \"${serviceName}\" already exists. Overwrite? (y/n)`,\r\n 'n',\r\n );\r\n if (overwrite.toLowerCase() !== 'y' && overwrite.toLowerCase() !== 'yes') {\r\n console.log('Scaffold cancelled.');\r\n return;\r\n }\r\n // Remove existing directory\r\n await fs.rm(targetDir, { recursive: true, force: true });\r\n } catch {\r\n // Directory doesn't exist, that's fine\r\n }\r\n\r\n // Generate project structure\r\n await generateProjectStructure(targetDir, options);\r\n\r\n console.log(`\\n✅ Successfully scaffolded microservice \"${serviceName}\"!`);\r\n console.log(`\\nNext steps:`);\r\n console.log(` 1. cd ${serviceName}`);\r\n console.log(` 2. npm install`);\r\n console.log(` 3. Copy .env.sample to .env and configure your credentials`);\r\n console.log(` 4. npm run dev`);\r\n}\r\n\r\n/**\r\n * Generates the complete project structure.\r\n */\r\nasync function generateProjectStructure(targetDir: string, options: ScaffoldOptions): Promise<void> {\r\n // Create directories\r\n await fs.mkdir(targetDir, { recursive: true });\r\n await fs.mkdir(path.join(targetDir, 'src'), { recursive: true });\r\n await fs.mkdir(path.join(targetDir, 'src', 'services'), { recursive: true });\r\n await fs.mkdir(path.join(targetDir, 'dist'), { recursive: true });\r\n\r\n // Generate files\r\n await fs.writeFile(path.join(targetDir, 'package.json'), generatePackageJson(options));\r\n await fs.writeFile(path.join(targetDir, 'tsconfig.json'), generateTsConfig());\r\n await fs.writeFile(path.join(targetDir, '.env.sample'), generateEnvSample());\r\n await fs.writeFile(path.join(targetDir, '.gitignore'), generateGitIgnore());\r\n await fs.writeFile(path.join(targetDir, 'src', 'main.ts'), generateMainTs(options));\r\n await fs.writeFile(\r\n path.join(targetDir, 'src', `${toPascalCase(options.serviceName)}Service.ts`),\r\n generateServiceTs(options),\r\n );\r\n await fs.writeFile(\r\n path.join(targetDir, 'src', 'services', 'example-domain-service.ts'),\r\n generateDomainServiceTs(),\r\n );\r\n}\r\n\r\n/**\r\n * Generates package.json content.\r\n */\r\nfunction generatePackageJson(options: ScaffoldOptions): string {\r\n const serviceNameKebab = toKebabCase(options.serviceName);\r\n const packageName = `@beamable/${serviceNameKebab}-node-microservice`;\r\n\r\n return JSON.stringify(\r\n {\r\n name: packageName,\r\n version: '0.1.0',\r\n private: true,\r\n type: 'module',\r\n main: 'dist/main.js',\r\n types: 'dist/main.d.ts',\r\n scripts: {\r\n dev: 'tsx --env-file .env src/main.ts',\r\n build: 'tsc -p tsconfig.json',\r\n validate: 'npx beamo-node validate --env-file .env',\r\n publish: 'npx beamo-node publish --env-file .env',\r\n },\r\n dependencies: {\r\n '@omen.foundation/node-microservice-runtime': `^${options.runtimeVersion}`,\r\n dotenv: '^16.4.7',\r\n mongodb: '^6.10.0',\r\n },\r\n devDependencies: {\r\n '@types/node': '^20.11.30',\r\n 'pino-pretty': '^13.0.0',\r\n tsx: '^4.19.2',\r\n typescript: '^5.4.5',\r\n },\r\n beamable: {\r\n beamoId: options.beamoId,\r\n projectType: 'service',\r\n },\r\n },\r\n null,\r\n 2,\r\n );\r\n}\r\n\r\n/**\r\n * Generates tsconfig.json content.\r\n */\r\nfunction generateTsConfig(): string {\r\n return JSON.stringify(\r\n {\r\n extends: '../Microservice/tsconfig.base.json',\r\n compilerOptions: {\r\n outDir: 'dist',\r\n rootDir: 'src',\r\n declaration: true,\r\n declarationMap: true,\r\n },\r\n include: ['src/**/*.ts'],\r\n exclude: ['dist', 'node_modules'],\r\n },\r\n null,\r\n 2,\r\n );\r\n}\r\n\r\n/**\r\n * Generates .env.sample content.\r\n */\r\nfunction generateEnvSample(): string {\r\n return `CID=\r\nPID=\r\nHOST=wss://api.beamable.com/socket\r\nSECRET=\r\nNAME_PREFIX=\r\nLOG_LEVEL=info\r\nBEAMABLE_TOKEN=\r\nBEAMABLE_API_HOST=https://api.beamable.com\r\nBEAMABLE_GAME_PID=\r\n`;\r\n}\r\n\r\n/**\r\n * Generates .gitignore content.\r\n */\r\nfunction generateGitIgnore(): string {\r\n return `node_modules/\r\ndist/\r\n.env\r\n*.log\r\n.DS_Store\r\n`;\r\n}\r\n\r\n/**\r\n * Generates main.ts content.\r\n */\r\nfunction generateMainTs(options: ScaffoldOptions): string {\r\n const serviceNamePascal = toPascalCase(options.serviceName);\r\n return `import 'dotenv/config';\r\nimport './${serviceNamePascal}Service.js';\r\nimport { runMicroservice } from '@omen.foundation/node-microservice-runtime';\r\n\r\nvoid runMicroservice();\r\n`;\r\n}\r\n\r\n/**\r\n * Generates the main service class file.\r\n */\r\nfunction generateServiceTs(options: ScaffoldOptions): string {\r\n const serviceNamePascal = toPascalCase(options.serviceName);\r\n return `import {\r\n Microservice,\r\n Callable,\r\n ClientCallable,\r\n ServerCallable,\r\n SwaggerCategory,\r\n ConfigureServices,\r\n type DependencyBuilder,\r\n type RequestContext,\r\n} from '@omen.foundation/node-microservice-runtime';\r\nimport { ExampleDomainService } from './services/example-domain-service.js';\r\n\r\n@Microservice('${options.serviceName}')\r\nexport class ${serviceNamePascal}Service {\r\n @ConfigureServices\r\n static register(builder: DependencyBuilder): void {\r\n builder.addSingletonClass(ExampleDomainService);\r\n }\r\n\r\n /**\r\n * Example of a public Callable endpoint.\r\n * No authentication required.\r\n */\r\n @Callable({ route: 'isTwo' })\r\n @SwaggerCategory('Examples')\r\n async isTwo(_ctx: RequestContext): Promise<boolean> {\r\n return 1 + 1 === 2;\r\n }\r\n\r\n /**\r\n * Example of a ClientCallable endpoint.\r\n * Requires user authentication (client scope).\r\n */\r\n @ClientCallable({ route: 'isThree' })\r\n @SwaggerCategory('Examples')\r\n async isThree(_ctx: RequestContext): Promise<boolean> {\r\n return 1 + 2 === 3;\r\n }\r\n\r\n /**\r\n * Example of a ServerCallable endpoint.\r\n * Requires server authentication (server scope).\r\n */\r\n @ServerCallable({ route: 'isFour' })\r\n @SwaggerCategory('Examples')\r\n async isFour(_ctx: RequestContext): Promise<boolean> {\r\n return 2 + 2 === 4;\r\n }\r\n}\r\n`;\r\n}\r\n\r\n/**\r\n * Generates example domain service content.\r\n */\r\nfunction generateDomainServiceTs(): string {\r\n return `import type { RequestContext } from '@omen.foundation/node-microservice-runtime';\r\n\r\nexport class ExampleDomainService {\r\n greet(userId: number): string {\r\n return \\`Hello from Node microservices, user \\${userId}!\\`;\r\n }\r\n\r\n async getServiceInfo(_ctx: RequestContext): Promise<{ service: string; version: string }> {\r\n return {\r\n service: 'ExampleDomainService',\r\n version: '1.0.0',\r\n };\r\n }\r\n}\r\n`;\r\n}\r\n\r\n"]}
|
|
1
|
+
{"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../../src/cli/commands/scaffold.ts"],"names":[],"mappings":"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","sourcesContent":["/**\r\n * Command handler for scaffolding new microservice projects.\r\n */\r\n\r\nimport fs from 'node:fs/promises';\r\nimport path from 'node:path';\r\nimport { toPascalCase, toKebabCase, validateServiceName } from '../utils/name-utils.js';\r\nimport { fetchLatestRuntimeVersion } from '../utils/version-utils.js';\r\nimport { promptInput } from '../utils/prompt-utils.js';\r\n\r\ninterface ScaffoldOptions {\r\n serviceName: string;\r\n displayName?: string;\r\n beamoId?: string;\r\n runtimeVersion: string;\r\n}\r\n\r\n/**\r\n * Handles the scaffold command.\r\n */\r\nexport async function handleScaffold(args: string[]): Promise<void> {\r\n let serviceName = args[0];\r\n\r\n // Interactive prompt for service name if not provided\r\n if (!serviceName) {\r\n serviceName = await promptInput('Enter microservice name (alphanumeric and underscores only)');\r\n }\r\n\r\n // Validate service name\r\n if (!serviceName || !validateServiceName(serviceName)) {\r\n throw new Error(\r\n `Invalid microservice name \"${serviceName}\". Only alphanumeric and underscore characters are allowed.`,\r\n );\r\n }\r\n\r\n // Fetch latest runtime version\r\n console.log('Fetching latest runtime version...');\r\n const runtimeVersion = await fetchLatestRuntimeVersion();\r\n console.log(`Using @omen.foundation/node-microservice-runtime version ${runtimeVersion}`);\r\n\r\n // Prompt for optional values\r\n const displayName = await promptInput('Enter display name for the service (optional)', serviceName);\r\n const beamoId = await promptInput('Enter Beamable Beamo ID (optional, defaults to service name)', serviceName);\r\n\r\n // Validate beamoId\r\n if (beamoId && !validateServiceName(beamoId)) {\r\n throw new Error(\r\n `Invalid Beamo ID \"${beamoId}\". Only alphanumeric and underscore characters are allowed.`,\r\n );\r\n }\r\n\r\n const options: ScaffoldOptions = {\r\n serviceName,\r\n displayName: displayName || serviceName,\r\n beamoId: beamoId || serviceName,\r\n runtimeVersion,\r\n };\r\n\r\n // Check if directory already exists\r\n const targetDir = path.resolve(process.cwd(), serviceName);\r\n try {\r\n await fs.access(targetDir);\r\n const overwrite = await promptInput(\r\n `Directory \"${serviceName}\" already exists. Overwrite? (y/n)`,\r\n 'n',\r\n );\r\n if (overwrite.toLowerCase() !== 'y' && overwrite.toLowerCase() !== 'yes') {\r\n console.log('Scaffold cancelled.');\r\n return;\r\n }\r\n // Remove existing directory\r\n await fs.rm(targetDir, { recursive: true, force: true });\r\n } catch {\r\n // Directory doesn't exist, that's fine\r\n }\r\n\r\n // Generate project structure\r\n await generateProjectStructure(targetDir, options);\r\n\r\n console.log(`\\n✅ Successfully scaffolded microservice \"${serviceName}\"!`);\r\n console.log(`\\nNext steps:`);\r\n console.log(` 1. cd ${serviceName}`);\r\n console.log(` 2. npm install`);\r\n console.log(` 3. Copy .env.sample to .env and configure your Beamable credentials`);\r\n console.log(` 4. (Optional) Copy beam.env.example to beam.env for custom environment variables`);\r\n console.log(` 5. npm run dev`);\r\n}\r\n\r\n/**\r\n * Generates the complete project structure.\r\n */\r\nasync function generateProjectStructure(targetDir: string, options: ScaffoldOptions): Promise<void> {\r\n // Create directories\r\n await fs.mkdir(targetDir, { recursive: true });\r\n await fs.mkdir(path.join(targetDir, 'src'), { recursive: true });\r\n await fs.mkdir(path.join(targetDir, 'src', 'services'), { recursive: true });\r\n await fs.mkdir(path.join(targetDir, 'dist'), { recursive: true });\r\n\r\n // Generate files\r\n await fs.writeFile(path.join(targetDir, 'package.json'), generatePackageJson(options));\r\n await fs.writeFile(path.join(targetDir, 'tsconfig.json'), generateTsConfig());\r\n await fs.writeFile(path.join(targetDir, '.env.sample'), generateEnvSample());\r\n await fs.writeFile(path.join(targetDir, 'beam.env.example'), generateBeamEnvExample());\r\n await fs.writeFile(path.join(targetDir, '.gitignore'), generateGitIgnore());\r\n await fs.writeFile(path.join(targetDir, 'src', 'main.ts'), generateMainTs(options));\r\n await fs.writeFile(\r\n path.join(targetDir, 'src', `${toPascalCase(options.serviceName)}Service.ts`),\r\n generateServiceTs(options),\r\n );\r\n await fs.writeFile(\r\n path.join(targetDir, 'src', 'services', 'example-domain-service.ts'),\r\n generateDomainServiceTs(),\r\n );\r\n}\r\n\r\n/**\r\n * Generates package.json content.\r\n */\r\nfunction generatePackageJson(options: ScaffoldOptions): string {\r\n const serviceNameKebab = toKebabCase(options.serviceName);\r\n const packageName = `@beamable/${serviceNameKebab}-node-microservice`;\r\n\r\n return JSON.stringify(\r\n {\r\n name: packageName,\r\n version: '0.1.0',\r\n private: true,\r\n type: 'module',\r\n main: 'dist/main.js',\r\n types: 'dist/main.d.ts',\r\n scripts: {\r\n dev: 'tsx --env-file .env src/main.ts',\r\n build: 'tsc -p tsconfig.json',\r\n validate: 'npx beamo-node validate --env-file .env',\r\n publish: 'npx beamo-node publish --env-file .env',\r\n },\r\n dependencies: {\r\n '@omen.foundation/node-microservice-runtime': `^${options.runtimeVersion}`,\r\n dotenv: '^16.4.7',\r\n mongodb: '^6.10.0',\r\n },\r\n devDependencies: {\r\n '@types/node': '^20.11.30',\r\n 'pino-pretty': '^13.0.0',\r\n tsx: '^4.19.2',\r\n typescript: '^5.4.5',\r\n },\r\n beamable: {\r\n beamoId: options.beamoId,\r\n projectType: 'service',\r\n },\r\n },\r\n null,\r\n 2,\r\n );\r\n}\r\n\r\n/**\r\n * Generates tsconfig.json content.\r\n */\r\nfunction generateTsConfig(): string {\r\n return JSON.stringify(\r\n {\r\n extends: '../Microservice/tsconfig.base.json',\r\n compilerOptions: {\r\n outDir: 'dist',\r\n rootDir: 'src',\r\n declaration: true,\r\n declarationMap: true,\r\n },\r\n include: ['src/**/*.ts'],\r\n exclude: ['dist', 'node_modules'],\r\n },\r\n null,\r\n 2,\r\n );\r\n}\r\n\r\n/**\r\n * Generates .env.sample content.\r\n */\r\nfunction generateEnvSample(): string {\r\n return `# Beamable Authentication & Configuration\r\n# This file is for Beamable-specific credentials (CID, PID, SECRET, etc.)\r\n# For custom environment variables, use beam.env instead (see beam.env.example)\r\n\r\nCID=\r\nPID=\r\nHOST=wss://api.beamable.com/socket\r\nSECRET=\r\nNAME_PREFIX=\r\nLOG_LEVEL=info\r\nBEAMABLE_TOKEN=\r\nBEAMABLE_API_HOST=https://api.beamable.com\r\nBEAMABLE_GAME_PID=\r\n`;\r\n}\r\n\r\n/**\r\n * Generates beam.env.example content.\r\n */\r\nfunction generateBeamEnvExample(): string {\r\n return `# Beamable Microservice Environment Variables\r\n# \r\n# This file defines developer-specific environment variables that will be\r\n# loaded into the container at runtime.\r\n#\r\n# These variables take priority over Beamable Config values but will NOT\r\n# overwrite existing process.env values.\r\n#\r\n# Format: KEY=value (one per line)\r\n# Comments start with #\r\n# Empty lines are ignored\r\n\r\n# Example: BetterStack Logging Configuration\r\n# BEAM_BETTERSTACK_ENABLED=true\r\n# BEAM_BETTERSTACK_TOKEN=your-token-here\r\n\r\n# Example: Custom configuration\r\n# MY_API_KEY=your-api-key-here\r\n# MY_FEATURE_FLAG=true\r\n\r\n# Example: Service-specific settings\r\n# LOG_LEVEL=debug\r\n# MAX_CONNECTIONS=100\r\n`;\r\n}\r\n\r\n/**\r\n * Generates .gitignore content.\r\n */\r\nfunction generateGitIgnore(): string {\r\n return `node_modules/\r\ndist/\r\n.env\r\n.env.local\r\nbeam.env\r\n.beam.env\r\n*.log\r\n.DS_Store\r\n`;\r\n}\r\n\r\n/**\r\n * Generates main.ts content.\r\n */\r\nfunction generateMainTs(options: ScaffoldOptions): string {\r\n const serviceNamePascal = toPascalCase(options.serviceName);\r\n return `import 'dotenv/config';\r\nimport './${serviceNamePascal}Service.js';\r\nimport { runMicroservice } from '@omen.foundation/node-microservice-runtime';\r\n\r\nvoid runMicroservice();\r\n`;\r\n}\r\n\r\n/**\r\n * Generates the main service class file.\r\n */\r\nfunction generateServiceTs(options: ScaffoldOptions): string {\r\n const serviceNamePascal = toPascalCase(options.serviceName);\r\n return `import {\r\n Microservice,\r\n Callable,\r\n ClientCallable,\r\n ServerCallable,\r\n SwaggerCategory,\r\n ConfigureServices,\r\n type DependencyBuilder,\r\n type RequestContext,\r\n} from '@omen.foundation/node-microservice-runtime';\r\nimport { ExampleDomainService } from './services/example-domain-service.js';\r\n\r\n@Microservice('${options.serviceName}')\r\nexport class ${serviceNamePascal}Service {\r\n @ConfigureServices\r\n static register(builder: DependencyBuilder): void {\r\n builder.addSingletonClass(ExampleDomainService);\r\n }\r\n\r\n /**\r\n * Example of a public Callable endpoint.\r\n * No authentication required.\r\n */\r\n @Callable({ route: 'isTwo' })\r\n @SwaggerCategory('Examples')\r\n async isTwo(_ctx: RequestContext): Promise<boolean> {\r\n return 1 + 1 === 2;\r\n }\r\n\r\n /**\r\n * Example of a ClientCallable endpoint.\r\n * Requires user authentication (client scope).\r\n */\r\n @ClientCallable({ route: 'isThree' })\r\n @SwaggerCategory('Examples')\r\n async isThree(_ctx: RequestContext): Promise<boolean> {\r\n return 1 + 2 === 3;\r\n }\r\n\r\n /**\r\n * Example of a ServerCallable endpoint.\r\n * Requires server authentication (server scope).\r\n */\r\n @ServerCallable({ route: 'isFour' })\r\n @SwaggerCategory('Examples')\r\n async isFour(_ctx: RequestContext): Promise<boolean> {\r\n return 2 + 2 === 4;\r\n }\r\n}\r\n`;\r\n}\r\n\r\n/**\r\n * Generates example domain service content.\r\n */\r\nfunction generateDomainServiceTs(): string {\r\n return `import type { RequestContext } from '@omen.foundation/node-microservice-runtime';\r\n\r\nexport class ExampleDomainService {\r\n greet(userId: number): string {\r\n return \\`Hello from Node microservices, user \\${userId}!\\`;\r\n }\r\n\r\n async getServiceInfo(_ctx: RequestContext): Promise<{ service: string; version: string }> {\r\n return {\r\n service: 'ExampleDomainService',\r\n version: '1.0.0',\r\n };\r\n }\r\n}\r\n`;\r\n}\r\n\r\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment Variable Loader
|
|
3
|
+
*
|
|
4
|
+
* This module handles loading environment variables from two sources:
|
|
5
|
+
* 1. Developer-defined variables (from beam.env file)
|
|
6
|
+
* 2. Beamable Config values (from API: namespace -> environment -> key-value pairs)
|
|
7
|
+
*
|
|
8
|
+
* All variables are injected into process.env before service initialization.
|
|
9
|
+
*/
|
|
10
|
+
import type { EnvironmentConfig } from './types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Loads all environment variables from both sources and injects them into process.env
|
|
13
|
+
*
|
|
14
|
+
* Priority (highest to lowest):
|
|
15
|
+
* 1. Existing process.env values (never overwrite)
|
|
16
|
+
* 2. Developer-defined variables (beam.env)
|
|
17
|
+
* 3. Beamable Config values (API)
|
|
18
|
+
*
|
|
19
|
+
* @param env - Environment configuration (CID, PID, HOST, SECRET)
|
|
20
|
+
* @param beamEnvPath - Optional path to beam.env file
|
|
21
|
+
* @param waitForConfig - If true, waits up to timeoutMs for Beamable Config. If false, returns immediately after developer vars.
|
|
22
|
+
* @param timeoutMs - Maximum time to wait for Beamable Config (default: 2000ms)
|
|
23
|
+
*/
|
|
24
|
+
export declare function loadAndInjectEnvironmentVariables(env: EnvironmentConfig, beamEnvPath?: string, waitForConfig?: boolean, timeoutMs?: number): Promise<void>;
|
|
25
|
+
//# sourceMappingURL=env-loader.d.ts.map
|
|
@@ -0,0 +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;AA+LpD;;;;;;;;;;;;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"}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment Variable Loader
|
|
3
|
+
*
|
|
4
|
+
* This module handles loading environment variables from two sources:
|
|
5
|
+
* 1. Developer-defined variables (from beam.env file)
|
|
6
|
+
* 2. Beamable Config values (from API: namespace -> environment -> key-value pairs)
|
|
7
|
+
*
|
|
8
|
+
* All variables are injected into process.env before service initialization.
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { createHash } from 'node:crypto';
|
|
13
|
+
import { URL } from 'node:url';
|
|
14
|
+
import { hostToHttpUrl } from './utils/urls.js';
|
|
15
|
+
/**
|
|
16
|
+
* Calculates Beamable signature for signed requests
|
|
17
|
+
* Signature format: MD5(secret + pid + version + uriPathAndQuery + body) as Base64
|
|
18
|
+
*/
|
|
19
|
+
function calculateSignature(pid, secret, uriPathAndQuery, body = null, version = '1') {
|
|
20
|
+
let dataToSign = `${secret}${pid}${version}${uriPathAndQuery}`;
|
|
21
|
+
if (body) {
|
|
22
|
+
dataToSign += body;
|
|
23
|
+
}
|
|
24
|
+
const hash = createHash('md5').update(dataToSign, 'utf8').digest('base64');
|
|
25
|
+
return hash;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Loads developer-defined environment variables from beam.env file
|
|
29
|
+
* Supports standard .env format: KEY=value
|
|
30
|
+
*/
|
|
31
|
+
function loadDeveloperEnvVars(beamEnvPath) {
|
|
32
|
+
const envVars = {};
|
|
33
|
+
// Try to find beam.env file
|
|
34
|
+
const possiblePaths = [
|
|
35
|
+
beamEnvPath,
|
|
36
|
+
join(process.cwd(), 'beam.env'),
|
|
37
|
+
join(process.cwd(), '.beam.env'),
|
|
38
|
+
'/beam/service/beam.env', // Container path
|
|
39
|
+
'/beam/service/.beam.env',
|
|
40
|
+
].filter((path) => !!path && existsSync(path));
|
|
41
|
+
if (possiblePaths.length === 0) {
|
|
42
|
+
return envVars;
|
|
43
|
+
}
|
|
44
|
+
const envFilePath = possiblePaths[0];
|
|
45
|
+
try {
|
|
46
|
+
const content = readFileSync(envFilePath, 'utf-8');
|
|
47
|
+
const lines = content.split('\n');
|
|
48
|
+
for (const line of lines) {
|
|
49
|
+
const trimmed = line.trim();
|
|
50
|
+
// Skip empty lines and comments
|
|
51
|
+
if (!trimmed || trimmed.startsWith('#')) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
// Parse KEY=value format
|
|
55
|
+
const match = trimmed.match(/^([^=#]+)=(.*)$/);
|
|
56
|
+
if (match) {
|
|
57
|
+
const key = match[1].trim();
|
|
58
|
+
let value = match[2].trim();
|
|
59
|
+
// Remove quotes if present
|
|
60
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
61
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
62
|
+
value = value.slice(1, -1);
|
|
63
|
+
}
|
|
64
|
+
if (key) {
|
|
65
|
+
envVars[key] = value;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
console.log(`[EnvLoader] Loaded ${Object.keys(envVars).length} variables from ${envFilePath}`);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
console.warn(`[EnvLoader] Failed to load beam.env from ${envFilePath}:`, error instanceof Error ? error.message : String(error));
|
|
73
|
+
}
|
|
74
|
+
return envVars;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Fetches Beamable Config values from the API
|
|
78
|
+
*
|
|
79
|
+
* The endpoint can be configured via BEAM_CONFIG_API_PATH environment variable.
|
|
80
|
+
* Default: /api/basic/realms/config
|
|
81
|
+
*
|
|
82
|
+
* Note: The exact endpoint format may vary. The API should return a structure like:
|
|
83
|
+
* {
|
|
84
|
+
* "config": {
|
|
85
|
+
* "key1": {
|
|
86
|
+
* "environment": "value1"
|
|
87
|
+
* }
|
|
88
|
+
* }
|
|
89
|
+
* }
|
|
90
|
+
*
|
|
91
|
+
* Returns key-value pairs from the current realm's config namespace/environment
|
|
92
|
+
*/
|
|
93
|
+
async function fetchBeamableConfig(env) {
|
|
94
|
+
const configVars = {};
|
|
95
|
+
// Skip if we don't have required credentials
|
|
96
|
+
if (!env.secret || !env.cid || !env.pid) {
|
|
97
|
+
console.log('[EnvLoader] Skipping Beamable Config fetch - missing SECRET, CID, or PID');
|
|
98
|
+
return configVars;
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
const apiUrl = hostToHttpUrl(env.host);
|
|
102
|
+
// Allow endpoint to be configured via environment variable (for flexibility)
|
|
103
|
+
const uriPath = process.env.BEAM_CONFIG_API_PATH || '/api/basic/realms/config';
|
|
104
|
+
const configUrl = new URL(uriPath, apiUrl).toString();
|
|
105
|
+
// Calculate signature for signed request
|
|
106
|
+
const pathAndQuery = uriPath;
|
|
107
|
+
const signature = calculateSignature(env.pid, env.secret, pathAndQuery, null, '1');
|
|
108
|
+
console.log(`[EnvLoader] Fetching Beamable Config from ${configUrl}...`);
|
|
109
|
+
const response = await fetch(configUrl, {
|
|
110
|
+
method: 'GET',
|
|
111
|
+
headers: {
|
|
112
|
+
'Content-Type': 'application/json',
|
|
113
|
+
Accept: 'application/json',
|
|
114
|
+
'X-BEAM-SCOPE': `${env.cid}.${env.pid}`,
|
|
115
|
+
'X-BEAM-SIGNATURE': signature,
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
if (!response.ok) {
|
|
119
|
+
const errorText = await response.text().catch(() => 'Unknown error');
|
|
120
|
+
console.warn(`[EnvLoader] Failed to fetch Beamable Config: ${response.status} ${response.statusText} - ${errorText.substring(0, 200)}`);
|
|
121
|
+
return configVars;
|
|
122
|
+
}
|
|
123
|
+
const data = (await response.json());
|
|
124
|
+
// Parse config structure: config[key][environment] = value
|
|
125
|
+
// We need to determine the current environment - typically it's the PID or a specific env var
|
|
126
|
+
const currentEnvironment = process.env.BEAM_ENVIRONMENT || process.env.ENVIRONMENT || env.pid;
|
|
127
|
+
if (data.config) {
|
|
128
|
+
for (const [key, environments] of Object.entries(data.config)) {
|
|
129
|
+
// Try to get value for current environment, fallback to first available
|
|
130
|
+
let value;
|
|
131
|
+
if (environments && typeof environments === 'object') {
|
|
132
|
+
value = environments[currentEnvironment];
|
|
133
|
+
// If no value for current environment, try common environment names
|
|
134
|
+
if (value === undefined) {
|
|
135
|
+
value = environments['production'] ||
|
|
136
|
+
environments['staging'] ||
|
|
137
|
+
environments['development'] ||
|
|
138
|
+
environments['dev'] ||
|
|
139
|
+
Object.values(environments)[0];
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Convert value to string and add to env vars
|
|
143
|
+
if (value !== undefined && value !== null) {
|
|
144
|
+
// Use BEAM_CONFIG_ prefix to avoid conflicts
|
|
145
|
+
const envKey = `BEAM_CONFIG_${key.toUpperCase()}`;
|
|
146
|
+
configVars[envKey] = String(value);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
console.log(`[EnvLoader] Loaded ${Object.keys(configVars).length} variables from Beamable Config`);
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
console.log('[EnvLoader] Beamable Config response contains no config object');
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
console.warn('[EnvLoader] Failed to fetch Beamable Config:', error instanceof Error ? error.message : String(error));
|
|
157
|
+
}
|
|
158
|
+
return configVars;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Loads all environment variables from both sources and injects them into process.env
|
|
162
|
+
*
|
|
163
|
+
* Priority (highest to lowest):
|
|
164
|
+
* 1. Existing process.env values (never overwrite)
|
|
165
|
+
* 2. Developer-defined variables (beam.env)
|
|
166
|
+
* 3. Beamable Config values (API)
|
|
167
|
+
*
|
|
168
|
+
* @param env - Environment configuration (CID, PID, HOST, SECRET)
|
|
169
|
+
* @param beamEnvPath - Optional path to beam.env file
|
|
170
|
+
* @param waitForConfig - If true, waits up to timeoutMs for Beamable Config. If false, returns immediately after developer vars.
|
|
171
|
+
* @param timeoutMs - Maximum time to wait for Beamable Config (default: 2000ms)
|
|
172
|
+
*/
|
|
173
|
+
export async function loadAndInjectEnvironmentVariables(env, beamEnvPath, waitForConfig = true, timeoutMs = 2000) {
|
|
174
|
+
console.log('[EnvLoader] Starting environment variable loading...');
|
|
175
|
+
// Load developer-defined variables from beam.env (synchronous, immediate)
|
|
176
|
+
const developerVars = loadDeveloperEnvVars(beamEnvPath);
|
|
177
|
+
// Inject developer vars immediately
|
|
178
|
+
let injectedCount = 0;
|
|
179
|
+
for (const [key, value] of Object.entries(developerVars)) {
|
|
180
|
+
if (!(key in process.env)) {
|
|
181
|
+
process.env[key] = value;
|
|
182
|
+
injectedCount++;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (injectedCount > 0) {
|
|
186
|
+
console.log(`[EnvLoader] Injected ${injectedCount} developer-defined variables from beam.env`);
|
|
187
|
+
}
|
|
188
|
+
// Fetch Beamable Config values (async, with optional timeout)
|
|
189
|
+
if (waitForConfig) {
|
|
190
|
+
try {
|
|
191
|
+
const configPromise = fetchBeamableConfig(env);
|
|
192
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
193
|
+
setTimeout(() => {
|
|
194
|
+
console.warn(`[EnvLoader] Beamable Config fetch timed out after ${timeoutMs}ms, continuing without it`);
|
|
195
|
+
resolve({});
|
|
196
|
+
}, timeoutMs);
|
|
197
|
+
});
|
|
198
|
+
const beamableConfigVars = await Promise.race([configPromise, timeoutPromise]);
|
|
199
|
+
// Inject Beamable Config vars
|
|
200
|
+
let configInjectedCount = 0;
|
|
201
|
+
for (const [key, value] of Object.entries(beamableConfigVars)) {
|
|
202
|
+
if (!(key in process.env)) {
|
|
203
|
+
process.env[key] = value;
|
|
204
|
+
configInjectedCount++;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (configInjectedCount > 0) {
|
|
208
|
+
console.log(`[EnvLoader] Injected ${configInjectedCount} variables from Beamable Config`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
console.warn('[EnvLoader] Error loading Beamable Config (continuing anyway):', error instanceof Error ? error.message : String(error));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
// Start fetch in background (non-blocking)
|
|
217
|
+
fetchBeamableConfig(env)
|
|
218
|
+
.then((beamableConfigVars) => {
|
|
219
|
+
let configInjectedCount = 0;
|
|
220
|
+
for (const [key, value] of Object.entries(beamableConfigVars)) {
|
|
221
|
+
if (!(key in process.env)) {
|
|
222
|
+
process.env[key] = value;
|
|
223
|
+
configInjectedCount++;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (configInjectedCount > 0) {
|
|
227
|
+
console.log(`[EnvLoader] Injected ${configInjectedCount} variables from Beamable Config (loaded asynchronously)`);
|
|
228
|
+
}
|
|
229
|
+
})
|
|
230
|
+
.catch((error) => {
|
|
231
|
+
console.warn('[EnvLoader] Failed to load Beamable Config in background:', error instanceof Error ? error.message : String(error));
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
console.log('[EnvLoader] Environment variable loading completed');
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=env-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-loader.js","sourceRoot":"","sources":["../src/env-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD;;;GAGG;AACH,SAAS,kBAAkB,CACzB,GAAW,EACX,MAAc,EACd,eAAuB,EACvB,OAAsB,IAAI,EAC1B,UAAkB,GAAG;IAErB,IAAI,UAAU,GAAG,GAAG,MAAM,GAAG,GAAG,GAAG,OAAO,GAAG,eAAe,EAAE,CAAC;IAC/D,IAAI,IAAI,EAAE,CAAC;QACT,UAAU,IAAI,IAAI,CAAC;IACrB,CAAC;IACD,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC3E,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,SAAS,oBAAoB,CAAC,WAAoB;IAChD,MAAM,OAAO,GAA2B,EAAE,CAAC;IAE3C,4BAA4B;IAC5B,MAAM,aAAa,GAAG;QACpB,WAAW;QACX,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,CAAC;QAC/B,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,CAAC;QAChC,wBAAwB,EAAE,iBAAiB;QAC3C,yBAAyB;KAC1B,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;IAE/D,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,WAAW,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QACnD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAElC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAC5B,gCAAgC;YAChC,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACxC,SAAS;YACX,CAAC;YAED,yBAAyB;YACzB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;YAC/C,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;gBAC5B,IAAI,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;gBAE5B,2BAA2B;gBAC3B,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;oBAC9C,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;oBACnD,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;gBAC7B,CAAC;gBAED,IAAI,GAAG,EAAE,CAAC;oBACR,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;gBACvB,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,sBAAsB,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,mBAAmB,WAAW,EAAE,CAAC,CAAC;IACjG,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,4CAA4C,WAAW,GAAG,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACnI,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAcD;;;;;;;;;;;;;;;;GAgBG;AACH,KAAK,UAAU,mBAAmB,CAChC,GAAsB;IAEtB,MAAM,UAAU,GAA2B,EAAE,CAAC;IAE9C,6CAA6C;IAC7C,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;QACxC,OAAO,CAAC,GAAG,CAAC,0EAA0E,CAAC,CAAC;QACxF,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACvC,6EAA6E;QAC7E,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,0BAA0B,CAAC;QAC/E,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;QAEtD,yCAAyC;QACzC,MAAM,YAAY,GAAG,OAAO,CAAC;QAC7B,MAAM,SAAS,GAAG,kBAAkB,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;QAEnF,OAAO,CAAC,GAAG,CAAC,6CAA6C,SAAS,KAAK,CAAC,CAAC;QAEzE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE;YACtC,MAAM,EAAE,KAAK;YACb,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,MAAM,EAAE,kBAAkB;gBAC1B,cAAc,EAAE,GAAG,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE;gBACvC,kBAAkB,EAAE,SAAS;aAC9B;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,CAAC;YACrE,OAAO,CAAC,IAAI,CAAC,gDAAgD,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,MAAM,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;YACxI,OAAO,UAAU,CAAC;QACpB,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA2B,CAAC;QAE/D,2DAA2D;QAC3D,8FAA8F;QAC9F,MAAM,kBAAkB,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,GAAG,CAAC;QAE9F,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,KAAK,MAAM,CAAC,GAAG,EAAE,YAAY,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC9D,wEAAwE;gBACxE,IAAI,KAAmD,CAAC;gBAExD,IAAI,YAAY,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;oBACrD,KAAK,GAAG,YAAY,CAAC,kBAAkB,CAAC,CAAC;oBAEzC,oEAAoE;oBACpE,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;wBACxB,KAAK,GAAG,YAAY,CAAC,YAAY,CAAC;4BAC1B,YAAY,CAAC,SAAS,CAAC;4BACvB,YAAY,CAAC,aAAa,CAAC;4BAC3B,YAAY,CAAC,KAAK,CAAC;4BACnB,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;oBACzC,CAAC;gBACH,CAAC;gBAED,8CAA8C;gBAC9C,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;oBAC1C,6CAA6C;oBAC7C,MAAM,MAAM,GAAG,eAAe,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC;oBAClD,UAAU,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;gBACrC,CAAC;YACH,CAAC;YAED,OAAO,CAAC,GAAG,CAAC,sBAAsB,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,iCAAiC,CAAC,CAAC;QACrG,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,gEAAgE,CAAC,CAAC;QAChF,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,8CAA8C,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACvH,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,iCAAiC,CACrD,GAAsB,EACtB,WAAoB,EACpB,gBAAyB,IAAI,EAC7B,YAAoB,IAAI;IAExB,OAAO,CAAC,GAAG,CAAC,sDAAsD,CAAC,CAAC;IAEpE,0EAA0E;IAC1E,MAAM,aAAa,GAAG,oBAAoB,CAAC,WAAW,CAAC,CAAC;IAExD,oCAAoC;IACpC,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;QACzD,IAAI,CAAC,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;YACzB,aAAa,EAAE,CAAC;QAClB,CAAC;IACH,CAAC;IAED,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CAAC,wBAAwB,aAAa,4CAA4C,CAAC,CAAC;IACjG,CAAC;IAED,8DAA8D;IAC9D,IAAI,aAAa,EAAE,CAAC;QAClB,IAAI,CAAC;YACH,MAAM,aAAa,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;YAC/C,MAAM,cAAc,GAAG,IAAI,OAAO,CAAyB,CAAC,OAAO,EAAE,EAAE;gBACrE,UAAU,CAAC,GAAG,EAAE;oBACd,OAAO,CAAC,IAAI,CAAC,qDAAqD,SAAS,2BAA2B,CAAC,CAAC;oBACxG,OAAO,CAAC,EAAE,CAAC,CAAC;gBACd,CAAC,EAAE,SAAS,CAAC,CAAC;YAChB,CAAC,CAAC,CAAC;YAEH,MAAM,kBAAkB,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,cAAc,CAAC,CAAC,CAAC;YAE/E,8BAA8B;YAC9B,IAAI,mBAAmB,GAAG,CAAC,CAAC;YAC5B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,EAAE,CAAC;gBAC9D,IAAI,CAAC,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;oBAC1B,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;oBACzB,mBAAmB,EAAE,CAAC;gBACxB,CAAC;YACH,CAAC;YAED,IAAI,mBAAmB,GAAG,CAAC,EAAE,CAAC;gBAC5B,OAAO,CAAC,GAAG,CAAC,wBAAwB,mBAAmB,iCAAiC,CAAC,CAAC;YAC5F,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,IAAI,CAAC,gEAAgE,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACzI,CAAC;IACH,CAAC;SAAM,CAAC;QACN,2CAA2C;QAC3C,mBAAmB,CAAC,GAAG,CAAC;aACrB,IAAI,CAAC,CAAC,kBAAkB,EAAE,EAAE;YAC3B,IAAI,mBAAmB,GAAG,CAAC,CAAC;YAC5B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,EAAE,CAAC;gBAC9D,IAAI,CAAC,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;oBAC1B,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;oBACzB,mBAAmB,EAAE,CAAC;gBACxB,CAAC;YACH,CAAC;YACD,IAAI,mBAAmB,GAAG,CAAC,EAAE,CAAC;gBAC5B,OAAO,CAAC,GAAG,CAAC,wBAAwB,mBAAmB,yDAAyD,CAAC,CAAC;YACpH,CAAC;QACH,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YACf,OAAO,CAAC,IAAI,CAAC,2DAA2D,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACpI,CAAC,CAAC,CAAC;IACP,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,oDAAoD,CAAC,CAAC;AACpE,CAAC","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\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): 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 const uriPath = process.env.BEAM_CONFIG_API_PATH || '/api/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 const 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 });\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);\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/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;AAI1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAiCpD,UAAU,oBAAoB;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;
|
|
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;AAiCpD,UAAU,oBAAoB;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAmmBD,wBAAgB,YAAY,CAAC,GAAG,EAAE,iBAAiB,EAAE,OAAO,GAAE,oBAAyB,GAAG,MAAM,CA6L/F"}
|
package/dist/logger.js
CHANGED
|
@@ -51,6 +51,93 @@ function mapPinoLevelToBeamableLevel(level) {
|
|
|
51
51
|
return 'Info';
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Initializes BetterStack OTLP log exporter if enabled.
|
|
56
|
+
* BetterStack is an optional logging provider that can be used alongside Beamable's ClickHouse logging
|
|
57
|
+
* for debugging and monitoring. This enables "double-pumping" logs to both ClickHouse and BetterStack.
|
|
58
|
+
*
|
|
59
|
+
* To enable BetterStack logging, set the following environment variables:
|
|
60
|
+
* - BEAM_BETTERSTACK_ENABLED=true (or 1)
|
|
61
|
+
* - BEAM_BETTERSTACK_TOKEN=<your-betterstack-ingestion-token>
|
|
62
|
+
* - BEAM_BETTERSTACK_ENDPOINT=<optional-custom-endpoint> (defaults to https://in.logs.betterstack.com)
|
|
63
|
+
*
|
|
64
|
+
* Default ingestion token: QxFSMKUoS66bgA4aZmqih7ec (from created BetterStack source)
|
|
65
|
+
*
|
|
66
|
+
* Logs will be sent to both:
|
|
67
|
+
* 1. Beamable ClickHouse (via OTLP collector) - for Portal integration
|
|
68
|
+
* 2. BetterStack (via OTLP HTTP) - for debugging and monitoring
|
|
69
|
+
*
|
|
70
|
+
* @param serviceName - Service name for resource attributes
|
|
71
|
+
* @param qualifiedServiceName - Qualified service name for resource attributes
|
|
72
|
+
* @param env - Environment configuration
|
|
73
|
+
* @returns BetterStack OTLP exporter if configured, null otherwise
|
|
74
|
+
*/
|
|
75
|
+
async function initializeBetterStackLogging(serviceName, qualifiedServiceName, env) {
|
|
76
|
+
// Check if BetterStack logging is enabled
|
|
77
|
+
const betterStackEnabled = process.env.BEAM_BETTERSTACK_ENABLED === 'true' ||
|
|
78
|
+
process.env.BEAM_BETTERSTACK_ENABLED === '1';
|
|
79
|
+
if (!betterStackEnabled) {
|
|
80
|
+
return Promise.resolve(null);
|
|
81
|
+
}
|
|
82
|
+
// Get BetterStack configuration
|
|
83
|
+
// Token: Use BEAM_BETTERSTACK_TOKEN or BETTERSTACK_TOKEN environment variable
|
|
84
|
+
// Default token from created source: QxFSMKUoS66bgA4aZmqih7ec (can be overridden)
|
|
85
|
+
const betterStackToken = process.env.BEAM_BETTERSTACK_TOKEN || process.env.BETTERSTACK_TOKEN;
|
|
86
|
+
// Endpoint: BetterStack OTLP endpoint (defaults to EU region)
|
|
87
|
+
// Can be overridden via BEAM_BETTERSTACK_ENDPOINT or BETTERSTACK_ENDPOINT
|
|
88
|
+
const betterStackEndpoint = process.env.BEAM_BETTERSTACK_ENDPOINT ||
|
|
89
|
+
process.env.BETTERSTACK_ENDPOINT ||
|
|
90
|
+
'https://in.logs.betterstack.com';
|
|
91
|
+
if (!betterStackToken) {
|
|
92
|
+
console.warn('[BetterStack] BetterStack logging is enabled but BEAM_BETTERSTACK_TOKEN is not set. Skipping BetterStack logging.');
|
|
93
|
+
return Promise.resolve(null);
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
// Build resource attributes (same as main OTLP)
|
|
97
|
+
const resourceAttributes = {};
|
|
98
|
+
if (serviceName) {
|
|
99
|
+
resourceAttributes['service.namespace'] = serviceName;
|
|
100
|
+
resourceAttributes['service.name'] = serviceName;
|
|
101
|
+
}
|
|
102
|
+
if (qualifiedServiceName) {
|
|
103
|
+
resourceAttributes['service.instance.id'] = qualifiedServiceName;
|
|
104
|
+
}
|
|
105
|
+
if (env?.cid) {
|
|
106
|
+
resourceAttributes['beam.cid'] = String(env.cid);
|
|
107
|
+
}
|
|
108
|
+
if (env?.pid) {
|
|
109
|
+
resourceAttributes['beam.pid'] = String(env.pid);
|
|
110
|
+
}
|
|
111
|
+
if (env?.routingKey) {
|
|
112
|
+
resourceAttributes['beam.routing_key'] = String(env.routingKey);
|
|
113
|
+
}
|
|
114
|
+
// BetterStack OTLP endpoint format
|
|
115
|
+
// BetterStack accepts OTLP logs at: https://in.logs.betterstack.com/v1/logs
|
|
116
|
+
const endpointUrl = betterStackEndpoint.includes('/v1/logs')
|
|
117
|
+
? betterStackEndpoint
|
|
118
|
+
: `${betterStackEndpoint.replace(/\/$/, '')}/v1/logs`;
|
|
119
|
+
// Create OTLP HTTP exporter for BetterStack
|
|
120
|
+
// BetterStack supports OTLP HTTP with JSON or Protobuf
|
|
121
|
+
// Using JSON format for better compatibility
|
|
122
|
+
const exporterOptions = {
|
|
123
|
+
url: endpointUrl,
|
|
124
|
+
headers: {
|
|
125
|
+
'Authorization': `Bearer ${betterStackToken}`,
|
|
126
|
+
},
|
|
127
|
+
contentType: 'application/json', // BetterStack supports both JSON and Protobuf, JSON is more compatible
|
|
128
|
+
};
|
|
129
|
+
const exporter = new OTLPLogExporter(exporterOptions);
|
|
130
|
+
console.error(`[BetterStack] BetterStack logging initialized. Endpoint: ${endpointUrl}, Service: ${serviceName || 'unknown'}`);
|
|
131
|
+
return Promise.resolve(exporter);
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
console.error('[BetterStack] Failed to initialize BetterStack logging:', error instanceof Error ? error.message : String(error));
|
|
135
|
+
if (error instanceof Error && error.stack) {
|
|
136
|
+
console.error('[BetterStack] Stack trace:', error.stack);
|
|
137
|
+
}
|
|
138
|
+
return Promise.resolve(null);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
54
141
|
/**
|
|
55
142
|
* Initializes OpenTelemetry OTLP log exporter if configured.
|
|
56
143
|
* Similar to C# microservices, checks for BEAM_OTEL_EXPORTER_OTLP_ENDPOINT or uses standard enabled flag.
|
|
@@ -203,14 +290,27 @@ async function initializeOtlpLogging(serviceName, qualifiedServiceName, env, log
|
|
|
203
290
|
const baseResource = defaultResource();
|
|
204
291
|
const customResource = resourceFromAttributes(resourceAttributes);
|
|
205
292
|
const resource = baseResource.merge(customResource);
|
|
206
|
-
// Create log record processor
|
|
293
|
+
// Create log record processor for Beamable ClickHouse
|
|
207
294
|
// C# uses BatchLogRecordExportProcessor which batches logs for better performance
|
|
208
295
|
// and includes retry logic. We should match this behavior.
|
|
209
|
-
const
|
|
210
|
-
//
|
|
296
|
+
const processors = [new BatchLogRecordProcessor(exporter)];
|
|
297
|
+
// Optionally add BetterStack as a second exporter for double-pumping logs
|
|
298
|
+
// This allows developers to enable BetterStack for debugging/monitoring
|
|
299
|
+
try {
|
|
300
|
+
const betterStackExporter = await initializeBetterStackLogging(serviceName, qualifiedServiceName, env);
|
|
301
|
+
if (betterStackExporter) {
|
|
302
|
+
processors.push(new BatchLogRecordProcessor(betterStackExporter));
|
|
303
|
+
console.error('[BetterStack] BetterStack exporter added as second processor - logs will be sent to both ClickHouse and BetterStack');
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
catch (error) {
|
|
307
|
+
// If BetterStack initialization fails, continue without it
|
|
308
|
+
console.warn('[BetterStack] Failed to initialize BetterStack exporter (continuing without it):', error instanceof Error ? error.message : String(error));
|
|
309
|
+
}
|
|
310
|
+
// Create logger provider with resource and processors (both ClickHouse and optionally BetterStack)
|
|
211
311
|
const loggerProvider = new LoggerProvider({
|
|
212
312
|
resource: resource,
|
|
213
|
-
processors:
|
|
313
|
+
processors: processors,
|
|
214
314
|
});
|
|
215
315
|
// Set as global logger provider
|
|
216
316
|
logs.setGlobalLoggerProvider(loggerProvider);
|