@omen.foundation/node-microservice-runtime 0.1.76 → 0.1.77

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.
@@ -0,0 +1,187 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loadDeveloperEnvVarsSync = loadDeveloperEnvVarsSync;
4
+ exports.loadAndInjectEnvironmentVariables = loadAndInjectEnvironmentVariables;
5
+ const node_fs_1 = require("node:fs");
6
+ const node_path_1 = require("node:path");
7
+ const node_crypto_1 = require("node:crypto");
8
+ const node_url_1 = require("node:url");
9
+ const urls_js_1 = require("./utils/urls.js");
10
+ function calculateSignature(pid, secret, uriPathAndQuery, body = null, version = '1') {
11
+ let dataToSign = `${secret}${pid}${version}${uriPathAndQuery}`;
12
+ if (body) {
13
+ dataToSign += body;
14
+ }
15
+ const hash = (0, node_crypto_1.createHash)('md5').update(dataToSign, 'utf8').digest('base64');
16
+ return hash;
17
+ }
18
+ function loadDeveloperEnvVarsSync(beamEnvPath) {
19
+ const envVars = loadDeveloperEnvVars(beamEnvPath);
20
+ let injectedCount = 0;
21
+ for (const [key, value] of Object.entries(envVars)) {
22
+ if (!(key in process.env)) {
23
+ process.env[key] = value;
24
+ injectedCount++;
25
+ }
26
+ }
27
+ if (injectedCount > 0) {
28
+ console.log(`[EnvLoader] Synchronously injected ${injectedCount} developer-defined variables from beam.env`);
29
+ }
30
+ }
31
+ function loadDeveloperEnvVars(beamEnvPath) {
32
+ const envVars = {};
33
+ const possiblePaths = [
34
+ beamEnvPath,
35
+ (0, node_path_1.join)(process.cwd(), 'beam.env'),
36
+ (0, node_path_1.join)(process.cwd(), '.beam.env'),
37
+ '/beam/service/beam.env',
38
+ '/beam/service/.beam.env',
39
+ ].filter((path) => !!path && (0, node_fs_1.existsSync)(path));
40
+ if (possiblePaths.length === 0) {
41
+ return envVars;
42
+ }
43
+ const envFilePath = possiblePaths[0];
44
+ try {
45
+ const content = (0, node_fs_1.readFileSync)(envFilePath, 'utf-8');
46
+ const lines = content.split('\n');
47
+ for (const line of lines) {
48
+ const trimmed = line.trim();
49
+ if (!trimmed || trimmed.startsWith('#')) {
50
+ continue;
51
+ }
52
+ const match = trimmed.match(/^([^=#]+)=(.*)$/);
53
+ if (match) {
54
+ const key = match[1].trim();
55
+ let value = match[2].trim();
56
+ if ((value.startsWith('"') && value.endsWith('"')) ||
57
+ (value.startsWith("'") && value.endsWith("'"))) {
58
+ value = value.slice(1, -1);
59
+ }
60
+ if (key) {
61
+ envVars[key] = value;
62
+ }
63
+ }
64
+ }
65
+ console.log(`[EnvLoader] Loaded ${Object.keys(envVars).length} variables from ${envFilePath}`);
66
+ }
67
+ catch (error) {
68
+ console.warn(`[EnvLoader] Failed to load beam.env from ${envFilePath}:`, error instanceof Error ? error.message : String(error));
69
+ }
70
+ return envVars;
71
+ }
72
+ async function fetchBeamableConfig(env) {
73
+ const configVars = {};
74
+ if (!env.secret || !env.cid || !env.pid) {
75
+ console.log('[EnvLoader] Skipping Beamable Config fetch - missing SECRET, CID, or PID');
76
+ return configVars;
77
+ }
78
+ try {
79
+ const apiUrl = (0, urls_js_1.hostToHttpUrl)(env.host);
80
+ const uriPath = process.env.BEAM_CONFIG_API_PATH || '/api/basic/realms/config';
81
+ 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');
84
+ console.log(`[EnvLoader] Fetching Beamable Config from ${configUrl}...`);
85
+ const response = await fetch(configUrl, {
86
+ method: 'GET',
87
+ headers: {
88
+ 'Content-Type': 'application/json',
89
+ Accept: 'application/json',
90
+ 'X-BEAM-SCOPE': `${env.cid}.${env.pid}`,
91
+ 'X-BEAM-SIGNATURE': signature,
92
+ },
93
+ });
94
+ if (!response.ok) {
95
+ const errorText = await response.text().catch(() => 'Unknown error');
96
+ console.warn(`[EnvLoader] Failed to fetch Beamable Config: ${response.status} ${response.statusText} - ${errorText.substring(0, 200)}`);
97
+ return configVars;
98
+ }
99
+ const data = (await response.json());
100
+ const currentEnvironment = process.env.BEAM_ENVIRONMENT || process.env.ENVIRONMENT || env.pid;
101
+ if (data.config) {
102
+ for (const [key, environments] of Object.entries(data.config)) {
103
+ let value;
104
+ if (environments && typeof environments === 'object') {
105
+ value = environments[currentEnvironment];
106
+ if (value === undefined) {
107
+ value = environments['production'] ||
108
+ environments['staging'] ||
109
+ environments['development'] ||
110
+ environments['dev'] ||
111
+ Object.values(environments)[0];
112
+ }
113
+ }
114
+ if (value !== undefined && value !== null) {
115
+ const envKey = `BEAM_CONFIG_${key.toUpperCase()}`;
116
+ configVars[envKey] = String(value);
117
+ }
118
+ }
119
+ console.log(`[EnvLoader] Loaded ${Object.keys(configVars).length} variables from Beamable Config`);
120
+ }
121
+ else {
122
+ console.log('[EnvLoader] Beamable Config response contains no config object');
123
+ }
124
+ }
125
+ catch (error) {
126
+ console.warn('[EnvLoader] Failed to fetch Beamable Config:', error instanceof Error ? error.message : String(error));
127
+ }
128
+ return configVars;
129
+ }
130
+ async function loadAndInjectEnvironmentVariables(env, beamEnvPath, waitForConfig = true, timeoutMs = 2000) {
131
+ console.log('[EnvLoader] Starting environment variable loading...');
132
+ const developerVars = loadDeveloperEnvVars(beamEnvPath);
133
+ let injectedCount = 0;
134
+ for (const [key, value] of Object.entries(developerVars)) {
135
+ if (!(key in process.env)) {
136
+ process.env[key] = value;
137
+ injectedCount++;
138
+ }
139
+ }
140
+ if (injectedCount > 0) {
141
+ console.log(`[EnvLoader] Injected ${injectedCount} developer-defined variables from beam.env`);
142
+ }
143
+ if (waitForConfig) {
144
+ try {
145
+ const configPromise = fetchBeamableConfig(env);
146
+ const timeoutPromise = new Promise((resolve) => {
147
+ setTimeout(() => {
148
+ console.warn(`[EnvLoader] Beamable Config fetch timed out after ${timeoutMs}ms, continuing without it`);
149
+ resolve({});
150
+ }, timeoutMs);
151
+ });
152
+ const beamableConfigVars = await Promise.race([configPromise, timeoutPromise]);
153
+ let configInjectedCount = 0;
154
+ for (const [key, value] of Object.entries(beamableConfigVars)) {
155
+ if (!(key in process.env)) {
156
+ process.env[key] = value;
157
+ configInjectedCount++;
158
+ }
159
+ }
160
+ if (configInjectedCount > 0) {
161
+ console.log(`[EnvLoader] Injected ${configInjectedCount} variables from Beamable Config`);
162
+ }
163
+ }
164
+ catch (error) {
165
+ console.warn('[EnvLoader] Error loading Beamable Config (continuing anyway):', error instanceof Error ? error.message : String(error));
166
+ }
167
+ }
168
+ else {
169
+ fetchBeamableConfig(env)
170
+ .then((beamableConfigVars) => {
171
+ let configInjectedCount = 0;
172
+ for (const [key, value] of Object.entries(beamableConfigVars)) {
173
+ if (!(key in process.env)) {
174
+ process.env[key] = value;
175
+ configInjectedCount++;
176
+ }
177
+ }
178
+ if (configInjectedCount > 0) {
179
+ console.log(`[EnvLoader] Injected ${configInjectedCount} variables from Beamable Config (loaded asynchronously)`);
180
+ }
181
+ })
182
+ .catch((error) => {
183
+ console.warn('[EnvLoader] Failed to load Beamable Config in background:', error instanceof Error ? error.message : String(error));
184
+ });
185
+ }
186
+ console.log('[EnvLoader] Environment variable loading completed');
187
+ }
package/dist/env.cjs ADDED
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loadEnvironmentConfig = loadEnvironmentConfig;
4
+ exports.ensureWritableTempDirectory = ensureWritableTempDirectory;
5
+ const node_fs_1 = require("node:fs");
6
+ const node_os_1 = require("node:os");
7
+ const node_path_1 = require("node:path");
8
+ const routing_js_1 = require("./routing.js");
9
+ function getBoolean(name, defaultValue = false) {
10
+ const raw = process.env[name];
11
+ if (!raw) {
12
+ return defaultValue;
13
+ }
14
+ return ['1', 'true', 'yes', 'on'].includes(raw.toLowerCase());
15
+ }
16
+ function getNumber(name, defaultValue) {
17
+ const raw = process.env[name];
18
+ if (!raw) {
19
+ return defaultValue;
20
+ }
21
+ const parsed = Number(raw);
22
+ return Number.isFinite(parsed) ? parsed : defaultValue;
23
+ }
24
+ function resolveHealthPort() {
25
+ const preferred = getNumber('HEALTH_PORT', 6565);
26
+ if (preferred > 0) {
27
+ return preferred;
28
+ }
29
+ const base = 45000;
30
+ const span = 2000;
31
+ const candidate = base + (process.pid % span);
32
+ return candidate;
33
+ }
34
+ function isInContainer() {
35
+ try {
36
+ const fs = require('fs');
37
+ if (fs.existsSync('/.dockerenv')) {
38
+ return true;
39
+ }
40
+ }
41
+ catch {
42
+ }
43
+ const hostname = process.env.HOSTNAME || '';
44
+ if (hostname && /^[a-f0-9]{12}$/i.test(hostname)) {
45
+ return true;
46
+ }
47
+ if (process.env.DOTNET_RUNNING_IN_CONTAINER === 'true' ||
48
+ process.env.CONTAINER === 'beamable' ||
49
+ !!process.env.ECS_CONTAINER_METADATA_URI ||
50
+ !!process.env.KUBERNETES_SERVICE_HOST) {
51
+ return true;
52
+ }
53
+ return false;
54
+ }
55
+ function resolveRoutingKey() {
56
+ var _a;
57
+ const raw = (_a = process.env.NAME_PREFIX) !== null && _a !== void 0 ? _a : process.env.ROUTING_KEY;
58
+ if (raw && raw.trim().length > 0) {
59
+ return raw.trim();
60
+ }
61
+ if (isInContainer()) {
62
+ return undefined;
63
+ }
64
+ try {
65
+ return (0, routing_js_1.getDefaultRoutingKeyForMachine)();
66
+ }
67
+ catch (error) {
68
+ throw new Error(`Unable to determine routing key automatically. Set NAME_PREFIX environment variable. ${error.message}`);
69
+ }
70
+ }
71
+ function resolveSdkVersionExecution() {
72
+ var _a;
73
+ return (_a = process.env.BEAMABLE_SDK_VERSION_EXECUTION) !== null && _a !== void 0 ? _a : '';
74
+ }
75
+ function resolveLogLevel() {
76
+ var _a;
77
+ const candidate = (_a = process.env.LOG_LEVEL) !== null && _a !== void 0 ? _a : 'info';
78
+ const allowed = new Set(['fatal', 'error', 'warn', 'info', 'debug', 'trace']);
79
+ return allowed.has(candidate.toLowerCase()) ? candidate.toLowerCase() : 'info';
80
+ }
81
+ function resolveWatchToken() {
82
+ const value = process.env.WATCH_TOKEN;
83
+ if (value === undefined) {
84
+ return false;
85
+ }
86
+ return getBoolean('WATCH_TOKEN', false);
87
+ }
88
+ function resolveBeamInstanceCount() {
89
+ return getNumber('BEAM_INSTANCE_COUNT', 1);
90
+ }
91
+ function loadEnvironmentConfig() {
92
+ var _a, _b, _c, _d, _e, _f;
93
+ const cid = (_a = process.env.CID) !== null && _a !== void 0 ? _a : '';
94
+ const pid = (_b = process.env.PID) !== null && _b !== void 0 ? _b : '';
95
+ const host = (_c = process.env.HOST) !== null && _c !== void 0 ? _c : '';
96
+ if (!cid || !pid || !host) {
97
+ throw new Error('Missing required Beamable environment variables (CID, PID, HOST).');
98
+ }
99
+ const config = {
100
+ cid,
101
+ pid,
102
+ host,
103
+ secret: (_d = process.env.SECRET) !== null && _d !== void 0 ? _d : undefined,
104
+ refreshToken: (_e = process.env.REFRESH_TOKEN) !== null && _e !== void 0 ? _e : undefined,
105
+ routingKey: resolveRoutingKey(),
106
+ accountId: getNumber('USER_ACCOUNT_ID', 0) || undefined,
107
+ accountEmail: (_f = process.env.USER_EMAIL) !== null && _f !== void 0 ? _f : undefined,
108
+ logLevel: resolveLogLevel(),
109
+ healthPort: resolveHealthPort(),
110
+ disableCustomInitializationHooks: getBoolean('DISABLE_CUSTOM_INITIALIZATION_HOOKS', false),
111
+ watchToken: resolveWatchToken(),
112
+ sdkVersionExecution: resolveSdkVersionExecution(),
113
+ beamInstanceCount: resolveBeamInstanceCount(),
114
+ logTruncateLimit: getNumber('LOG_TRUNCATE_LIMIT', 1000),
115
+ };
116
+ return config;
117
+ }
118
+ function ensureWritableTempDirectory() {
119
+ var _a;
120
+ const candidate = (_a = process.env.LOG_PATH) !== null && _a !== void 0 ? _a : (0, node_path_1.join)((0, node_os_1.tmpdir)(), 'beamable-node-runtime');
121
+ if (!(0, node_fs_1.existsSync)(candidate)) {
122
+ return candidate;
123
+ }
124
+ return candidate;
125
+ }
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InvalidConfigurationError = exports.SerializationError = exports.UnknownRouteError = exports.UnauthorizedUserError = exports.MissingScopesError = exports.TimeoutError = exports.AuthenticationError = exports.BeamableRuntimeError = void 0;
4
+ class BeamableRuntimeError extends Error {
5
+ constructor(code, message, cause) {
6
+ var _a;
7
+ super(message);
8
+ this.code = code;
9
+ if (cause !== undefined) {
10
+ this.cause = cause;
11
+ }
12
+ this.name = this.constructor.name;
13
+ (_a = Error.captureStackTrace) === null || _a === void 0 ? void 0 : _a.call(Error, this, this.constructor);
14
+ }
15
+ }
16
+ exports.BeamableRuntimeError = BeamableRuntimeError;
17
+ class AuthenticationError extends BeamableRuntimeError {
18
+ constructor(message, cause) {
19
+ super('AUTHENTICATION_FAILED', message, cause);
20
+ }
21
+ }
22
+ exports.AuthenticationError = AuthenticationError;
23
+ class TimeoutError extends BeamableRuntimeError {
24
+ constructor(message, cause) {
25
+ super('TIMEOUT', message, cause);
26
+ }
27
+ }
28
+ exports.TimeoutError = TimeoutError;
29
+ class MissingScopesError extends BeamableRuntimeError {
30
+ constructor(requiredScopes) {
31
+ super('MISSING_SCOPES', `Missing required scopes: ${Array.from(requiredScopes).join(', ')}`);
32
+ }
33
+ }
34
+ exports.MissingScopesError = MissingScopesError;
35
+ class UnauthorizedUserError extends BeamableRuntimeError {
36
+ constructor(route) {
37
+ super('UNAUTHORIZED_USER', `Route "${route}" requires an authenticated user.`);
38
+ }
39
+ }
40
+ exports.UnauthorizedUserError = UnauthorizedUserError;
41
+ class UnknownRouteError extends BeamableRuntimeError {
42
+ constructor(route) {
43
+ super('UNKNOWN_ROUTE', `No callable registered for route "${route}".`);
44
+ }
45
+ }
46
+ exports.UnknownRouteError = UnknownRouteError;
47
+ class SerializationError extends BeamableRuntimeError {
48
+ constructor(message, cause) {
49
+ super('SERIALIZATION_ERROR', message, cause);
50
+ }
51
+ }
52
+ exports.SerializationError = SerializationError;
53
+ class InvalidConfigurationError extends BeamableRuntimeError {
54
+ constructor(message, cause) {
55
+ super('INVALID_CONFIGURATION', message, cause);
56
+ }
57
+ }
58
+ exports.InvalidConfigurationError = InvalidConfigurationError;