@omen.foundation/node-microservice-runtime 0.1.75 → 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.
- package/dist/auth.cjs +97 -0
- package/dist/cli/commands/scaffold.js +17 -1
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/collector-manager.cjs +957 -0
- package/dist/decorators.cjs +181 -0
- package/dist/dependency.cjs +165 -0
- package/dist/dev.cjs +34 -0
- package/dist/discovery.cjs +79 -0
- package/dist/docs.cjs +206 -0
- package/dist/env-loader.cjs +187 -0
- package/dist/env-loader.d.ts +7 -0
- package/dist/env-loader.d.ts.map +1 -1
- package/dist/env-loader.js +20 -0
- package/dist/env-loader.js.map +1 -1
- package/dist/env.cjs +125 -0
- package/dist/errors.cjs +58 -0
- package/dist/federation.cjs +356 -0
- package/dist/index.cjs +66 -0
- package/dist/inventory.cjs +361 -0
- package/dist/logger.cjs +545 -0
- package/dist/message.cjs +19 -0
- package/dist/requester.cjs +100 -0
- package/dist/routing.cjs +39 -0
- package/dist/runtime.cjs +841 -0
- package/dist/runtime.d.ts +13 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +74 -21
- package/dist/runtime.js.map +1 -1
- package/dist/services.cjs +356 -0
- package/dist/storage.cjs +147 -0
- package/dist/types.cjs +2 -0
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/urls.cjs +55 -0
- package/dist/websocket.cjs +142 -0
- package/package.json +1 -1
package/dist/runtime.d.ts
CHANGED
|
@@ -20,6 +20,13 @@ export declare class MicroserviceRuntime {
|
|
|
20
20
|
private handleEvent;
|
|
21
21
|
private toRequestContext;
|
|
22
22
|
private findServiceForPath;
|
|
23
|
+
/**
|
|
24
|
+
* Parses a query string into a record of parameter names to values.
|
|
25
|
+
* Handles multiple values for the same parameter name by storing them as an array.
|
|
26
|
+
* @param queryString The query string (without the leading '?')
|
|
27
|
+
* @returns A record mapping parameter names to their values (single string or array of strings)
|
|
28
|
+
*/
|
|
29
|
+
private parseQueryString;
|
|
23
30
|
private dispatch;
|
|
24
31
|
private buildInvocationArguments;
|
|
25
32
|
private sendSuccessResponse;
|
|
@@ -30,6 +37,12 @@ export declare class MicroserviceRuntime {
|
|
|
30
37
|
private printHelpfulUrls;
|
|
31
38
|
private initializeDependencyProviders;
|
|
32
39
|
private createRequestScope;
|
|
40
|
+
/**
|
|
41
|
+
* Extracts the path portion without the query string.
|
|
42
|
+
* @param path The full path potentially containing a query string
|
|
43
|
+
* @returns The path without the query string
|
|
44
|
+
*/
|
|
45
|
+
private getPathWithoutQuery;
|
|
33
46
|
}
|
|
34
47
|
export declare function runMicroservice(): Promise<void>;
|
|
35
48
|
interface GenerateOpenApiOptions {
|
package/dist/runtime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,iBAAiB,EAOlB,MAAM,YAAY,CAAC;AA2BpB,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoB;IACxC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoB;IAC9C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmB;IAC7C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAuB;IAClD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAgB;IAC/C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAyB;IACxD,OAAO,CAAC,iBAAiB,CAAC,CAAS;IACnC,OAAO,CAAC,OAAO,CAAkB;gBAErB,GAAG,CAAC,EAAE,iBAAiB;
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,iBAAiB,EAOlB,MAAM,YAAY,CAAC;AA2BpB,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoB;IACxC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoB;IAC9C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmB;IAC7C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAuB;IAClD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAgB;IAC/C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAyB;IACxD,OAAO,CAAC,iBAAiB,CAAC,CAAS;IACnC,OAAO,CAAC,OAAO,CAAkB;gBAErB,GAAG,CAAC,EAAE,iBAAiB;IA2H7B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA6DtB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;YASjB,sBAAsB;YA2CtB,qBAAqB;YAarB,eAAe;YA+Gf,WAAW;IA8BzB,OAAO,CAAC,gBAAgB;IAuFxB,OAAO,CAAC,kBAAkB;IAW1B;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;YA+BV,QAAQ;IAwDtB,OAAO,CAAC,wBAAwB;YAmBlB,mBAAmB;YAiBnB,sBAAsB;YAStB,wBAAwB;YAsBxB,mBAAmB;IA6GjC,OAAO,CAAC,kBAAkB;IAgB1B,OAAO,CAAC,gBAAgB;YA+BV,6BAA6B;IA6B3C,OAAO,CAAC,kBAAkB;IAO1B;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;CAI5B;AA+GD,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAoDrD;AAED,UAAU,sBAAsB;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,4CAA4C,CAC1D,SAAS,GAAE,OAAO,CAAC,iBAAiB,CAAM,EAC1C,OAAO,GAAE,sBAA2B,GACnC,OAAO,CA6BT"}
|
package/dist/runtime.js
CHANGED
|
@@ -4,7 +4,7 @@ import { GatewayRequester } from './requester.js';
|
|
|
4
4
|
import { AuthManager } from './auth.js';
|
|
5
5
|
import { createLogger } from './logger.js';
|
|
6
6
|
import { loadEnvironmentConfig } from './env.js';
|
|
7
|
-
import { loadAndInjectEnvironmentVariables } from './env-loader.js';
|
|
7
|
+
import { loadAndInjectEnvironmentVariables, loadDeveloperEnvVarsSync } from './env-loader.js';
|
|
8
8
|
import { startCollectorAndWaitForReady } from './collector-manager.js';
|
|
9
9
|
import pino from 'pino';
|
|
10
10
|
// Removed deasync - using non-blocking async pattern instead
|
|
@@ -33,7 +33,10 @@ export class MicroserviceRuntime {
|
|
|
33
33
|
constructor(env) {
|
|
34
34
|
this.env = env ?? loadEnvironmentConfig();
|
|
35
35
|
const envConfig = this.env; // Capture for async IIFE to satisfy TypeScript
|
|
36
|
-
// STEP 1:
|
|
36
|
+
// STEP 1: Load developer-defined environment variables SYNCHRONOUSLY before logger creation
|
|
37
|
+
// This ensures BetterStack and other env vars are available when the logger initializes
|
|
38
|
+
loadDeveloperEnvVarsSync();
|
|
39
|
+
// STEP 2: Create minimal console logger for startup messages (before collector setup)
|
|
37
40
|
// This ensures we have logging available immediately, even before collector is ready
|
|
38
41
|
const startupLogger = pino({
|
|
39
42
|
name: 'beamable-runtime-startup',
|
|
@@ -41,24 +44,19 @@ export class MicroserviceRuntime {
|
|
|
41
44
|
}, process.stdout);
|
|
42
45
|
// Display runtime version at startup (VERSION is imported synchronously at top of file)
|
|
43
46
|
startupLogger.info(`Starting Beamable Node microservice runtime (version: ${VERSION}).`);
|
|
44
|
-
// STEP
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
// Beamable Config is fetched via API with a 2-second timeout to avoid blocking startup
|
|
48
|
-
// Note: This is wrapped in an IIFE to allow await at the top level of constructor logic
|
|
49
|
-
// We use a fire-and-forget pattern: load vars but don't block service initialization
|
|
47
|
+
// STEP 2.5: Load Beamable Config asynchronously (in background, non-blocking)
|
|
48
|
+
// Developer-defined vars are already loaded synchronously above
|
|
49
|
+
// Beamable Config is fetched via API with a 2-second timeout
|
|
50
50
|
(async () => {
|
|
51
51
|
try {
|
|
52
|
-
startupLogger.info('Loading environment variables
|
|
52
|
+
startupLogger.info('Loading Beamable Config environment variables...');
|
|
53
53
|
await loadAndInjectEnvironmentVariables(envConfig, undefined, true, 2000);
|
|
54
|
-
startupLogger.info('
|
|
54
|
+
startupLogger.info('Beamable Config loading completed');
|
|
55
55
|
}
|
|
56
56
|
catch (error) {
|
|
57
|
-
startupLogger.warn(`
|
|
57
|
+
startupLogger.warn(`Beamable Config loading completed with warnings: ${error instanceof Error ? error.message : String(error)}`);
|
|
58
58
|
}
|
|
59
59
|
})();
|
|
60
|
-
// Continue immediately - env vars will be available as they load
|
|
61
|
-
// Developer vars are already injected, Beamable Config will arrive within 2 seconds
|
|
62
60
|
// STEP 2: Get registered services to extract service name
|
|
63
61
|
const registered = listRegisteredServices();
|
|
64
62
|
if (registered.length === 0) {
|
|
@@ -386,6 +384,9 @@ export class MicroserviceRuntime {
|
|
|
386
384
|
const method = envelope.method ?? 'post';
|
|
387
385
|
const userId = typeof envelope.from === 'number' ? envelope.from : 0;
|
|
388
386
|
const headers = envelope.headers ?? {};
|
|
387
|
+
// Parse query parameters from path (e.g., /service/route?param1=value1¶m2=value2)
|
|
388
|
+
const [pathWithoutQuery, queryString] = path.split('?', 2);
|
|
389
|
+
const query = this.parseQueryString(queryString ?? '');
|
|
389
390
|
// Extract scopes from envelope.scopes array
|
|
390
391
|
// Note: X-DE-SCOPE header contains CID.PID, not scope values
|
|
391
392
|
// The gateway sends scopes in envelope.scopes array
|
|
@@ -395,7 +396,7 @@ export class MicroserviceRuntime {
|
|
|
395
396
|
let scopes = normalizeScopes(envelopeScopes);
|
|
396
397
|
// If this is an admin endpoint and no scopes are provided, infer admin scope
|
|
397
398
|
// The gateway may not always send scopes for admin routes
|
|
398
|
-
const pathLower =
|
|
399
|
+
const pathLower = pathWithoutQuery.toLowerCase();
|
|
399
400
|
if (pathLower.includes('/admin/') && scopes.size === 0) {
|
|
400
401
|
scopes = normalizeScopes(['admin']);
|
|
401
402
|
}
|
|
@@ -427,9 +428,9 @@ export class MicroserviceRuntime {
|
|
|
427
428
|
payload = rawPayload;
|
|
428
429
|
}
|
|
429
430
|
}
|
|
430
|
-
const targetService = this.findServiceForPath(
|
|
431
|
+
const targetService = this.findServiceForPath(pathWithoutQuery);
|
|
431
432
|
const services = this.serviceManager.createFacade(userId, scopes, targetService?.definition.name);
|
|
432
|
-
const provider = this.createRequestScope(
|
|
433
|
+
const provider = this.createRequestScope(pathWithoutQuery, targetService);
|
|
433
434
|
const context = {
|
|
434
435
|
id: envelope.id,
|
|
435
436
|
path,
|
|
@@ -438,6 +439,7 @@ export class MicroserviceRuntime {
|
|
|
438
439
|
userId,
|
|
439
440
|
payload,
|
|
440
441
|
body,
|
|
442
|
+
query,
|
|
441
443
|
scopes,
|
|
442
444
|
headers,
|
|
443
445
|
cid: this.env.cid,
|
|
@@ -461,14 +463,51 @@ export class MicroserviceRuntime {
|
|
|
461
463
|
findServiceForPath(path) {
|
|
462
464
|
// Gateway sends paths with lowercase service names, so we need case-insensitive matching
|
|
463
465
|
// Match by comparing lowercase versions to handle gateway's lowercase path format
|
|
466
|
+
// Note: path should already have query string stripped before calling this method
|
|
464
467
|
const pathLower = path.toLowerCase();
|
|
465
468
|
return this.services.find((service) => {
|
|
466
469
|
const qualifiedNameLower = service.definition.qualifiedName.toLowerCase();
|
|
467
470
|
return pathLower.startsWith(`${qualifiedNameLower}/`);
|
|
468
471
|
});
|
|
469
472
|
}
|
|
473
|
+
/**
|
|
474
|
+
* Parses a query string into a record of parameter names to values.
|
|
475
|
+
* Handles multiple values for the same parameter name by storing them as an array.
|
|
476
|
+
* @param queryString The query string (without the leading '?')
|
|
477
|
+
* @returns A record mapping parameter names to their values (single string or array of strings)
|
|
478
|
+
*/
|
|
479
|
+
parseQueryString(queryString) {
|
|
480
|
+
if (!queryString || queryString.trim().length === 0) {
|
|
481
|
+
return {};
|
|
482
|
+
}
|
|
483
|
+
const params = {};
|
|
484
|
+
const pairs = queryString.split('&');
|
|
485
|
+
for (const pair of pairs) {
|
|
486
|
+
const [key, value = ''] = pair.split('=', 2);
|
|
487
|
+
if (key) {
|
|
488
|
+
const decodedKey = decodeURIComponent(key);
|
|
489
|
+
const decodedValue = decodeURIComponent(value);
|
|
490
|
+
// If the key already exists, convert to array or append to existing array
|
|
491
|
+
if (decodedKey in params) {
|
|
492
|
+
const existing = params[decodedKey];
|
|
493
|
+
if (Array.isArray(existing)) {
|
|
494
|
+
existing.push(decodedValue);
|
|
495
|
+
}
|
|
496
|
+
else {
|
|
497
|
+
params[decodedKey] = [existing, decodedValue];
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
else {
|
|
501
|
+
params[decodedKey] = decodedValue;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
return params;
|
|
506
|
+
}
|
|
470
507
|
async dispatch(ctx) {
|
|
471
|
-
|
|
508
|
+
// Extract path without query string for routing
|
|
509
|
+
const pathWithoutQuery = this.getPathWithoutQuery(ctx.path);
|
|
510
|
+
const service = this.findServiceForPath(pathWithoutQuery);
|
|
472
511
|
if (!service) {
|
|
473
512
|
throw new UnknownRouteError(ctx.path);
|
|
474
513
|
}
|
|
@@ -479,7 +518,7 @@ export class MicroserviceRuntime {
|
|
|
479
518
|
return;
|
|
480
519
|
}
|
|
481
520
|
// Extract route from path - handle case-insensitive path matching
|
|
482
|
-
const pathLower =
|
|
521
|
+
const pathLower = pathWithoutQuery.toLowerCase();
|
|
483
522
|
const qualifiedNameLower = service.definition.qualifiedName.toLowerCase();
|
|
484
523
|
const route = pathLower.substring(qualifiedNameLower.length + 1);
|
|
485
524
|
const metadata = service.definition.callables.get(route);
|
|
@@ -554,14 +593,16 @@ export class MicroserviceRuntime {
|
|
|
554
593
|
}
|
|
555
594
|
async tryHandleFederationRoute(ctx, service) {
|
|
556
595
|
// Gateway sends paths with lowercase service names, so we need case-insensitive matching
|
|
557
|
-
|
|
596
|
+
// Extract path without query string for routing
|
|
597
|
+
const pathWithoutQuery = this.getPathWithoutQuery(ctx.path);
|
|
598
|
+
const pathLower = pathWithoutQuery.toLowerCase();
|
|
558
599
|
const qualifiedNameLower = service.definition.qualifiedName.toLowerCase();
|
|
559
600
|
const prefixLower = `${qualifiedNameLower}/`;
|
|
560
601
|
if (!pathLower.startsWith(prefixLower)) {
|
|
561
602
|
return false;
|
|
562
603
|
}
|
|
563
604
|
// Extract relative path - use lowercase length since gateway sends lowercase paths
|
|
564
|
-
const relativePath =
|
|
605
|
+
const relativePath = pathWithoutQuery.substring(qualifiedNameLower.length + 1);
|
|
565
606
|
const handler = service.federationRegistry.resolve(relativePath);
|
|
566
607
|
if (!handler) {
|
|
567
608
|
return false;
|
|
@@ -573,7 +614,9 @@ export class MicroserviceRuntime {
|
|
|
573
614
|
async tryHandleAdminRoute(ctx, service) {
|
|
574
615
|
// Gateway sends paths with lowercase service names, so we need case-insensitive matching
|
|
575
616
|
// Check if path starts with the admin prefix (case-insensitive)
|
|
576
|
-
|
|
617
|
+
// Extract path without query string for routing
|
|
618
|
+
const pathWithoutQuery = this.getPathWithoutQuery(ctx.path);
|
|
619
|
+
const pathLower = pathWithoutQuery.toLowerCase();
|
|
577
620
|
const adminPrefixLower = `${service.definition.qualifiedName.toLowerCase()}/admin/`;
|
|
578
621
|
if (!pathLower.startsWith(adminPrefixLower)) {
|
|
579
622
|
return false;
|
|
@@ -730,10 +773,20 @@ export class MicroserviceRuntime {
|
|
|
730
773
|
}
|
|
731
774
|
}
|
|
732
775
|
createRequestScope(path, service) {
|
|
776
|
+
// Path should already have query string stripped before calling this method
|
|
733
777
|
const targetService = service ?? this.findServiceForPath(path);
|
|
734
778
|
const provider = targetService?.provider ?? new DependencyBuilder().build();
|
|
735
779
|
return provider.createScope();
|
|
736
780
|
}
|
|
781
|
+
/**
|
|
782
|
+
* Extracts the path portion without the query string.
|
|
783
|
+
* @param path The full path potentially containing a query string
|
|
784
|
+
* @returns The path without the query string
|
|
785
|
+
*/
|
|
786
|
+
getPathWithoutQuery(path) {
|
|
787
|
+
const [pathWithoutQuery] = path.split('?', 2);
|
|
788
|
+
return pathWithoutQuery;
|
|
789
|
+
}
|
|
737
790
|
}
|
|
738
791
|
function enforceAccess(access, ctx) {
|
|
739
792
|
switch (access) {
|