@omen.foundation/node-microservice-runtime 0.1.65 → 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.
package/src/federation.ts DELETED
@@ -1,559 +0,0 @@
1
- import type { Logger } from 'pino';
2
- import type { RequestContext } from './types.js';
3
-
4
- export interface FederationComponentDescriptor {
5
- federationType: string;
6
- federationNamespace: string;
7
- localSettings?: unknown;
8
- }
9
-
10
- export interface FederationMetadata {
11
- typeName: string;
12
- identity: string;
13
- localSettings?: unknown;
14
- }
15
-
16
- export interface FederationIdentity {
17
- getUniqueName(): string;
18
- }
19
-
20
- export type FederationIdentityConstructor<T extends FederationIdentity = FederationIdentity> = new () => T;
21
-
22
- export interface FederatedAuthenticationRequest {
23
- token?: string;
24
- challenge?: string;
25
- solution?: string;
26
- }
27
-
28
- export interface FederatedAuthenticationResponse {
29
- user_id?: string;
30
- challenge?: string;
31
- challenge_ttl?: number;
32
- [key: string]: unknown;
33
- }
34
-
35
- export interface FederatedItemProperty {
36
- name: string;
37
- value: string;
38
- }
39
-
40
- export interface FederatedItemProxy {
41
- proxyId: string;
42
- properties?: FederatedItemProperty[];
43
- }
44
-
45
- export interface FederatedInventoryProxyState {
46
- currencies?: Record<string, number>;
47
- items?: Record<string, FederatedItemProxy[]>;
48
- }
49
-
50
- export interface FederatedItemCreateRequest {
51
- contentId: string;
52
- properties?: Record<string, string>;
53
- requestId?: string;
54
- }
55
-
56
- export interface FederatedItemDeleteRequest {
57
- contentId: string;
58
- proxyId: string;
59
- }
60
-
61
- export interface FederatedItemUpdateRequest {
62
- contentId: string;
63
- proxyId: string;
64
- properties?: Record<string, string>;
65
- }
66
-
67
- export interface FederatedInventoryStateRequest {
68
- id: string;
69
- }
70
-
71
- export interface FederatedInventoryTransactionRequest {
72
- id: string;
73
- transaction: string;
74
- currencies?: Record<string, number>;
75
- newItems?: FederatedItemCreateRequest[];
76
- deleteItems?: FederatedItemDeleteRequest[];
77
- updateItems?: FederatedItemUpdateRequest[];
78
- }
79
-
80
- export interface FederatedInventoryOptions<T extends FederationIdentity> {
81
- identity: FederationIdentityConstructor<T>;
82
- authenticate?: string;
83
- getInventoryState?: string;
84
- startInventoryTransaction?: string;
85
- }
86
-
87
- interface FederatedInventoryMetadata {
88
- identityCtor: FederationIdentityConstructor;
89
- identityName: string;
90
- methods: {
91
- authenticate: string;
92
- getInventoryState: string;
93
- startInventoryTransaction: string;
94
- };
95
- }
96
-
97
- interface FederationRouteHandler {
98
- identityName: string;
99
- type: 'authenticate' | 'inventory-state' | 'inventory-transaction';
100
- invoke(ctx: FederatedRequestContext): Promise<unknown>;
101
- }
102
-
103
- export interface FederatedRequestContext extends RequestContext {
104
- federation: {
105
- identity: string;
106
- };
107
- }
108
-
109
- const FEDERATION_COMPONENTS = new Map<Function, FederationComponentDescriptor[]>();
110
- const FEDERATED_INVENTORY_MAP = new Map<Function, FederatedInventoryMetadata[]>();
111
-
112
- export function FederationComponent(descriptor: FederationComponentDescriptor): ClassDecorator {
113
- validateDescriptor(descriptor);
114
-
115
- return (target) => {
116
- const existing = FEDERATION_COMPONENTS.get(target) ?? [];
117
- existing.push({
118
- federationNamespace: descriptor.federationNamespace,
119
- federationType: descriptor.federationType,
120
- localSettings: descriptor.localSettings,
121
- });
122
- FEDERATION_COMPONENTS.set(target, existing);
123
- };
124
- }
125
-
126
- export function getFederationComponents(target: Function): FederationComponentDescriptor[] {
127
- return FEDERATION_COMPONENTS.get(target) ?? [];
128
- }
129
-
130
- export function FederatedInventory<T extends FederationIdentity>(
131
- options: FederatedInventoryOptions<T>,
132
- ): ClassDecorator {
133
- const authenticate = options.authenticate ?? 'Authenticate';
134
- const getInventoryState = options.getInventoryState ?? 'GetInventoryState';
135
- const startInventoryTransaction = options.startInventoryTransaction ?? 'StartInventoryTransaction';
136
-
137
- return (target) => {
138
- const identityInstance = new options.identity();
139
- if (!identityInstance || typeof identityInstance.getUniqueName !== 'function') {
140
- throw new Error('@FederatedInventory identity must implement getUniqueName().');
141
- }
142
- const identityName = identityInstance.getUniqueName();
143
- if (!identityName || !identityName.trim()) {
144
- throw new Error('@FederatedInventory identity must provide a non-empty unique name.');
145
- }
146
-
147
- const existing = FEDERATED_INVENTORY_MAP.get(target) ?? [];
148
- existing.push({
149
- identityCtor: options.identity,
150
- identityName: identityName.trim(),
151
- methods: {
152
- authenticate,
153
- getInventoryState,
154
- startInventoryTransaction,
155
- },
156
- });
157
- FEDERATED_INVENTORY_MAP.set(target, existing);
158
- };
159
- }
160
-
161
- export function getFederatedInventoryMetadata(target: Function): FederatedInventoryMetadata[] {
162
- return FEDERATED_INVENTORY_MAP.get(target) ?? [];
163
- }
164
-
165
- function validateDescriptor(descriptor: FederationComponentDescriptor): void {
166
- if (!descriptor.federationNamespace || !descriptor.federationNamespace.trim()) {
167
- throw new Error('@FederationComponent requires a non-empty federationNamespace.');
168
- }
169
- if (!descriptor.federationType || !descriptor.federationType.trim()) {
170
- throw new Error('@FederationComponent requires a non-empty federationType.');
171
- }
172
- }
173
-
174
- export class FederationRegistry {
175
- private readonly logger: Logger;
176
- private readonly manualComponents = new Map<string, Set<string>>();
177
- private readonly autoComponents = new Map<string, Set<string>>();
178
- private readonly routeHandlers = new Map<string, FederationRouteHandler>();
179
-
180
- constructor(logger: Logger) {
181
- this.logger = logger.child({ component: 'FederationRegistry' });
182
- }
183
-
184
- register(component: FederationComponentDescriptor): void {
185
- validateDescriptor(component);
186
- const namespace = component.federationNamespace.trim();
187
- const type = component.federationType.trim();
188
- const existing = this.manualComponents.get(namespace) ?? new Set<string>();
189
- existing.add(type);
190
- this.manualComponents.set(namespace, existing);
191
- this.logger.debug({ component }, 'Registered federation component.');
192
- }
193
-
194
- registerInventoryHandlers(service: object, metadata: FederatedInventoryMetadata[]): void {
195
- for (const entry of metadata) {
196
- const identity = entry.identityName;
197
- this.addAutoComponent(identity, 'IFederatedInventory');
198
- this.addAutoComponent(identity, 'IFederatedLogin');
199
-
200
- const authenticateHandler = this.createInventoryHandler(
201
- service,
202
- entry.methods.authenticate,
203
- identity,
204
- 'authenticate',
205
- );
206
- const stateHandler = this.createInventoryHandler(
207
- service,
208
- entry.methods.getInventoryState,
209
- identity,
210
- 'inventory-state',
211
- );
212
- const transactionHandler = this.createInventoryHandler(
213
- service,
214
- entry.methods.startInventoryTransaction,
215
- identity,
216
- 'inventory-transaction',
217
- );
218
-
219
- this.routeHandlers.set(`${identity.toLowerCase()}/authenticate`, authenticateHandler);
220
- this.routeHandlers.set(`${identity.toLowerCase()}/inventory/state`, stateHandler);
221
- this.routeHandlers.set(`${identity.toLowerCase()}/inventory/put`, transactionHandler);
222
- }
223
- }
224
-
225
- list(): FederationComponentDescriptor[] {
226
- const components: FederationComponentDescriptor[] = [];
227
- for (const [namespace, types] of this.manualComponents.entries()) {
228
- for (const type of types) {
229
- components.push({ federationNamespace: namespace, federationType: type });
230
- }
231
- }
232
- for (const [namespace, types] of this.autoComponents.entries()) {
233
- for (const type of types) {
234
- components.push({ federationNamespace: namespace, federationType: type });
235
- }
236
- }
237
- return components;
238
- }
239
-
240
- resolve(path: string): FederationRouteHandler | undefined {
241
- return this.routeHandlers.get(path.toLowerCase());
242
- }
243
-
244
- private addAutoComponent(namespace: string, type: string): void {
245
- const existing = this.autoComponents.get(namespace) ?? new Set<string>();
246
- existing.add(type);
247
- this.autoComponents.set(namespace, existing);
248
- }
249
-
250
- private createInventoryHandler(
251
- service: object,
252
- methodName: string,
253
- identity: string,
254
- type: FederationRouteHandler['type'],
255
- ): FederationRouteHandler {
256
- const method = (service as Record<string, unknown>)[methodName];
257
- if (typeof method !== 'function') {
258
- throw new Error(
259
- `Federated inventory method "${methodName}" is not defined on service "${service.constructor.name}".`,
260
- );
261
- }
262
- const paramTypes = getMethodParameterTypes(service, methodName);
263
-
264
- return {
265
- identityName: identity,
266
- type,
267
- invoke: async (ctx: FederatedRequestContext) => {
268
- const federatedCtx: FederatedRequestContext = {
269
- ...ctx,
270
- federation: { identity },
271
- };
272
- switch (type) {
273
- case 'authenticate':
274
- return invokeFederatedAuthenticate(
275
- method as (...args: unknown[]) => unknown,
276
- service,
277
- federatedCtx,
278
- normalizeAuthenticationRequest(ctx.body),
279
- paramTypes,
280
- );
281
- case 'inventory-state':
282
- return invokeFederatedGetInventoryState(
283
- method as (...args: unknown[]) => unknown,
284
- service,
285
- federatedCtx,
286
- normalizeInventoryStateRequest(ctx.body),
287
- paramTypes,
288
- );
289
- case 'inventory-transaction':
290
- return invokeFederatedStartInventoryTransaction(
291
- method as (...args: unknown[]) => unknown,
292
- service,
293
- federatedCtx,
294
- normalizeInventoryTransactionRequest(ctx.body),
295
- paramTypes,
296
- );
297
- default:
298
- throw new Error(`Unsupported federated handler type: ${type as string}`);
299
- }
300
- },
301
- };
302
- }
303
- }
304
-
305
- function normalizeAuthenticationRequest(payload: unknown): FederatedAuthenticationRequest {
306
- if (!payload || typeof payload !== 'object') {
307
- return {};
308
- }
309
- const body = payload as Record<string, unknown>;
310
- return {
311
- token: typeof body.token === 'string' ? body.token : undefined,
312
- challenge: typeof body.challenge === 'string' ? body.challenge : undefined,
313
- solution: typeof body.solution === 'string' ? body.solution : undefined,
314
- };
315
- }
316
-
317
- function normalizeInventoryStateRequest(payload: unknown): FederatedInventoryStateRequest {
318
- if (!payload || typeof payload !== 'object' || typeof (payload as Record<string, unknown>).id !== 'string') {
319
- throw new Error('Federated inventory state requests must include an "id" property.');
320
- }
321
- const body = payload as Record<string, unknown>;
322
- return { id: String(body.id) };
323
- }
324
-
325
- function normalizeInventoryTransactionRequest(payload: unknown): FederatedInventoryTransactionRequest {
326
- if (!payload || typeof payload !== 'object') {
327
- throw new Error('Federated inventory transaction requests require a JSON body.');
328
- }
329
- const body = payload as Record<string, unknown>;
330
- if (typeof body.id !== 'string') {
331
- throw new Error('Federated inventory transaction requests must include an "id" property.');
332
- }
333
- if (typeof body.transaction !== 'string') {
334
- throw new Error('Federated inventory transaction requests must include a "transaction" property.');
335
- }
336
-
337
- return {
338
- id: body.id,
339
- transaction: body.transaction,
340
- currencies: normalizeRecordOfNumbers(body.currencies),
341
- newItems: normalizeFederatedCreateRequests(body.newItems),
342
- deleteItems: normalizeFederatedDeleteRequests(body.deleteItems),
343
- updateItems: normalizeFederatedUpdateRequests(body.updateItems),
344
- };
345
- }
346
-
347
- function normalizeRecordOfNumbers(value: unknown): Record<string, number> | undefined {
348
- if (!value || typeof value !== 'object') {
349
- return undefined;
350
- }
351
- const record: Record<string, number> = {};
352
- for (const [key, raw] of Object.entries(value as Record<string, unknown>)) {
353
- const numeric = Number(raw);
354
- if (Number.isFinite(numeric)) {
355
- record[key] = numeric;
356
- }
357
- }
358
- return Object.keys(record).length > 0 ? record : undefined;
359
- }
360
-
361
- function normalizeFederatedCreateRequests(value: unknown): FederatedItemCreateRequest[] | undefined {
362
- if (!Array.isArray(value)) {
363
- return undefined;
364
- }
365
- const normalized: FederatedItemCreateRequest[] = [];
366
- for (const item of value) {
367
- if (!item || typeof item !== 'object') {
368
- continue;
369
- }
370
- const record = item as Record<string, unknown>;
371
- if (typeof record.contentId !== 'string') {
372
- continue;
373
- }
374
- const properties = record.properties && typeof record.properties === 'object'
375
- ? Object.fromEntries(
376
- Object.entries(record.properties as Record<string, unknown>).map(([propKey, propVal]) => [
377
- propKey,
378
- String(propVal),
379
- ]),
380
- )
381
- : undefined;
382
- normalized.push({
383
- contentId: record.contentId,
384
- requestId: typeof record.requestId === 'string' ? record.requestId : undefined,
385
- properties,
386
- });
387
- }
388
- return normalized.length > 0 ? normalized : undefined;
389
- }
390
-
391
- function normalizeFederatedDeleteRequests(value: unknown): FederatedItemDeleteRequest[] | undefined {
392
- if (!Array.isArray(value)) {
393
- return undefined;
394
- }
395
- const normalized: FederatedItemDeleteRequest[] = [];
396
- for (const item of value) {
397
- if (!item || typeof item !== 'object') {
398
- continue;
399
- }
400
- const record = item as Record<string, unknown>;
401
- if (typeof record.contentId !== 'string' || typeof record.proxyId !== 'string') {
402
- continue;
403
- }
404
- normalized.push({
405
- contentId: record.contentId,
406
- proxyId: record.proxyId,
407
- });
408
- }
409
- return normalized.length > 0 ? normalized : undefined;
410
- }
411
-
412
- function normalizeFederatedUpdateRequests(value: unknown): FederatedItemUpdateRequest[] | undefined {
413
- if (!Array.isArray(value)) {
414
- return undefined;
415
- }
416
- const normalized: FederatedItemUpdateRequest[] = [];
417
- for (const item of value) {
418
- if (!item || typeof item !== 'object') {
419
- continue;
420
- }
421
- const record = item as Record<string, unknown>;
422
- if (typeof record.contentId !== 'string' || typeof record.proxyId !== 'string') {
423
- continue;
424
- }
425
- const properties = record.properties && typeof record.properties === 'object'
426
- ? Object.fromEntries(
427
- Object.entries(record.properties as Record<string, unknown>).map(([propKey, propVal]) => [
428
- propKey,
429
- String(propVal),
430
- ]),
431
- )
432
- : undefined;
433
- normalized.push({
434
- contentId: record.contentId,
435
- proxyId: record.proxyId,
436
- properties,
437
- });
438
- }
439
- return normalized.length > 0 ? normalized : undefined;
440
- }
441
-
442
- function getMethodParameterTypes(service: object, methodName: string): unknown[] {
443
- const prototype = Object.getPrototypeOf(service);
444
- if (!prototype || typeof Reflect.getMetadata !== 'function') {
445
- return [];
446
- }
447
- return (Reflect.getMetadata('design:paramtypes', prototype, methodName) as unknown[]) ?? [];
448
- }
449
-
450
- function isStringType(type: unknown): boolean {
451
- return type === String;
452
- }
453
-
454
- function invokeFederatedAuthenticate(
455
- method: (...args: unknown[]) => unknown,
456
- service: object,
457
- ctx: FederatedRequestContext,
458
- request: FederatedAuthenticationRequest,
459
- paramTypes: unknown[],
460
- ): unknown {
461
- const argsWithoutContext = [request.token, request.challenge, request.solution];
462
-
463
- if (method.length === argsWithoutContext.length) {
464
- return method.call(service, ...argsWithoutContext);
465
- }
466
-
467
- if (method.length === argsWithoutContext.length + 1) {
468
- return method.call(service, ctx, ...argsWithoutContext);
469
- }
470
-
471
- if (method.length === 1) {
472
- const firstType = paramTypes[0];
473
- if (isStringType(firstType)) {
474
- return method.call(service, ...argsWithoutContext);
475
- }
476
- return method.call(service, request);
477
- }
478
-
479
- if (method.length === 2) {
480
- const secondType = paramTypes[1];
481
- if (isStringType(secondType)) {
482
- return method.call(service, ctx, ...argsWithoutContext);
483
- }
484
- return method.call(service, ctx, request);
485
- }
486
-
487
- return method.call(service, ctx, request);
488
- }
489
-
490
- function invokeFederatedGetInventoryState(
491
- method: (...args: unknown[]) => unknown,
492
- service: object,
493
- ctx: FederatedRequestContext,
494
- request: FederatedInventoryStateRequest,
495
- paramTypes: unknown[],
496
- ): unknown {
497
- const argsWithoutContext = [request.id];
498
-
499
- if (method.length === argsWithoutContext.length) {
500
- const firstType = paramTypes[0];
501
- if (isStringType(firstType)) {
502
- return method.call(service, ...argsWithoutContext);
503
- }
504
- return method.call(service, request);
505
- }
506
-
507
- if (method.length === argsWithoutContext.length + 1) {
508
- const secondType = paramTypes[1];
509
- if (isStringType(secondType)) {
510
- return method.call(service, ctx, ...argsWithoutContext);
511
- }
512
- return method.call(service, ctx, request);
513
- }
514
-
515
- if (method.length === 1) {
516
- return method.call(service, request);
517
- }
518
-
519
- if (method.length === 2) {
520
- return method.call(service, ctx, request);
521
- }
522
-
523
- return method.call(service, ctx, request);
524
- }
525
-
526
- function invokeFederatedStartInventoryTransaction(
527
- method: (...args: unknown[]) => unknown,
528
- service: object,
529
- ctx: FederatedRequestContext,
530
- request: FederatedInventoryTransactionRequest,
531
- _paramTypes: unknown[],
532
- ): unknown {
533
- const argsWithoutContext = [
534
- request.id,
535
- request.transaction,
536
- request.currencies ?? {},
537
- request.newItems ?? [],
538
- request.deleteItems ?? [],
539
- request.updateItems ?? [],
540
- ];
541
-
542
- if (method.length === argsWithoutContext.length) {
543
- return method.call(service, ...argsWithoutContext);
544
- }
545
-
546
- if (method.length === argsWithoutContext.length + 1) {
547
- return method.call(service, ctx, ...argsWithoutContext);
548
- }
549
-
550
- if (method.length === 1) {
551
- return method.call(service, request);
552
- }
553
-
554
- if (method.length === 2) {
555
- return method.call(service, ctx, request);
556
- }
557
-
558
- return method.call(service, ctx, request);
559
- }
package/src/index.ts DELETED
@@ -1,84 +0,0 @@
1
- export {
2
- Microservice,
3
- Callable,
4
- ClientCallable,
5
- ServerCallable,
6
- AdminCallable,
7
- SwaggerCategory,
8
- SwaggerTags,
9
- ConfigureServices,
10
- InitializeServices,
11
- } from './decorators.js';
12
- export type { MicroserviceOptions, CallableOptions, ServiceAccess } from './types.js';
13
- export type { EnvironmentConfig, RequestContext, ServiceDefinition } from './types.js';
14
- export type { BeamableMicroserviceServices, BoundBeamApi } from './services.js';
15
- export { BeamableServiceManager } from './services.js';
16
- export {
17
- DependencyBuilder,
18
- type DependencyScope,
19
- ServiceLifetime,
20
- LOGGER_TOKEN,
21
- ENVIRONMENT_CONFIG_TOKEN,
22
- REQUEST_CONTEXT_TOKEN,
23
- BEAMABLE_SERVICES_TOKEN,
24
- } from './dependency.js';
25
- export { loadEnvironmentConfig } from './env.js';
26
- export { BeamableRuntimeError } from './errors.js';
27
- export { MicroserviceRuntime, runMicroservice } from './runtime.js';
28
- export {
29
- InventoryUpdateBuilder,
30
- createInventoryService,
31
- type InventoryUpdatePayload,
32
- type InventoryBuilderOptions,
33
- } from './inventory.js';
34
- export { StorageService, StorageObject, getStorageMetadata, listRegisteredStorageObjects } from './storage.js';
35
- export {
36
- FederationComponent,
37
- FederationRegistry,
38
- getFederationComponents,
39
- FederatedInventory,
40
- type FederationIdentity,
41
- type FederatedAuthenticationRequest,
42
- type FederatedAuthenticationResponse,
43
- type FederatedInventoryProxyState,
44
- type FederatedInventoryStateRequest,
45
- type FederatedInventoryTransactionRequest,
46
- type FederatedItemCreateRequest,
47
- type FederatedItemDeleteRequest,
48
- type FederatedItemUpdateRequest,
49
- type FederatedItemProperty,
50
- type FederatedItemProxy,
51
- } from './federation.js';
52
- export {
53
- isCollectorRunning,
54
- getClickHouseCredentialsStatus,
55
- fetchClickHouseCredentials,
56
- addAuthEnvironmentVars,
57
- getCollectorProcessStatus,
58
- setupCollectorBeforeLogging,
59
- startCollectorAsync,
60
- startCollectorAndWaitForReady,
61
- type CollectorStatus,
62
- } from './collector-manager.js';
63
-
64
- // Export runtime version - read from package.json dynamically
65
- import { readFileSync } from 'fs';
66
- import { createRequire } from 'module';
67
-
68
- function getPackageVersion(): string {
69
- try {
70
- // Use createRequire to get a require function that works in both ESM and CJS
71
- // This allows us to resolve the package.json path relative to this module
72
- // @ts-ignore - import.meta is ESM-only, TypeScript error in CJS is expected
73
- const require = createRequire(import.meta.url);
74
- const packageJsonPath = require.resolve('../package.json');
75
- const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
76
- return packageJson.version || '0.0.0';
77
- } catch (error) {
78
- // Fallback if package.json can't be read
79
- console.warn('[Runtime] Failed to read version from package.json, using fallback:', error instanceof Error ? error.message : String(error));
80
- return '0.0.0';
81
- }
82
- }
83
-
84
- export const VERSION = getPackageVersion();