@common-stack/store-mongo 8.0.2-alpha.0 → 8.1.1-alpha.13

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 (77) hide show
  1. package/LICENSE +34 -21
  2. package/lib/containers/container.d.ts +14 -0
  3. package/lib/containers/container.js +17 -0
  4. package/lib/containers/container.js.map +1 -0
  5. package/lib/containers/index.d.ts +1 -0
  6. package/lib/dataloaders/bulk-dataloader-v2.d.ts +40 -0
  7. package/lib/dataloaders/bulk-dataloader-v2.js +118 -0
  8. package/lib/dataloaders/bulk-dataloader-v2.js.map +1 -0
  9. package/lib/dataloaders/bulk-dataloader-v2.test.d.ts +1 -0
  10. package/lib/dataloaders/bulk-dataloader.d.ts +1 -1
  11. package/lib/dataloaders/bulk-dataloader.js.map +1 -1
  12. package/lib/dataloaders/bulk-dataloader.test.d.ts +1 -0
  13. package/lib/dataloaders/index.d.ts +1 -0
  14. package/lib/graphql/schema/base-services.graphql +238 -0
  15. package/lib/helpers/mongoose-connection.js.map +1 -1
  16. package/lib/index.d.ts +2 -1
  17. package/lib/index.js +1 -1
  18. package/lib/interfaces/base-repository.d.ts +1 -5
  19. package/lib/interfaces/base-service.d.ts +1 -1
  20. package/lib/interfaces/getAllArgs.d.ts +135 -0
  21. package/lib/interfaces/getAllArgs.js +39 -0
  22. package/lib/interfaces/getAllArgs.js.map +1 -0
  23. package/lib/interfaces/index.d.ts +1 -4
  24. package/lib/interfaces/index.old.d.ts +3 -0
  25. package/lib/mixins/BaseServiceMixin.d.ts +45 -0
  26. package/lib/mixins/BaseServiceMixin.js +212 -0
  27. package/lib/mixins/BaseServiceMixin.js.map +1 -0
  28. package/lib/mixins/__tests__/BaseServiceMixin.test.d.ts +1 -0
  29. package/lib/mixins/base-service-mixin.js.map +1 -1
  30. package/lib/mixins/index.d.ts +1 -0
  31. package/lib/module.d.ts +2 -0
  32. package/lib/module.js +4 -0
  33. package/lib/module.js.map +1 -0
  34. package/lib/services/BaseProxyService.d.ts +28 -0
  35. package/lib/services/BaseProxyService.js +52 -0
  36. package/lib/services/BaseProxyService.js.map +1 -0
  37. package/lib/services/BaseService.d.ts +23 -0
  38. package/lib/services/BaseService.js +65 -0
  39. package/lib/services/BaseService.js.map +1 -0
  40. package/lib/services/BaseService.test.d.ts +1 -0
  41. package/lib/services/ConnectionPoolManager.d.ts +54 -0
  42. package/lib/services/ConnectionPoolManager.js +163 -0
  43. package/lib/services/ConnectionPoolManager.js.map +1 -0
  44. package/lib/services/base-proxy-service.d.ts +2 -1
  45. package/lib/services/base-proxy-service.js.map +1 -1
  46. package/lib/services/base-service.d.ts +2 -1
  47. package/lib/services/base-service.js.map +1 -1
  48. package/lib/services/index.d.ts +3 -0
  49. package/lib/store/models/common-options-v2.d.ts +16 -0
  50. package/lib/store/models/common-options-v2.js +40 -0
  51. package/lib/store/models/common-options-v2.js.map +1 -0
  52. package/lib/store/models/common-options.js.map +1 -1
  53. package/lib/store/models/index.d.ts +1 -0
  54. package/lib/store/repositories/BaseMongoRepository.d.ts +72 -0
  55. package/lib/store/repositories/BaseMongoRepository.js +423 -0
  56. package/lib/store/repositories/BaseMongoRepository.js.map +1 -0
  57. package/lib/store/repositories/BaseMongoRepository.test.d.ts +1 -0
  58. package/lib/store/repositories/CustomIdRepository.test.d.ts +1 -0
  59. package/lib/store/repositories/base-repository.d.ts +2 -1
  60. package/lib/store/repositories/base-repository.js +1 -1
  61. package/lib/store/repositories/base-repository.js.map +1 -1
  62. package/lib/store/repositories/index.d.ts +1 -0
  63. package/lib/templates/constants/SERVER_TYPES.ts.template +7 -0
  64. package/lib/templates/repositories/DataLoader.ts.template +211 -0
  65. package/lib/templates/repositories/DatabaseMigration.ts.template +13 -0
  66. package/lib/templates/repositories/IBaseMongoRepository.ts.template +284 -0
  67. package/lib/templates/repositories/IBaseService.ts.template +231 -0
  68. package/lib/templates/repositories/IBaseServiceMixin.ts.template +477 -0
  69. package/lib/templates/repositories/dbCommonTypes.ts.template +140 -0
  70. package/lib/templates/repositories/moleculerEventHandler.ts.template.toberemoved +118 -0
  71. package/lib/templates/repositories/mongoCommonTypes.ts.template +21 -0
  72. package/lib/templates/repositories/typedMoleculerService.ts.template.toberemoved +1188 -0
  73. package/lib/templates/repositories/zodToMoleculer.ts.template.toberemoved +133 -0
  74. package/package.json +22 -5
  75. package/lib/interfaces/base-repository.js +0 -5
  76. package/lib/interfaces/base-repository.js.map +0 -1
  77. package/lib/interfaces/get-all-args.d.ts +0 -9
@@ -0,0 +1,1188 @@
1
+ /**
2
+ * @file typed-moleculer-service.ts
3
+ * @description Provides type-safe base class for Moleculer services with ENFORCED
4
+ * compile-time verification AND automatic action generation from service methods.
5
+ */
6
+
7
+ /* eslint-disable @typescript-eslint/no-namespace */
8
+ import type { ServiceSchema } from 'moleculer';
9
+ import { Moleculer } from './moleculerEventHandler';
10
+
11
+ /**
12
+ * Exclude base service CRUD methods and lifecycle methods
13
+ */
14
+ type ExcludedMethods =
15
+ | 'dispose'
16
+ | 'get'
17
+ | 'getAll'
18
+ | 'bulkDelete'
19
+ | 'delete'
20
+ | 'count'
21
+ | 'getByName'
22
+ | 'getByIds'
23
+ | 'getAllWithCount'
24
+ | 'create'
25
+ | 'update'
26
+ | 'bulkCreate'
27
+ | 'insert';
28
+
29
+ /**
30
+ * Extract all method names from a service interface (excluding base methods)
31
+ */
32
+ export type ServiceMethods<T> = {
33
+ // eslint-disable-next-line @typescript-eslint/ban-types
34
+ [K in keyof T]: T[K] extends Function ? (K extends ExcludedMethods ? never : K) : never;
35
+ }[keyof T];
36
+
37
+ /**
38
+ * Required actions type - forces all service methods to have actions
39
+ */
40
+ export type RequiredServiceActions<TService> = {
41
+ [K in ServiceMethods<TService>]: {
42
+ params?: Record<string, unknown>;
43
+ handler: (...args: unknown[]) => unknown;
44
+ };
45
+ };
46
+
47
+ /**
48
+ * Service schema with REQUIRED typed actions - TypeScript will error if any are missing
49
+ */
50
+ export interface EnforcedServiceSchema<TService> extends Omit<ServiceSchema, 'actions'> {
51
+ name: string; // Required by Moleculer
52
+ actions: RequiredServiceActions<TService> & Record<string, unknown>;
53
+ }
54
+
55
+ /**
56
+ * Infer Moleculer param type from TypeScript function parameter types
57
+ * Maps TypeScript types to Moleculer validation strings
58
+ *
59
+ * @internal Reserved for future advanced type inference
60
+ */
61
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
62
+ type InferMoleculerParamType<T> =
63
+ T extends string ? 'string' :
64
+ T extends number ? 'number' :
65
+ T extends boolean ? 'boolean' :
66
+ T extends unknown[] ? 'array' :
67
+ T extends object ? 'object' :
68
+ 'any';
69
+
70
+ /**
71
+ * Extract parameter types from a function signature
72
+ *
73
+ * @internal Reserved for future advanced type inference
74
+ */
75
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
76
+ type FunctionParams<T> = T extends (...args: infer P) => unknown ? P : never;
77
+
78
+ /**
79
+ * Split parameters by comma, respecting nested structures
80
+ */
81
+ function splitParams(paramsStr: string): string[] {
82
+ const params: string[] = [];
83
+ let current = '';
84
+ let depth = 0;
85
+
86
+ for (let i = 0; i < paramsStr.length; i++) {
87
+ const char = paramsStr[i];
88
+
89
+ if (char === '<' || char === '{' || char === '[' || char === '(') {
90
+ depth++;
91
+ current += char;
92
+ } else if (char === '>' || char === '}' || char === ']' || char === ')') {
93
+ depth--;
94
+ current += char;
95
+ } else if (char === ',' && depth === 0) {
96
+ params.push(current);
97
+ current = '';
98
+ } else {
99
+ current += char;
100
+ }
101
+ }
102
+
103
+ if (current) {
104
+ params.push(current);
105
+ }
106
+
107
+ return params;
108
+ }
109
+
110
+ /**
111
+ * Infer Moleculer validator type from TypeScript type annotation
112
+ */
113
+ function inferMoleculerType(typeAnnotation: string): string {
114
+ const type = typeAnnotation.trim();
115
+
116
+ // Check for array types
117
+ if (type.endsWith('[]') || type.startsWith('Array<') || type.startsWith('ReadonlyArray<')) {
118
+ return 'array';
119
+ }
120
+
121
+ // Check for primitive types
122
+ if (type === 'string' || type === 'String') return 'string';
123
+ if (type === 'number' || type === 'Number') return 'number';
124
+ if (type === 'boolean' || type === 'Boolean') return 'boolean';
125
+
126
+ // Check for common built-in types
127
+ if (type === 'Date') return 'date';
128
+ if (type === 'RegExp') return 'object';
129
+
130
+ // Check for union types with primitive
131
+ if (type.includes('|')) {
132
+ const types = type.split('|').map(t => t.trim());
133
+ // If all types are same primitive, return that
134
+ const nonNullTypes = types.filter(t => t !== 'null' && t !== 'undefined');
135
+ if (nonNullTypes.length === 1) {
136
+ return inferMoleculerType(nonNullTypes[0]);
137
+ }
138
+ // If contains string, number, or boolean, try to infer
139
+ if (nonNullTypes.some(t => t === 'string')) return 'string';
140
+ if (nonNullTypes.some(t => t === 'number')) return 'number';
141
+ if (nonNullTypes.some(t => t === 'boolean')) return 'boolean';
142
+ }
143
+
144
+ // Check for enum types (assume string enum)
145
+ if (type.includes('Enum') || type.includes('Type')) {
146
+ return 'string';
147
+ }
148
+
149
+ // Default to object for interfaces, types, classes, etc.
150
+ return 'object';
151
+ }
152
+
153
+ /**
154
+ * Infer type from default value
155
+ */
156
+ function inferTypeFromDefault(defaultValue: string): string {
157
+ // String defaults
158
+ if (defaultValue.startsWith("'") || defaultValue.startsWith('"') || defaultValue.startsWith('`')) {
159
+ return 'string';
160
+ }
161
+
162
+ // Number defaults
163
+ if (/^-?\d+(\.\d+)?$/.test(defaultValue)) {
164
+ return 'number';
165
+ }
166
+
167
+ // Boolean defaults
168
+ if (defaultValue === 'true' || defaultValue === 'false') {
169
+ return 'boolean';
170
+ }
171
+
172
+ // Array defaults
173
+ if (defaultValue.startsWith('[')) {
174
+ return 'array';
175
+ }
176
+
177
+ // Object defaults
178
+ if (defaultValue.startsWith('{')) {
179
+ return 'object';
180
+ }
181
+
182
+ return 'object';
183
+ }
184
+
185
+ /**
186
+ * Extract parameter names and infer types from a function's string representation
187
+ * Returns array of { name, type } where type is the inferred Moleculer validator
188
+ */
189
+ function extractParamsWithTypes(funcStr: string): Array<{ name: string; type: string; optional: boolean }> {
190
+ // Match function parameters: function(a, b) or (a, b) => or async (a, b) =>
191
+ const match = funcStr.match(/(?:function\s*)?(?:\w+\s*)?\(([^)]*)\)/);
192
+ if (!match || !match[1]) return [];
193
+
194
+ const paramsStr = match[1].trim();
195
+ if (!paramsStr) return [];
196
+
197
+ const params: Array<{ name: string; type: string; optional: boolean }> = [];
198
+
199
+ // Split by comma, but handle nested objects/arrays
200
+ const paramParts = splitParams(paramsStr);
201
+
202
+ paramParts.forEach(param => {
203
+ const trimmed = param.trim();
204
+ if (!trimmed) return;
205
+
206
+ // Check if optional (has ?)
207
+ const optional = trimmed.includes('?');
208
+
209
+ // Extract parameter name and type annotation
210
+ // Patterns: "name", "name?", "name: Type", "name?: Type", "name = default"
211
+ const paramMatch = trimmed.match(/^(\w+)\??(?:\s*:\s*([^=]+))?(?:\s*=\s*(.+))?/);
212
+ if (!paramMatch) return;
213
+
214
+ const name = paramMatch[1];
215
+ const typeAnnotation = paramMatch[2]?.trim();
216
+ const defaultValue = paramMatch[3]?.trim();
217
+
218
+ // Infer Moleculer type from TypeScript annotation or default value
219
+ let moleculerType = 'object'; // Safe default
220
+
221
+ if (typeAnnotation) {
222
+ moleculerType = inferMoleculerType(typeAnnotation);
223
+ } else if (defaultValue) {
224
+ moleculerType = inferTypeFromDefault(defaultValue);
225
+ }
226
+
227
+ params.push({
228
+ name,
229
+ type: moleculerType,
230
+ optional: optional || !!defaultValue,
231
+ });
232
+ });
233
+
234
+ return params;
235
+ }
236
+
237
+ /**
238
+ * Extract parameter names from a function's string representation (legacy)
239
+ * @deprecated Use extractParamsWithTypes instead
240
+ */
241
+ function extractParamNames(funcStr: string): string[] {
242
+ const params = extractParamsWithTypes(funcStr);
243
+ return params.map(p => p.name);
244
+ }
245
+
246
+ /**
247
+ * ADVANCED AUTO-GENERATED configuration with full control
248
+ * Params are inferred from TypeScript function signatures with optional fine-grained control
249
+ */
250
+ export type AutoInferredActionConfig<TService> = {
251
+ /**
252
+ * Optional: Override param schemas for specific methods
253
+ * Use this when you need custom Moleculer validation beyond the default 'object' type
254
+ *
255
+ * @example
256
+ * ```typescript
257
+ * paramOverrides: {
258
+ * findAccountById: { id: 'string' }, // Override inferred 'object' with 'string'
259
+ * getUsers: { where: { type: 'object', optional: true } } // Add optional flag
260
+ * }
261
+ * ```
262
+ */
263
+ paramOverrides?: Partial<{
264
+ [K in ServiceMethods<TService>]: Record<string, unknown>;
265
+ }>;
266
+
267
+ /**
268
+ * Optional: Only include specific methods (whitelist approach)
269
+ * If specified, ONLY these methods will be auto-generated
270
+ * Takes precedence over exclude
271
+ *
272
+ * @example
273
+ * ```typescript
274
+ * include: ['findAccountById', 'createAccount', 'updateAccount']
275
+ * ```
276
+ */
277
+ include?: Array<ServiceMethods<TService>>;
278
+
279
+ /**
280
+ * Optional: Exclude specific methods from auto-generation (blacklist approach)
281
+ * Ignored if 'include' is specified
282
+ *
283
+ * @example
284
+ * ```typescript
285
+ * exclude: ['deleteAccount', 'internalMethod']
286
+ * ```
287
+ */
288
+ exclude?: Array<ServiceMethods<TService>>;
289
+
290
+ /**
291
+ * Optional: Map action names to different method names
292
+ * Use this when your Moleculer action name differs from the service method name
293
+ *
294
+ * @example
295
+ * ```typescript
296
+ * actionToMethodMap: {
297
+ * 'accounts.find': 'findAccountById',
298
+ * 'accounts.create': 'createAccount'
299
+ * }
300
+ * ```
301
+ */
302
+ actionToMethodMap?: Record<string, string>;
303
+
304
+ /**
305
+ * Optional: Add custom Moleculer action properties for specific methods
306
+ * Allows setting visibility, caching, rate limiting, etc.
307
+ *
308
+ * @example
309
+ * ```typescript
310
+ * actionConfig: {
311
+ * findAccountById: {
312
+ * cache: true,
313
+ * visibility: 'public'
314
+ * },
315
+ * deleteAccount: {
316
+ * visibility: 'protected'
317
+ * }
318
+ * }
319
+ * ```
320
+ */
321
+ actionConfig?: Partial<{
322
+ [K in ServiceMethods<TService>]: {
323
+ cache?: boolean | object;
324
+ visibility?: 'public' | 'protected' | 'private';
325
+ [key: string]: unknown;
326
+ };
327
+ }>;
328
+ };
329
+
330
+ /**
331
+ * Helper function to get all method names from an object, including inherited methods
332
+ * from the entire prototype chain (excluding Object.prototype methods)
333
+ *
334
+ * @param obj - The object to get methods from
335
+ * @returns Array of method names
336
+ */
337
+ function getAllMethodNames(obj: unknown): string[] {
338
+ const methods = new Set<string>();
339
+ let current = obj;
340
+
341
+ // Traverse up the prototype chain
342
+ while (current && current !== Object.prototype) {
343
+ const prototype = Object.getPrototypeOf(current);
344
+ if (prototype && prototype !== Object.prototype) {
345
+ Object.getOwnPropertyNames(prototype).forEach(name => {
346
+ // Only add functions, not constructors or private methods
347
+ if (name !== 'constructor' && !name.startsWith('_')) {
348
+ const descriptor = Object.getOwnPropertyDescriptor(prototype, name);
349
+ if (descriptor && typeof descriptor.value === 'function') {
350
+ methods.add(name);
351
+ }
352
+ }
353
+ });
354
+ }
355
+ current = prototype;
356
+ }
357
+
358
+ return Array.from(methods);
359
+ }
360
+
361
+ /**
362
+ * ADVANCED AUTO-GENERATED: Generate actions with full control over inclusion, exclusion, and overrides.
363
+ * Params are inferred from TypeScript function signatures with optional fine-grained control.
364
+ *
365
+ * @param service - The service instance to wrap
366
+ * @param config - Advanced configuration for fine-grained control
367
+ * @returns Actions object with handlers for selected service methods
368
+ *
369
+ * @example
370
+ * ```typescript
371
+ * // Basic: Auto-generate all methods
372
+ * const actions = generateAutoInferredServiceActions<IAccountService>(accountService);
373
+ *
374
+ * // Advanced: Include only specific methods
375
+ * const actions = generateAutoInferredServiceActions<IAccountService>(accountService, {
376
+ * include: ['findAccountById', 'createAccount', 'updateAccount']
377
+ * });
378
+ *
379
+ * // Advanced: Exclude specific methods
380
+ * const actions = generateAutoInferredServiceActions<IAccountService>(accountService, {
381
+ * exclude: ['deleteAccount', 'internalMethod']
382
+ * });
383
+ *
384
+ * // Advanced: Override param schemas for specific methods
385
+ * const actions = generateAutoInferredServiceActions<IAccountService>(accountService, {
386
+ * paramOverrides: {
387
+ * findAccountById: { id: 'string' },
388
+ * getUsers: { where: { type: 'object', optional: true } }
389
+ * }
390
+ * });
391
+ *
392
+ * // Advanced: Add custom action configuration
393
+ * const actions = generateAutoInferredServiceActions<IAccountService>(accountService, {
394
+ * actionConfig: {
395
+ * findAccountById: { cache: true, visibility: 'public' },
396
+ * deleteAccount: { visibility: 'protected' }
397
+ * }
398
+ * });
399
+ *
400
+ * // Advanced: Combine multiple options
401
+ * const actions = generateAutoInferredServiceActions<IAccountService>(accountService, {
402
+ * include: ['findAccountById', 'createAccount'],
403
+ * paramOverrides: {
404
+ * findAccountById: { id: 'string' }
405
+ * },
406
+ * actionConfig: {
407
+ * findAccountById: { cache: true }
408
+ * }
409
+ * });
410
+ * ```
411
+ */
412
+ export function generateAutoInferredServiceActions<TService>(
413
+ service: TService,
414
+ config: AutoInferredActionConfig<TService> = {},
415
+ ): Record<string, { params?: Record<string, unknown>; handler: (ctx: unknown) => unknown }> {
416
+ const {
417
+ paramOverrides = {},
418
+ include,
419
+ exclude = [],
420
+ actionToMethodMap = {},
421
+ actionConfig = {}
422
+ } = config;
423
+ const actions: Record<string, { params?: Record<string, unknown>; handler: (ctx: unknown) => unknown }> = {};
424
+
425
+ // Get all methods from the service, including inherited methods from parent classes
426
+ const allKeys = getAllMethodNames(service);
427
+
428
+ allKeys.forEach((key) => {
429
+ // Skip constructor and private methods
430
+ if (key === 'constructor' || key.startsWith('_')) {
431
+ return;
432
+ }
433
+
434
+ // Apply include filter (whitelist) - takes precedence
435
+ if (include && include.length > 0) {
436
+ if (!include.includes(key as ServiceMethods<TService>)) {
437
+ return;
438
+ }
439
+ }
440
+ // Apply exclude filter (blacklist) - only if include is not specified
441
+ else if (exclude.includes(key as ServiceMethods<TService>)) {
442
+ return;
443
+ }
444
+
445
+ const method = (service as Record<string, unknown>)[key];
446
+ if (typeof method !== 'function') {
447
+ return;
448
+ }
449
+
450
+ const methodName = actionToMethodMap[key] || key;
451
+ const serviceMethod = (service as Record<string, unknown>)[methodName];
452
+
453
+ if (typeof serviceMethod !== 'function') {
454
+ return;
455
+ }
456
+
457
+ // Use runtime reflection to infer params and types from function signature
458
+ const funcStr = serviceMethod.toString();
459
+ const paramsWithTypes = extractParamsWithTypes(funcStr);
460
+
461
+ // Build param schema
462
+ let paramSchema: Record<string, unknown> | undefined;
463
+
464
+ // Use override if provided
465
+ if (paramOverrides[key as keyof typeof paramOverrides]) {
466
+ paramSchema = paramOverrides[key as keyof typeof paramOverrides] as Record<string, unknown>;
467
+ } else if (paramsWithTypes.length > 0) {
468
+ // Auto-generate schema from inferred types
469
+ paramSchema = paramsWithTypes.reduce((acc, param) => {
470
+ if (param.optional) {
471
+ acc[param.name] = { type: param.type, optional: true };
472
+ } else {
473
+ acc[param.name] = param.type;
474
+ }
475
+ return acc;
476
+ }, {} as Record<string, unknown>);
477
+ }
478
+
479
+ // Get custom action configuration if provided
480
+ const customActionConfig = actionConfig[key as keyof typeof actionConfig];
481
+
482
+ // Build the action object
483
+ const action: { params?: Record<string, unknown>; handler: (ctx: unknown) => unknown; [key: string]: unknown } = {
484
+ ...(paramSchema ? { params: paramSchema } : {}),
485
+ handler: (ctx: { params: Record<string, unknown> }) => {
486
+ const method = (service as Record<string, (...args: unknown[]) => unknown>)[methodName];
487
+ const paramsObj = ctx.params || {};
488
+ const values = Object.values(paramsObj);
489
+ return method.apply(service, values.length > 0 ? values : [paramsObj]);
490
+ },
491
+ };
492
+
493
+ // Merge custom action config if provided (cache, visibility, etc.)
494
+ if (customActionConfig) {
495
+ Object.assign(action, customActionConfig);
496
+ }
497
+
498
+ actions[key] = action;
499
+ });
500
+
501
+ return actions;
502
+ }
503
+
504
+ /**
505
+ * Generate both actions AND events from a service instance
506
+ * - Actions: Generated from regular service methods
507
+ * - Events: Generated from methods marked with @MoleculerEventHandler decorator
508
+ *
509
+ * @param service - The service instance to wrap
510
+ * @param config - Configuration for actions and events
511
+ * @returns Object with both actions and events
512
+ *
513
+ * @example
514
+ * ```typescript
515
+ * // Service with decorator
516
+ * class OrganizationService {
517
+ * // Regular method becomes action
518
+ * async createOrganization(org: Org): Promise<Org> { }
519
+ *
520
+ * // Decorated method becomes event handler
521
+ * @MoleculerEventHandler(UserBroadcasterAction.OnUserCreated)
522
+ * async onUserCreated(event: UserEvent): Promise<void> { }
523
+ * }
524
+ *
525
+ * // Generate both
526
+ * const { actions, events } = generateServiceActionsAndEvents(service);
527
+ * ```
528
+ */
529
+ export function generateServiceActionsAndEvents<TService>(
530
+ service: TService,
531
+ config: AutoInferredActionConfig<TService> = {},
532
+ ): {
533
+ actions: Record<string, { params?: Record<string, unknown>; handler: (ctx: unknown) => unknown; [key: string]: unknown }>;
534
+ events: Record<string, { handler: (ctx: unknown) => unknown; group?: string }>;
535
+ } {
536
+ // Get event handlers from decorator metadata
537
+ const eventHandlers = Moleculer.getEventHandlers(service as object);
538
+ const eventHandlerMethods = new Set(eventHandlers.map(h => h.methodName));
539
+
540
+ // Generate actions (excluding event handler methods)
541
+ const actionsConfig: AutoInferredActionConfig<TService> = {
542
+ ...config,
543
+ exclude: [
544
+ ...(config.exclude || []),
545
+ ...Array.from(eventHandlerMethods) as Array<ServiceMethods<TService>>,
546
+ ],
547
+ };
548
+
549
+ const actions = generateAutoInferredServiceActions<TService>(service, actionsConfig);
550
+
551
+ // Generate events from decorated methods
552
+ const events: Record<string, { handler: (ctx: unknown) => unknown; group?: string }> = {};
553
+
554
+ eventHandlers.forEach((metadata: Moleculer.EventHandlerMetadata) => {
555
+ const method = (service as Record<string, unknown>)[metadata.methodName];
556
+
557
+ if (typeof method === 'function') {
558
+ events[metadata.eventName] = {
559
+ handler: async (ctx: { params: { event?: unknown } }) => {
560
+ // Event payload is typically wrapped in ctx.params.event
561
+ const eventData = ctx.params.event || ctx.params;
562
+ return (method as (...args: unknown[]) => unknown).call(service, eventData);
563
+ },
564
+ ...(metadata.group ? { group: metadata.group } : {}),
565
+ };
566
+ }
567
+ });
568
+
569
+ return { actions, events };
570
+ }
571
+
572
+ export interface AutoActionConfig {
573
+ /**
574
+ * Custom parameter validation schema for specific actions
575
+ * Key is the action/method name, value is Moleculer params schema
576
+ */
577
+ params?: Record<string, Record<string, unknown>>;
578
+
579
+ /**
580
+ * Actions to exclude from auto-generation (use manual handlers instead)
581
+ */
582
+ exclude?: string[];
583
+
584
+ /**
585
+ * Action name mappings (if action name differs from method name)
586
+ * Key is the action name, value is the method name
587
+ */
588
+ actionToMethodMap?: Record<string, string>;
589
+ }
590
+
591
+ /**
592
+ * Type-safe configuration that REQUIRES params for ALL service methods
593
+ * TypeScript will show compile error if any method is missing from params
594
+ */
595
+ export type TypeSafeActionConfig<TService> = {
596
+ /**
597
+ * REQUIRED: Parameter validation schema for ALL service methods
598
+ * TypeScript will error if any method is missing
599
+ */
600
+ params: {
601
+ [K in ServiceMethods<TService>]: Record<string, unknown>;
602
+ };
603
+
604
+ /**
605
+ * Optional: Actions to exclude from auto-generation
606
+ */
607
+ exclude?: string[];
608
+
609
+ /**
610
+ * Optional: Action name mappings
611
+ */
612
+ actionToMethodMap?: Record<string, string>;
613
+ };
614
+
615
+ /**
616
+ * Automatically generate Moleculer actions from a service instance.
617
+ *
618
+ * This eliminates the need to write repetitive action handlers - just pass your service
619
+ * and it will create handlers that forward all calls automatically.
620
+ *
621
+ * @param service - The service instance to wrap
622
+ * @param config - Optional configuration for customizing actions
623
+ * @returns Actions object with handlers for all service methods
624
+ *
625
+ * @example
626
+ * ```typescript
627
+ * const actions = generateServiceActions(accountService, {
628
+ * params: {
629
+ * findAccountById: { id: 'string' },
630
+ * createAccount: { account: 'object' }
631
+ * },
632
+ * exclude: ['get', 'getAll'] // Handle these manually
633
+ * });
634
+ * ```
635
+ */
636
+ export function generateServiceActions<TService extends Record<string, unknown>>(
637
+ service: TService,
638
+ config: AutoActionConfig = {},
639
+ ): Record<string, { params?: Record<string, unknown>; handler: (ctx: unknown) => unknown }> {
640
+ const { params = {}, exclude = [], actionToMethodMap = {} } = config;
641
+ const actions: Record<string, { params?: Record<string, unknown>; handler: (ctx: unknown) => unknown }> = {};
642
+
643
+ // Get all methods from the service
644
+ const allKeys = Object.getOwnPropertyNames(Object.getPrototypeOf(service));
645
+ const generatedMethods: string[] = [];
646
+
647
+ allKeys.forEach((key) => {
648
+ // Skip excluded methods, constructors, and private methods
649
+ if (exclude.includes(key) || key === 'constructor' || key.startsWith('_')) {
650
+ return;
651
+ }
652
+
653
+ const method = (service as Record<string, unknown>)[key];
654
+
655
+ // Only process functions
656
+ if (typeof method !== 'function') {
657
+ return;
658
+ }
659
+
660
+ // Determine the method name to call (in case of mapping)
661
+ const methodName = actionToMethodMap[key] || key;
662
+ const serviceMethod = (service as Record<string, unknown>)[methodName];
663
+
664
+ if (typeof serviceMethod !== 'function') {
665
+ return;
666
+ }
667
+
668
+ generatedMethods.push(key);
669
+
670
+ // Create the action handler
671
+ actions[key] = {
672
+ ...(params[key] ? { params: params[key] } : {}),
673
+ handler: (ctx: { params: Record<string, unknown> }) => {
674
+ // Call the service method with ctx.params spread as arguments
675
+ const method = (service as Record<string, (...args: unknown[]) => unknown>)[methodName];
676
+
677
+ // If params is an object with known keys, extract them in order
678
+ // Otherwise, pass the whole params object
679
+ const paramsObj = ctx.params || {};
680
+ const values = Object.values(paramsObj);
681
+
682
+ return method.apply(service, values.length > 0 ? values : [paramsObj]);
683
+ },
684
+ };
685
+ });
686
+
687
+ // In development, log methods that don't have param validation
688
+ if (process.env.NODE_ENV !== 'production') {
689
+ const methodsWithoutParams = generatedMethods.filter(method => !params[method]);
690
+ if (methodsWithoutParams.length > 0) {
691
+ console.warn(
692
+ `⚠️ [generateServiceActions] The following methods don't have param validation defined:`,
693
+ methodsWithoutParams
694
+ );
695
+ }
696
+ }
697
+
698
+ return actions;
699
+ }
700
+
701
+ /**
702
+ * TYPE-SAFE version: Generate Moleculer actions with COMPILE-TIME verification.
703
+ * TypeScript will error if any service method is missing from params.
704
+ *
705
+ * @param service - The service instance to wrap
706
+ * @param config - Type-safe configuration requiring ALL methods to have params
707
+ * @returns Actions object with handlers for all service methods
708
+ *
709
+ * @example
710
+ * ```typescript
711
+ * const actions = generateTypeSafeServiceActions<IAccountService>(accountService, {
712
+ * params: {
713
+ * // TypeScript enforces ALL IAccountService methods are here!
714
+ * findAccountById: { id: 'string' },
715
+ * createAccount: { account: 'object' },
716
+ * // ... if you forget any method, TypeScript will error
717
+ * }
718
+ * });
719
+ * ```
720
+ */
721
+ export function generateTypeSafeServiceActions<TService>(
722
+ service: TService,
723
+ config: TypeSafeActionConfig<TService>,
724
+ ): Record<string, { params?: Record<string, unknown>; handler: (ctx: unknown) => unknown }> {
725
+ // Delegate to the regular function - type checking happens at compile time
726
+ return generateServiceActions(service as unknown as Record<string, unknown>, config as AutoActionConfig);
727
+ }
728
+
729
+ /**
730
+ * Verify that all service methods have param validation defined.
731
+ * Throws an error if any methods are missing params.
732
+ *
733
+ * Use this in development to ensure you haven't forgotten any param schemas.
734
+ *
735
+ * @param service - The service instance
736
+ * @param config - The config used for generateServiceActions
737
+ * @throws Error if any methods are missing param validation
738
+ *
739
+ * @example
740
+ * ```typescript
741
+ * const config = { params: { ... }, exclude: [...] };
742
+ * verifyAllParamsDefined(accountService, config); // Throws if params missing
743
+ * const actions = generateServiceActions(accountService, config);
744
+ * ```
745
+ */
746
+ export function verifyAllParamsDefined<TService extends Record<string, unknown>>(
747
+ service: TService,
748
+ config: AutoActionConfig = {},
749
+ ): void {
750
+ const { params = {}, exclude = [] } = config;
751
+ const allKeys = Object.getOwnPropertyNames(Object.getPrototypeOf(service));
752
+ const missingParams: string[] = [];
753
+
754
+ allKeys.forEach((key) => {
755
+ // Skip excluded methods, constructors, and private methods
756
+ if (exclude.includes(key) || key === 'constructor' || key.startsWith('_')) {
757
+ return;
758
+ }
759
+
760
+ const method = (service as Record<string, unknown>)[key];
761
+
762
+ // Only check functions
763
+ if (typeof method !== 'function') {
764
+ return;
765
+ }
766
+
767
+ // Check if params defined
768
+ if (!params[key]) {
769
+ missingParams.push(key);
770
+ }
771
+ });
772
+
773
+ if (missingParams.length > 0) {
774
+ const errorMessage = [
775
+ `Missing param validation for methods: ${missingParams.join(', ')}`,
776
+ '',
777
+ 'Add these to your params config:',
778
+ ...missingParams.map(method => ` ${method}: { /* your params */ },`),
779
+ ].join('\n');
780
+
781
+ throw new Error(errorMessage);
782
+ }
783
+ }
784
+
785
+ /**
786
+ * Type-safe Moleculer Service base class with ENFORCED action verification.
787
+ *
788
+ * @deprecated Use generateServiceActionsAndEvents() or generateAutoInferredServiceActions() instead
789
+ * This class requires importing Moleculer Service which causes build issues.
790
+ *
791
+ * Usage with auto-generated actions:
792
+ * ```typescript
793
+ * export class AccountMoleculerService extends Service {
794
+ * constructor(broker: ServiceBroker, { container }: { container: Container }) {
795
+ * super(broker);
796
+ * const accountService = container.get<IAccountService>(SERVER_TYPES.IAccountService);
797
+ *
798
+ * // Automatically generate ALL actions from the service!
799
+ * const autoActions = generateAutoInferredServiceActions(accountService);
800
+ *
801
+ * this.parseServiceSchema({
802
+ * name: MoleculerServiceName.AccountUser,
803
+ * actions: autoActions,
804
+ * });
805
+ * }
806
+ * }
807
+ * ```
808
+ */
809
+ // export class TypedMoleculerService<TService> extends Service {
810
+ // constructor(broker: ServiceBroker) {
811
+ // super(broker);
812
+ // }
813
+
814
+ // protected generateActions<T extends Record<string, unknown>>(
815
+ // service: T,
816
+ // config?: AutoActionConfig,
817
+ // ): Record<string, { params?: Record<string, unknown>; handler: (ctx: unknown) => unknown }> {
818
+ // return generateServiceActions(service, config);
819
+ // }
820
+
821
+ // protected parseTypedServiceSchema<T = TService>(schema: EnforcedServiceSchema<T>): void {
822
+ // this.parseServiceSchema(schema as ServiceSchema);
823
+ // }
824
+
825
+ // protected parsePartialServiceSchema(schema: ServiceSchema): void {
826
+ // this.parseServiceSchema(schema);
827
+ // }
828
+ // }
829
+
830
+ /**
831
+ * Placeholder for TypedMoleculerService for backward compatibility
832
+ * @deprecated Use generateServiceActionsAndEvents() instead
833
+ */
834
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
835
+ export type TypedMoleculerService<TService> = unknown;
836
+
837
+ /**
838
+ * Helper type for explicit verification - use in const to get better error messages
839
+ */
840
+ export type VerifyAllActionsImplemented<TService> = {
841
+ [K in ServiceMethods<TService>]: true;
842
+ };
843
+
844
+ /**
845
+ * Typed action handler with parameter and return type safety
846
+ */
847
+ export interface TypedActionHandler<TParams = unknown, TReturn = unknown> {
848
+ params?: Record<string, unknown>;
849
+ handler: (ctx: { params: TParams }) => TReturn | Promise<TReturn>;
850
+ }
851
+
852
+ /**
853
+ * Helper to create a typed action handler
854
+ */
855
+ export function createTypedAction<TParams, TReturn>(
856
+ action: TypedActionHandler<TParams, TReturn>,
857
+ ): TypedActionHandler<TParams, TReturn> {
858
+ return action;
859
+ }
860
+
861
+ /**
862
+ * Service schema type with better action type hints
863
+ */
864
+ export interface TypedServiceSchema extends ServiceSchema {
865
+ actions?: Record<string, TypedActionHandler>;
866
+ }
867
+
868
+ /**
869
+ * Extend Moleculer namespace with action generation utilities
870
+ */
871
+ declare module './moleculerEventHandler' {
872
+ export namespace Moleculer {
873
+ /**
874
+ * ADVANCED AUTO-GENERATED configuration with full control
875
+ */
876
+ export type AutoInferredActionConfig<TService> = {
877
+ paramOverrides?: Partial<{
878
+ [K in ServiceMethods<TService>]: Record<string, unknown>;
879
+ }>;
880
+ include?: Array<ServiceMethods<TService>>;
881
+ exclude?: Array<ServiceMethods<TService>>;
882
+ actionToMethodMap?: Record<string, string>;
883
+ actionConfig?: Partial<{
884
+ [K in ServiceMethods<TService>]: {
885
+ cache?: boolean | object;
886
+ visibility?: 'public' | 'protected' | 'private';
887
+ [key: string]: unknown;
888
+ };
889
+ }>;
890
+ };
891
+
892
+ /**
893
+ * Generate actions with full control over inclusion, exclusion, and overrides.
894
+ * Params are inferred from TypeScript function signatures.
895
+ */
896
+ export function generateAutoInferredActions<TService>(
897
+ service: TService,
898
+ config?: AutoInferredActionConfig<TService>,
899
+ ): Record<string, { params?: Record<string, unknown>; handler: (ctx: unknown) => unknown }>;
900
+
901
+ /**
902
+ * Generate both actions AND events from a service instance
903
+ */
904
+ export function generateActionsAndEvents<TService>(
905
+ service: TService,
906
+ config?: AutoInferredActionConfig<TService>,
907
+ ): {
908
+ actions: Record<string, { params?: Record<string, unknown>; handler: (ctx: unknown) => unknown; [key: string]: unknown }>;
909
+ events: Record<string, { handler: (ctx: unknown) => unknown; group?: string }>;
910
+ };
911
+
912
+ /**
913
+ * DEBUG UTILITY: Extract parameter information for all service methods
914
+ * Returns detailed information about parameters, schemas, and configurations
915
+ */
916
+ export function debugServiceParams<TService>(
917
+ service: TService,
918
+ config?: AutoInferredActionConfig<TService>,
919
+ ): Record<string, {
920
+ methodName: string;
921
+ paramNames: string[];
922
+ functionSignature: string;
923
+ inferredSchema: Record<string, unknown> | undefined;
924
+ overrideSchema: Record<string, unknown> | undefined;
925
+ isExcluded: boolean;
926
+ isEventHandler: boolean;
927
+ }>;
928
+
929
+ /**
930
+ * DEBUG UTILITY: Pretty print service parameter information to console
931
+ * Useful for understanding auto-generation and identifying methods needing overrides
932
+ */
933
+ export function printServiceParams<TService>(
934
+ service: TService,
935
+ config?: AutoInferredActionConfig<TService>,
936
+ options?: {
937
+ onlyMissingOverrides?: boolean;
938
+ onlyIncluded?: boolean;
939
+ showExcluded?: boolean;
940
+ },
941
+ ): void;
942
+ }
943
+ }
944
+
945
+ /**
946
+ * DEBUG UTILITY: Dumps parameter information for all service methods
947
+ *
948
+ * This function extracts and logs parameter names, types, and inferred schemas
949
+ * for debugging auto-generation issues. Use during development to understand
950
+ * what the auto-generation sees and optimize paramOverrides.
951
+ *
952
+ * @param service - The service instance to analyze
953
+ * @param config - Optional configuration to see what would be generated
954
+ * @returns Object with detailed parameter information for each method
955
+ *
956
+ * @example
957
+ * ```typescript
958
+ * // In development/debug mode
959
+ * const paramInfo = Moleculer.debugServiceParams(accountService);
960
+ * console.log(JSON.stringify(paramInfo, null, 2));
961
+ *
962
+ * // With config to see filtered results
963
+ * const paramInfo = Moleculer.debugServiceParams(accountService, {
964
+ * exclude: ['dispose', 'createUserToken']
965
+ * });
966
+ * ```
967
+ */
968
+ export function debugServiceParams<TService>(
969
+ service: TService,
970
+ config: AutoInferredActionConfig<TService> = {},
971
+ ): Record<string, {
972
+ methodName: string;
973
+ paramNames: string[];
974
+ functionSignature: string;
975
+ inferredSchema: Record<string, unknown> | undefined;
976
+ overrideSchema: Record<string, unknown> | undefined;
977
+ isExcluded: boolean;
978
+ isEventHandler: boolean;
979
+ }> {
980
+ const {
981
+ paramOverrides = {},
982
+ include,
983
+ exclude = [],
984
+ } = config;
985
+
986
+ const result: Record<string, {
987
+ methodName: string;
988
+ paramNames: string[];
989
+ functionSignature: string;
990
+ inferredSchema: Record<string, unknown> | undefined;
991
+ overrideSchema: Record<string, unknown> | undefined;
992
+ isExcluded: boolean;
993
+ isEventHandler: boolean;
994
+ }> = {};
995
+
996
+ // Get all methods from the service, including inherited methods
997
+ const allKeys = getAllMethodNames(service);
998
+
999
+ // Get event handler methods
1000
+ const eventHandlers = Moleculer.getEventHandlers(service as object);
1001
+ const eventHandlerMethods = new Set(eventHandlers.map(h => h.methodName));
1002
+
1003
+ allKeys.forEach((key) => {
1004
+ // Skip constructor and private methods
1005
+ if (key === 'constructor' || key.startsWith('_')) {
1006
+ return;
1007
+ }
1008
+
1009
+ const method = (service as Record<string, unknown>)[key];
1010
+ if (typeof method !== 'function') {
1011
+ return;
1012
+ }
1013
+
1014
+ // Determine if this method would be excluded
1015
+ const isEventHandler = eventHandlerMethods.has(key);
1016
+ let isExcluded = false;
1017
+
1018
+ if (include && include.length > 0) {
1019
+ isExcluded = !include.includes(key as ServiceMethods<TService>);
1020
+ } else {
1021
+ isExcluded = exclude.includes(key as ServiceMethods<TService>) || isEventHandler;
1022
+ }
1023
+
1024
+ // Get function signature
1025
+ const funcStr = method.toString();
1026
+ const paramsWithTypes = extractParamsWithTypes(funcStr);
1027
+
1028
+ // Extract just the function signature (first line with params)
1029
+ const signatureMatch = funcStr.match(/^(?:async\s+)?(?:function\s+)?(?:\w+\s*)?\(([^)]*)\)/);
1030
+ const signature = signatureMatch ? signatureMatch[0] : funcStr.split('{')[0].trim();
1031
+
1032
+ // Build inferred schema using new type inference
1033
+ let inferredSchema: Record<string, unknown> | undefined;
1034
+ if (paramsWithTypes.length > 0) {
1035
+ inferredSchema = paramsWithTypes.reduce((acc, param) => {
1036
+ if (param.optional) {
1037
+ acc[param.name] = { type: param.type, optional: true };
1038
+ } else {
1039
+ acc[param.name] = param.type;
1040
+ }
1041
+ return acc;
1042
+ }, {} as Record<string, unknown>);
1043
+ }
1044
+
1045
+ // Get override schema if provided
1046
+ const overrideSchema = paramOverrides[key as keyof typeof paramOverrides] as Record<string, unknown> | undefined;
1047
+
1048
+ result[key] = {
1049
+ methodName: key,
1050
+ paramNames: paramsWithTypes.map(p => p.name),
1051
+ functionSignature: signature,
1052
+ inferredSchema,
1053
+ overrideSchema,
1054
+ isExcluded,
1055
+ isEventHandler,
1056
+ };
1057
+ });
1058
+
1059
+ return result;
1060
+ }
1061
+
1062
+ /**
1063
+ * DEBUG UTILITY: Pretty prints service parameter information to console
1064
+ *
1065
+ * Formats and logs the output of debugServiceParams() in a readable way.
1066
+ * Shows which methods need paramOverrides and what types are inferred.
1067
+ *
1068
+ * @param service - The service instance to analyze
1069
+ * @param config - Optional configuration
1070
+ * @param options - Display options
1071
+ *
1072
+ * @example
1073
+ * ```typescript
1074
+ * // Print all methods
1075
+ * Moleculer.printServiceParams(accountService);
1076
+ *
1077
+ * // Print only methods without overrides
1078
+ * Moleculer.printServiceParams(accountService, config, { onlyMissingOverrides: true });
1079
+ *
1080
+ * // Print only included methods
1081
+ * Moleculer.printServiceParams(accountService, config, { onlyIncluded: true });
1082
+ * ```
1083
+ */
1084
+ export function printServiceParams<TService>(
1085
+ service: TService,
1086
+ config: AutoInferredActionConfig<TService> = {},
1087
+ options: {
1088
+ onlyMissingOverrides?: boolean;
1089
+ onlyIncluded?: boolean;
1090
+ showExcluded?: boolean;
1091
+ } = {},
1092
+ ): void {
1093
+ const paramInfo = debugServiceParams(service, config);
1094
+ const methods = Object.values(paramInfo);
1095
+
1096
+ const separator = '='.repeat(80);
1097
+ const dashedLine = '-'.repeat(80);
1098
+
1099
+ console.log(`\n${separator}`);
1100
+ console.log('SERVICE PARAMETER DEBUG INFORMATION');
1101
+ console.log(`${separator}\n`);
1102
+
1103
+ // Summary statistics
1104
+ const totalMethods = methods.length;
1105
+ const includedMethods = methods.filter(m => !m.isExcluded);
1106
+ const eventHandlers = methods.filter(m => m.isEventHandler);
1107
+ const withOverrides = methods.filter(m => m.overrideSchema);
1108
+ const withoutOverrides = includedMethods.filter(m => !m.overrideSchema && !m.isExcluded);
1109
+
1110
+ console.log('📊 SUMMARY:');
1111
+ console.log(` Total methods found: ${totalMethods}`);
1112
+ console.log(` Included in actions: ${includedMethods.length}`);
1113
+ console.log(` Event handlers (excluded from actions): ${eventHandlers.length}`);
1114
+ console.log(` With param overrides: ${withOverrides.length}`);
1115
+ console.log(` Without param overrides: ${withoutOverrides.length}`);
1116
+ console.log(`\n${dashedLine}\n`);
1117
+
1118
+ // Filter methods based on options
1119
+ let displayMethods = methods;
1120
+ if (options.onlyMissingOverrides) {
1121
+ displayMethods = displayMethods.filter(m => !m.overrideSchema && !m.isExcluded);
1122
+ }
1123
+ if (options.onlyIncluded) {
1124
+ displayMethods = displayMethods.filter(m => !m.isExcluded);
1125
+ }
1126
+ if (!options.showExcluded) {
1127
+ displayMethods = displayMethods.filter(m => !m.isExcluded);
1128
+ }
1129
+
1130
+ // Print each method
1131
+ displayMethods.forEach((info, index) => {
1132
+ let status: string;
1133
+ if (info.isExcluded) {
1134
+ status = '❌ EXCLUDED';
1135
+ } else if (info.isEventHandler) {
1136
+ status = '📡 EVENT HANDLER';
1137
+ } else if (info.overrideSchema) {
1138
+ status = '✅ HAS OVERRIDE';
1139
+ } else {
1140
+ status = '⚠️ NEEDS OVERRIDE';
1141
+ }
1142
+
1143
+ console.log(`${index + 1}. ${info.methodName} ${status}`);
1144
+ console.log(` Signature: ${info.functionSignature}`);
1145
+ console.log(` Parameters: [${info.paramNames.join(', ')}]`);
1146
+
1147
+ if (info.inferredSchema) {
1148
+ console.log(` Inferred: ${JSON.stringify(info.inferredSchema)}`);
1149
+ }
1150
+
1151
+ if (info.overrideSchema) {
1152
+ console.log(` Override: ${JSON.stringify(info.overrideSchema)}`);
1153
+ }
1154
+
1155
+ console.log('');
1156
+ });
1157
+
1158
+ // Suggestions
1159
+ if (withoutOverrides.length > 0 && !options.onlyMissingOverrides) {
1160
+ console.log(`\n${separator}`);
1161
+ console.log('💡 SUGGESTIONS:');
1162
+ console.log(`${separator}\n`);
1163
+ console.log('The following methods use default "object" validation:');
1164
+ console.log('Consider adding paramOverrides for better type safety:\n');
1165
+
1166
+ const suggestions: string[] = [];
1167
+ withoutOverrides.forEach(info => {
1168
+ const params = info.paramNames.map(p => `${p}: 'string'`).join(', ');
1169
+ suggestions.push(` ${info.methodName}: { ${params} },`);
1170
+ });
1171
+
1172
+ console.log('paramOverrides: {');
1173
+ console.log(suggestions.join('\n'));
1174
+ console.log('}');
1175
+ }
1176
+
1177
+ console.log(`\n${separator}\n`);
1178
+ }
1179
+
1180
+ // Implement the namespace extensions
1181
+ Moleculer.generateAutoInferredActions = generateAutoInferredServiceActions;
1182
+ Moleculer.generateActionsAndEvents = generateServiceActionsAndEvents;
1183
+ Moleculer.debugServiceParams = debugServiceParams;
1184
+ Moleculer.printServiceParams = printServiceParams;
1185
+
1186
+
1187
+
1188
+