@omen.foundation/node-microservice-runtime 0.1.64 → 0.1.67

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.
Files changed (44) hide show
  1. package/dist/collector-manager.cjs +17 -1
  2. package/dist/collector-manager.d.ts +3 -0
  3. package/dist/collector-manager.d.ts.map +1 -1
  4. package/dist/collector-manager.js +21 -1
  5. package/dist/collector-manager.js.map +1 -1
  6. package/package.json +4 -1
  7. package/.env +0 -13
  8. package/env.sample +0 -13
  9. package/scripts/generate-openapi.mjs +0 -114
  10. package/scripts/lib/cli-utils.mjs +0 -58
  11. package/scripts/prepare-cjs.mjs +0 -44
  12. package/scripts/publish-service.mjs +0 -1119
  13. package/scripts/validate-service.mjs +0 -103
  14. package/scripts/ws-test.mjs +0 -25
  15. package/src/auth.ts +0 -117
  16. package/src/cli/index.ts +0 -725
  17. package/src/collector-manager.ts +0 -1240
  18. package/src/decorators.ts +0 -207
  19. package/src/dependency.ts +0 -211
  20. package/src/dev.ts +0 -17
  21. package/src/discovery.ts +0 -88
  22. package/src/docs.ts +0 -262
  23. package/src/env.ts +0 -148
  24. package/src/errors.ts +0 -55
  25. package/src/federation.ts +0 -559
  26. package/src/index.ts +0 -84
  27. package/src/inventory.ts +0 -491
  28. package/src/logger.ts +0 -727
  29. package/src/message.ts +0 -19
  30. package/src/requester.ts +0 -126
  31. package/src/routing.ts +0 -42
  32. package/src/runtime.ts +0 -1071
  33. package/src/services.ts +0 -459
  34. package/src/storage.ts +0 -206
  35. package/src/types/beamable-sdk-api.d.ts +0 -5
  36. package/src/types.ts +0 -117
  37. package/src/utils/urls.ts +0 -53
  38. package/src/websocket.ts +0 -170
  39. package/test-downloads/collector-test +0 -0
  40. package/test-downloads/collector-test.gz +0 -0
  41. package/tsconfig.base.json +0 -31
  42. package/tsconfig.build.json +0 -10
  43. package/tsconfig.cjs.json +0 -16
  44. package/tsconfig.dev.json +0 -14
package/src/docs.ts DELETED
@@ -1,262 +0,0 @@
1
- import type { EnvironmentConfig, ServiceCallableMetadata } from './types.js';
2
- import { hostToHttpUrl } from './utils/urls.js';
3
-
4
- const JSON_CONTENT_TYPE = 'application/json';
5
- const ADMIN_TAG = 'Administration';
6
-
7
- interface ServiceCallableDescriptor {
8
- name: string;
9
- route: string;
10
- metadata: ServiceCallableMetadata;
11
- handler?: (...args: unknown[]) => unknown;
12
- }
13
-
14
- interface ServiceDescriptorLite {
15
- qualifiedName: string;
16
- name: string;
17
- callables: ServiceCallableDescriptor[];
18
- }
19
-
20
- const SCOPE_SECURITY_SCHEME = {
21
- type: 'apiKey',
22
- name: 'X-DE-SCOPE',
23
- in: 'header',
24
- description: "Customer and project scope. This should contain the '<customer-id>.<project-id>'. Required for all microservice calls.",
25
- };
26
-
27
- const BEAM_SCOPE_SECURITY_SCHEME = {
28
- type: 'apiKey',
29
- name: 'X-BEAM-SCOPE',
30
- in: 'header',
31
- description: "Customer and project scope. This should contain the '<customer-id>.<project-id>'. Required for all microservice calls. Alternative to X-DE-SCOPE.",
32
- };
33
-
34
- const USER_SECURITY_SCHEME = {
35
- type: 'http',
36
- scheme: 'bearer',
37
- bearerFormat: 'Bearer <Access Token>',
38
- description: 'Bearer authentication with a player access token in the Authorization header.',
39
- };
40
-
41
- interface OpenApiDocument {
42
- openapi: string;
43
- info: {
44
- title: string;
45
- version: string;
46
- };
47
- servers: Array<{ url: string }>;
48
- paths: Record<string, unknown>;
49
- components: {
50
- securitySchemes: Record<string, unknown>;
51
- schemas: Record<string, unknown>;
52
- };
53
- }
54
-
55
- export function generateOpenApiDocument(service: ServiceDescriptorLite, env: EnvironmentConfig, runtimeVersion?: string): OpenApiDocument {
56
- const serverBase = buildServiceBaseUrl(env, service);
57
-
58
- const doc: OpenApiDocument = {
59
- openapi: '3.0.1',
60
- info: {
61
- title: service.name,
62
- version: runtimeVersion || process.env.BEAMABLE_RUNTIME_VERSION || '0.0.0',
63
- },
64
- servers: serverBase ? [{ url: serverBase }] : [],
65
- paths: {},
66
- components: {
67
- securitySchemes: {
68
- scope: SCOPE_SECURITY_SCHEME,
69
- beamScope: BEAM_SCOPE_SECURITY_SCHEME,
70
- user: USER_SECURITY_SCHEME,
71
- },
72
- schemas: {},
73
- },
74
- };
75
-
76
- for (const callable of service.callables) {
77
- const normalizedRoute = normalizeRoute(callable.route);
78
- const pathKey = `/${normalizedRoute}`;
79
- const payload = buildRequestSchema(callable.handler);
80
- // All endpoints require X-DE-SCOPE or X-BEAM-SCOPE for routing
81
- const securityEntry: Record<string, string[]> = { scope: [], beamScope: [] };
82
- if (callable.metadata.requireAuth) {
83
- securityEntry.user = [];
84
- }
85
-
86
- const operation: Record<string, unknown> = {
87
- operationId: callable.name,
88
- summary: callable.name,
89
- tags: callable.metadata.tags.length > 0 ? callable.metadata.tags : ['Callables'],
90
- responses: {
91
- '200': {
92
- description: 'Success',
93
- content: {
94
- [JSON_CONTENT_TYPE]: {
95
- schema: {
96
- oneOf: [{ type: 'object' }, { type: 'array' }, { type: 'string' }, { type: 'number' }, { type: 'boolean' }, { type: 'null' }],
97
- },
98
- },
99
- },
100
- },
101
- },
102
- security: [securityEntry],
103
- 'x-beamable-access': callable.metadata.access,
104
- };
105
-
106
- if (callable.metadata.requiredScopes.length > 0) {
107
- operation['x-beamable-required-scopes'] = callable.metadata.requiredScopes;
108
- }
109
-
110
- if (payload) {
111
- operation.requestBody = {
112
- required: payload.required,
113
- content: {
114
- [JSON_CONTENT_TYPE]: {
115
- schema: payload.schema,
116
- },
117
- },
118
- };
119
- }
120
-
121
- doc.paths[pathKey] = {
122
- post: operation,
123
- };
124
- }
125
-
126
- addAdminOperations(doc);
127
-
128
- return JSON.parse(JSON.stringify(doc)) as OpenApiDocument;
129
- }
130
-
131
- function buildRequestSchema(handler?: (...args: unknown[]) => unknown):
132
- | { required: boolean; schema: Record<string, unknown> }
133
- | undefined {
134
- if (typeof handler !== 'function') {
135
- return {
136
- required: false,
137
- schema: {
138
- type: 'object',
139
- properties: {
140
- payload: {
141
- type: 'array',
142
- description: 'Callable arguments in invocation order.',
143
- },
144
- },
145
- additionalProperties: false,
146
- },
147
- };
148
- }
149
-
150
- const expectedParams = handler.length;
151
- // The runtime injects RequestContext as the first argument when the handler expects one more parameter than provided.
152
- const payloadCount = Math.max(expectedParams - 1, 0);
153
-
154
- if (payloadCount <= 0) {
155
- return undefined;
156
- }
157
-
158
- return {
159
- required: true,
160
- schema: {
161
- type: 'object',
162
- properties: {
163
- payload: {
164
- type: 'array',
165
- minItems: payloadCount,
166
- description: 'Callable arguments in invocation order.',
167
- },
168
- },
169
- required: ['payload'],
170
- additionalProperties: false,
171
- },
172
- };
173
- }
174
-
175
- function buildServiceBaseUrl(env: EnvironmentConfig, service: ServiceDescriptorLite): string {
176
- const host = hostToHttpUrl(env.host).replace(/\/$/, '');
177
- return `${host}/basic/${env.cid}.${env.pid}.${service.qualifiedName}/`;
178
- }
179
-
180
- function normalizeRoute(route: string): string {
181
- if (!route) {
182
- return '';
183
- }
184
- return route.replace(/^\/+/, '');
185
- }
186
-
187
- function addAdminOperations(doc: OpenApiDocument): void {
188
- // All endpoints require X-DE-SCOPE or X-BEAM-SCOPE for routing
189
- const requiredScopeSecurity: Record<string, string[]> = { scope: [], beamScope: [] };
190
-
191
- doc.paths['/admin/HealthCheck'] = {
192
- post: {
193
- operationId: 'adminHealthCheck',
194
- summary: 'HealthCheck',
195
- tags: [ADMIN_TAG],
196
- responses: {
197
- '200': {
198
- description: 'The word "responsive" if all is well.',
199
- content: {
200
- [JSON_CONTENT_TYPE]: {
201
- schema: {
202
- type: 'string',
203
- example: 'responsive',
204
- },
205
- },
206
- },
207
- },
208
- },
209
- security: [requiredScopeSecurity],
210
- // Note: The portal constructs X-BEAM-SERVICE-ROUTING-KEY header from the service name
211
- // in the server URL. The service name format must match what we registered with.
212
- },
213
- };
214
-
215
- const adminSecurity: Record<string, string[]> = { scope: ['admin'], beamScope: ['admin'] };
216
-
217
- doc.paths['/admin/Docs'] = {
218
- post: {
219
- operationId: 'adminDocs',
220
- summary: 'Docs',
221
- description: 'Generates an OpenAPI/Swagger 3.0 document that describes the available service endpoints.',
222
- tags: [ADMIN_TAG],
223
- responses: {
224
- '200': {
225
- description: 'Swagger JSON document.',
226
- content: {
227
- [JSON_CONTENT_TYPE]: {
228
- schema: {
229
- type: 'object',
230
- },
231
- },
232
- },
233
- },
234
- },
235
- security: [adminSecurity],
236
- 'x-beamable-required-scopes': ['admin'],
237
- },
238
- };
239
-
240
- doc.paths['/admin/Metadata'] = {
241
- post: {
242
- operationId: 'adminMetadata',
243
- summary: 'Metadata',
244
- description: 'Fetch various Beamable SDK metadata for the Microservice.',
245
- tags: [ADMIN_TAG],
246
- responses: {
247
- '200': {
248
- description: 'Service metadata.',
249
- content: {
250
- [JSON_CONTENT_TYPE]: {
251
- schema: {
252
- type: 'object',
253
- },
254
- },
255
- },
256
- },
257
- },
258
- security: [adminSecurity],
259
- 'x-beamable-required-scopes': ['admin'],
260
- },
261
- };
262
- }
package/src/env.ts DELETED
@@ -1,148 +0,0 @@
1
- import { existsSync } from 'node:fs';
2
- import { tmpdir } from 'node:os';
3
- import { join } from 'node:path';
4
- import type { EnvironmentConfig } from './types.js';
5
- import { getDefaultRoutingKeyForMachine } from './routing.js';
6
-
7
- function getBoolean(name: string, defaultValue = false): boolean {
8
- const raw = process.env[name];
9
- if (!raw) {
10
- return defaultValue;
11
- }
12
- return ['1', 'true', 'yes', 'on'].includes(raw.toLowerCase());
13
- }
14
-
15
- function getNumber(name: string, defaultValue: number): number {
16
- const raw = process.env[name];
17
- if (!raw) {
18
- return defaultValue;
19
- }
20
- const parsed = Number(raw);
21
- return Number.isFinite(parsed) ? parsed : defaultValue;
22
- }
23
-
24
- function resolveHealthPort(): number {
25
- // Always default to 6565 if HEALTH_PORT is not explicitly set
26
- // This ensures the health check server starts in deployed environments
27
- // even if container detection fails or HEALTH_PORT env var is missing
28
- const preferred = getNumber('HEALTH_PORT', 6565);
29
- if (preferred > 0) {
30
- return preferred;
31
- }
32
- // Use PID-scoped pseudo random port to avoid collisions on dev machines.
33
- const base = 45000;
34
- const span = 2000;
35
- const candidate = base + (process.pid % span);
36
- return candidate;
37
- }
38
-
39
- function isInContainer(): boolean {
40
- // Check for Docker container
41
- try {
42
- const fs = require('fs');
43
- if (fs.existsSync('/.dockerenv')) {
44
- return true;
45
- }
46
- } catch {
47
- // fs might not be available
48
- }
49
-
50
- // Check for Docker container hostname pattern (12 hex chars)
51
- const hostname = process.env.HOSTNAME || '';
52
- if (hostname && /^[a-f0-9]{12}$/i.test(hostname)) {
53
- return true;
54
- }
55
-
56
- // Explicit container indicators
57
- if (
58
- process.env.DOTNET_RUNNING_IN_CONTAINER === 'true' ||
59
- process.env.CONTAINER === 'beamable' ||
60
- !!process.env.ECS_CONTAINER_METADATA_URI ||
61
- !!process.env.KUBERNETES_SERVICE_HOST
62
- ) {
63
- return true;
64
- }
65
-
66
- return false;
67
- }
68
-
69
- function resolveRoutingKey(): string | undefined {
70
- const raw = process.env.NAME_PREFIX ?? process.env.ROUTING_KEY;
71
- if (raw && raw.trim().length > 0) {
72
- return raw.trim();
73
- }
74
-
75
- // If we're actually in a container, return undefined (deployed service)
76
- if (isInContainer()) {
77
- return undefined;
78
- }
79
-
80
- // Local development - always try to get a routing key
81
- try {
82
- return getDefaultRoutingKeyForMachine();
83
- } catch (error) {
84
- throw new Error(
85
- `Unable to determine routing key automatically. Set NAME_PREFIX environment variable. ${(error as Error).message}`,
86
- );
87
- }
88
- }
89
-
90
- function resolveSdkVersionExecution(): string {
91
- return process.env.BEAMABLE_SDK_VERSION_EXECUTION ?? '';
92
- }
93
-
94
- function resolveLogLevel(): string {
95
- const candidate = process.env.LOG_LEVEL ?? 'info';
96
- const allowed = new Set(['fatal', 'error', 'warn', 'info', 'debug', 'trace']);
97
- return allowed.has(candidate.toLowerCase()) ? candidate.toLowerCase() : 'info';
98
- }
99
-
100
- function resolveWatchToken(): boolean {
101
- const value = process.env.WATCH_TOKEN;
102
- if (value === undefined) {
103
- return false;
104
- }
105
- return getBoolean('WATCH_TOKEN', false);
106
- }
107
-
108
- function resolveBeamInstanceCount(): number {
109
- return getNumber('BEAM_INSTANCE_COUNT', 1);
110
- }
111
-
112
- export function loadEnvironmentConfig(): EnvironmentConfig {
113
- const cid = process.env.CID ?? '';
114
- const pid = process.env.PID ?? '';
115
- const host = process.env.HOST ?? '';
116
-
117
- if (!cid || !pid || !host) {
118
- throw new Error('Missing required Beamable environment variables (CID, PID, HOST).');
119
- }
120
-
121
- const config: EnvironmentConfig = {
122
- cid,
123
- pid,
124
- host,
125
- secret: process.env.SECRET ?? undefined,
126
- refreshToken: process.env.REFRESH_TOKEN ?? undefined,
127
- routingKey: resolveRoutingKey(),
128
- accountId: getNumber('USER_ACCOUNT_ID', 0) || undefined,
129
- accountEmail: process.env.USER_EMAIL ?? undefined,
130
- logLevel: resolveLogLevel(),
131
- healthPort: resolveHealthPort(),
132
- disableCustomInitializationHooks: getBoolean('DISABLE_CUSTOM_INITIALIZATION_HOOKS', false),
133
- watchToken: resolveWatchToken(),
134
- sdkVersionExecution: resolveSdkVersionExecution(),
135
- beamInstanceCount: resolveBeamInstanceCount(),
136
- logTruncateLimit: getNumber('LOG_TRUNCATE_LIMIT', 1000),
137
- };
138
-
139
- return config;
140
- }
141
-
142
- export function ensureWritableTempDirectory(): string {
143
- const candidate = process.env.LOG_PATH ?? join(tmpdir(), 'beamable-node-runtime');
144
- if (!existsSync(candidate)) {
145
- return candidate;
146
- }
147
- return candidate;
148
- }
package/src/errors.ts DELETED
@@ -1,55 +0,0 @@
1
- export class BeamableRuntimeError extends Error {
2
- public readonly code: string;
3
-
4
- constructor(code: string, message: string, cause?: unknown) {
5
- super(message);
6
- this.code = code;
7
- if (cause !== undefined) {
8
- (this as { cause?: unknown }).cause = cause;
9
- }
10
- this.name = this.constructor.name;
11
- Error.captureStackTrace?.(this, this.constructor);
12
- }
13
- }
14
-
15
- export class AuthenticationError extends BeamableRuntimeError {
16
- constructor(message: string, cause?: unknown) {
17
- super('AUTHENTICATION_FAILED', message, cause);
18
- }
19
- }
20
-
21
- export class TimeoutError extends BeamableRuntimeError {
22
- constructor(message: string, cause?: unknown) {
23
- super('TIMEOUT', message, cause);
24
- }
25
- }
26
-
27
- export class MissingScopesError extends BeamableRuntimeError {
28
- constructor(requiredScopes: Iterable<string>) {
29
- super('MISSING_SCOPES', `Missing required scopes: ${Array.from(requiredScopes).join(', ')}`);
30
- }
31
- }
32
-
33
- export class UnauthorizedUserError extends BeamableRuntimeError {
34
- constructor(route: string) {
35
- super('UNAUTHORIZED_USER', `Route "${route}" requires an authenticated user.`);
36
- }
37
- }
38
-
39
- export class UnknownRouteError extends BeamableRuntimeError {
40
- constructor(route: string) {
41
- super('UNKNOWN_ROUTE', `No callable registered for route "${route}".`);
42
- }
43
- }
44
-
45
- export class SerializationError extends BeamableRuntimeError {
46
- constructor(message: string, cause?: unknown) {
47
- super('SERIALIZATION_ERROR', message, cause);
48
- }
49
- }
50
-
51
- export class InvalidConfigurationError extends BeamableRuntimeError {
52
- constructor(message: string, cause?: unknown) {
53
- super('INVALID_CONFIGURATION', message, cause);
54
- }
55
- }