@flusys/nestjs-shared 3.0.0 → 4.0.0-lts

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 (103) hide show
  1. package/README.md +160 -80
  2. package/cjs/classes/api-controller.class.js +26 -8
  3. package/cjs/classes/api-service.class.js +100 -17
  4. package/cjs/classes/winston-logger-adapter.class.js +15 -20
  5. package/cjs/classes/winston.logger.class.js +103 -70
  6. package/cjs/constants/index.js +1 -0
  7. package/cjs/constants/message-keys.js +80 -0
  8. package/cjs/constants/permissions.js +65 -11
  9. package/cjs/decorators/index.js +1 -0
  10. package/cjs/decorators/log-action.decorator.js +149 -0
  11. package/cjs/dtos/response-payload.dto.js +72 -0
  12. package/cjs/enums/index.js +20 -0
  13. package/cjs/enums/notification-type.enum.js +17 -0
  14. package/cjs/enums/participant-status.enum.js +17 -0
  15. package/cjs/enums/recurrence-type.enum.js +18 -0
  16. package/cjs/exceptions/base-app.exception.js +145 -0
  17. package/cjs/exceptions/index.js +1 -0
  18. package/cjs/exceptions/permission.exception.js +12 -8
  19. package/cjs/filters/global-exception.filter.js +167 -0
  20. package/cjs/filters/index.js +18 -0
  21. package/cjs/guards/jwt-auth.guard.js +4 -1
  22. package/cjs/guards/permission.guard.js +6 -13
  23. package/cjs/index.js +2 -0
  24. package/cjs/interceptors/idempotency.interceptor.js +1 -1
  25. package/cjs/interceptors/index.js +0 -1
  26. package/cjs/interfaces/event-manager-adapter.interface.js +11 -0
  27. package/cjs/interfaces/index.js +2 -0
  28. package/cjs/interfaces/logger.interface.js +1 -4
  29. package/cjs/interfaces/notification-adapter.interface.js +11 -0
  30. package/cjs/middlewares/logger.middleware.js +83 -26
  31. package/cjs/modules/datasource/multi-tenant-datasource.service.js +33 -11
  32. package/cjs/modules/utils/utils.service.js +4 -20
  33. package/cjs/utils/index.js +0 -1
  34. package/cjs/utils/query-helpers.util.js +8 -1
  35. package/classes/api-controller.class.d.ts +1 -0
  36. package/classes/api-service.class.d.ts +5 -10
  37. package/classes/winston-logger-adapter.class.d.ts +12 -11
  38. package/classes/winston.logger.class.d.ts +1 -0
  39. package/constants/index.d.ts +1 -0
  40. package/constants/message-keys.d.ts +81 -0
  41. package/constants/permissions.d.ts +72 -0
  42. package/decorators/index.d.ts +1 -0
  43. package/decorators/log-action.decorator.d.ts +8 -0
  44. package/dtos/response-payload.dto.d.ts +8 -0
  45. package/enums/index.d.ts +3 -0
  46. package/enums/notification-type.enum.d.ts +6 -0
  47. package/enums/participant-status.enum.d.ts +6 -0
  48. package/enums/recurrence-type.enum.d.ts +7 -0
  49. package/exceptions/base-app.exception.d.ts +41 -0
  50. package/exceptions/index.d.ts +1 -0
  51. package/exceptions/permission.exception.d.ts +1 -1
  52. package/fesm/classes/api-controller.class.js +26 -8
  53. package/fesm/classes/api-service.class.js +101 -18
  54. package/fesm/classes/winston-logger-adapter.class.js +18 -44
  55. package/fesm/classes/winston.logger.class.js +100 -68
  56. package/fesm/constants/index.js +2 -0
  57. package/fesm/constants/message-keys.js +59 -0
  58. package/fesm/constants/permissions.js +51 -14
  59. package/fesm/decorators/index.js +1 -0
  60. package/fesm/decorators/log-action.decorator.js +139 -0
  61. package/fesm/dtos/response-payload.dto.js +72 -0
  62. package/fesm/enums/index.js +3 -0
  63. package/fesm/enums/notification-type.enum.js +7 -0
  64. package/fesm/enums/participant-status.enum.js +7 -0
  65. package/fesm/enums/recurrence-type.enum.js +8 -0
  66. package/fesm/exceptions/base-app.exception.js +109 -0
  67. package/fesm/exceptions/index.js +1 -0
  68. package/fesm/exceptions/permission.exception.js +15 -17
  69. package/fesm/filters/global-exception.filter.js +157 -0
  70. package/fesm/filters/index.js +1 -0
  71. package/fesm/guards/jwt-auth.guard.js +5 -2
  72. package/fesm/guards/permission.guard.js +8 -15
  73. package/fesm/index.js +2 -0
  74. package/fesm/interceptors/idempotency.interceptor.js +2 -2
  75. package/fesm/interceptors/index.js +0 -1
  76. package/fesm/interfaces/event-manager-adapter.interface.js +1 -0
  77. package/fesm/interfaces/index.js +2 -0
  78. package/fesm/interfaces/logger.interface.js +1 -4
  79. package/fesm/interfaces/notification-adapter.interface.js +1 -0
  80. package/fesm/middlewares/logger.middleware.js +83 -26
  81. package/fesm/modules/datasource/multi-tenant-datasource.service.js +34 -12
  82. package/fesm/modules/utils/utils.service.js +5 -21
  83. package/fesm/utils/index.js +0 -1
  84. package/fesm/utils/query-helpers.util.js +8 -1
  85. package/filters/global-exception.filter.d.ts +10 -0
  86. package/filters/index.d.ts +1 -0
  87. package/guards/permission.guard.d.ts +1 -3
  88. package/index.d.ts +2 -0
  89. package/interceptors/index.d.ts +0 -1
  90. package/interfaces/event-manager-adapter.interface.d.ts +43 -0
  91. package/interfaces/index.d.ts +2 -0
  92. package/interfaces/logger.interface.d.ts +5 -5
  93. package/interfaces/notification-adapter.interface.d.ts +22 -0
  94. package/modules/datasource/multi-tenant-datasource.service.d.ts +1 -2
  95. package/modules/utils/utils.service.d.ts +0 -1
  96. package/package.json +10 -3
  97. package/utils/index.d.ts +0 -1
  98. package/cjs/interceptors/query-performance.interceptor.js +0 -66
  99. package/cjs/utils/error-handler.util.js +0 -90
  100. package/fesm/interceptors/query-performance.interceptor.js +0 -56
  101. package/fesm/utils/error-handler.util.js +0 -82
  102. package/interceptors/query-performance.interceptor.d.ts +0 -8
  103. package/utils/error-handler.util.d.ts +0 -19
@@ -26,7 +26,8 @@ function _ts_param(paramIndex, decorator) {
26
26
  };
27
27
  }
28
28
  import { DEFAULT_TENANT_HEADER, IDataSourceServiceOptions, MODULE_OPTIONS } from '@flusys/nestjs-core';
29
- import { BadRequestException, Inject, Injectable, Logger, Optional, Scope } from '@nestjs/common';
29
+ import { BadRequestException, Inject, Injectable, InternalServerErrorException, Optional, Scope } from '@nestjs/common';
30
+ import { SYSTEM_MESSAGES } from '../../constants';
30
31
  import { REQUEST } from '@nestjs/core';
31
32
  import { Request } from 'express';
32
33
  import { DataSource } from 'typeorm';
@@ -58,7 +59,10 @@ export class MultiTenantDataSourceService {
58
59
  const tenantId = this.request.headers[this.tenantHeader];
59
60
  if (!tenantId) return null;
60
61
  if (!/^[a-zA-Z0-9_-]+$/.test(tenantId)) {
61
- throw new BadRequestException('Invalid tenant ID format');
62
+ throw new BadRequestException({
63
+ message: 'Invalid tenant ID format',
64
+ messageKey: SYSTEM_MESSAGES.INVALID_TENANT_ID
65
+ });
62
66
  }
63
67
  return tenantId;
64
68
  }
@@ -80,7 +84,15 @@ export class MultiTenantDataSourceService {
80
84
  }
81
85
  async getDataSourceForTenant(tenantId) {
82
86
  const tenant = this.getTenant(tenantId);
83
- if (!tenant) throw new Error(`Tenant '${tenantId}' not found`);
87
+ if (!tenant) {
88
+ throw new BadRequestException({
89
+ message: `Tenant '${tenantId}' not found`,
90
+ messageKey: SYSTEM_MESSAGES.TENANT_NOT_FOUND,
91
+ messageParams: {
92
+ tenantId
93
+ }
94
+ });
95
+ }
84
96
  return this.getOrCreateTenantConnection(tenant);
85
97
  }
86
98
  setDataSource(dataSource) {
@@ -100,8 +112,8 @@ export class MultiTenantDataSourceService {
100
112
  try {
101
113
  const dataSource = await this.getOrCreateTenantConnection(tenant);
102
114
  results.set(tenant.id, await callback(tenant, dataSource));
103
- } catch (error) {
104
- this.logger.error(`Error processing tenant ${tenant.id}: ${error}`);
115
+ } catch {
116
+ // Silent failure for individual tenant
105
117
  }
106
118
  }
107
119
  return results;
@@ -118,7 +130,6 @@ export class MultiTenantDataSourceService {
118
130
  if (connection?.isInitialized) {
119
131
  await connection.destroy();
120
132
  MultiTenantDataSourceService.tenantConnections.delete(tenantId);
121
- this.logger.log(`Closed connection for tenant: ${tenantId}`);
122
133
  }
123
134
  }
124
135
  async onModuleDestroy() {
@@ -148,7 +159,10 @@ export class MultiTenantDataSourceService {
148
159
  }
149
160
  const config = this.getDefaultDatabaseConfig();
150
161
  if (!config) {
151
- throw new Error('No database config available. Provide defaultDatabaseConfig or tenantDefaultDatabaseConfig.');
162
+ throw new InternalServerErrorException({
163
+ message: 'No database config available. Provide defaultDatabaseConfig or tenantDefaultDatabaseConfig.',
164
+ messageKey: SYSTEM_MESSAGES.DATABASE_CONFIG_NOT_AVAILABLE
165
+ });
152
166
  }
153
167
  // Create connection with lock to prevent race conditions
154
168
  const connectionPromise = this.createDataSourceFromConfig(config);
@@ -166,7 +180,13 @@ export class MultiTenantDataSourceService {
166
180
  */ async getTenantDataSource() {
167
181
  const tenant = this.getCurrentTenant();
168
182
  if (!tenant) {
169
- throw new Error(`Tenant not found. Ensure '${this.tenantHeader}' header is set.`);
183
+ throw new BadRequestException({
184
+ message: `Tenant not found. Ensure '${this.tenantHeader}' header is set.`,
185
+ messageKey: SYSTEM_MESSAGES.TENANT_HEADER_REQUIRED,
186
+ messageParams: {
187
+ header: this.tenantHeader
188
+ }
189
+ });
170
190
  }
171
191
  return this.getOrCreateTenantConnection(tenant);
172
192
  }
@@ -181,7 +201,6 @@ export class MultiTenantDataSourceService {
181
201
  try {
182
202
  const dataSource = await connectionPromise;
183
203
  MultiTenantDataSourceService.tenantConnections.set(tenant.id, dataSource);
184
- this.logger.log(`Created connection for tenant: ${tenant.id}`);
185
204
  return dataSource;
186
205
  } finally{
187
206
  MultiTenantDataSourceService.connectionLocks.delete(tenant.id);
@@ -192,7 +211,12 @@ export class MultiTenantDataSourceService {
192
211
  }
193
212
  buildTenantDatabaseConfig(tenant) {
194
213
  const defaultConfig = this.getDefaultDatabaseConfig();
195
- if (!defaultConfig) throw new Error('No default database config for multi-tenant mode.');
214
+ if (!defaultConfig) {
215
+ throw new InternalServerErrorException({
216
+ message: 'No default database config for multi-tenant mode.',
217
+ messageKey: SYSTEM_MESSAGES.DATABASE_CONFIG_NOT_AVAILABLE
218
+ });
219
+ }
196
220
  return {
197
221
  type: defaultConfig.type,
198
222
  host: tenant.host ?? defaultConfig.host,
@@ -220,11 +244,9 @@ export class MultiTenantDataSourceService {
220
244
  constructor(options, request){
221
245
  _define_property(this, "options", void 0);
222
246
  _define_property(this, "request", void 0);
223
- _define_property(this, "logger", void 0);
224
247
  _define_property(this, "tenantHeader", void 0);
225
248
  this.options = options;
226
249
  this.request = request;
227
- this.logger = new Logger(this.constructor.name);
228
250
  this.tenantHeader = DEFAULT_TENANT_HEADER;
229
251
  this.initializeFromOptions();
230
252
  }
@@ -1,23 +1,10 @@
1
- function _define_property(obj, key, value) {
2
- if (key in obj) {
3
- Object.defineProperty(obj, key, {
4
- value: value,
5
- enumerable: true,
6
- configurable: true,
7
- writable: true
8
- });
9
- } else {
10
- obj[key] = value;
11
- }
12
- return obj;
13
- }
14
1
  function _ts_decorate(decorators, target, key, desc) {
15
2
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
16
3
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
17
4
  else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
18
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
19
6
  }
20
- import { Injectable, Logger } from '@nestjs/common';
7
+ import { Injectable } from '@nestjs/common';
21
8
  export class UtilsService {
22
9
  // ---------------- CACHE HELPERS ----------------
23
10
  /**
@@ -37,8 +24,8 @@ export class UtilsService {
37
24
  const keys = await cacheManager.get(trackingKey) || [];
38
25
  if (!keys.includes(cacheKey)) keys.push(cacheKey);
39
26
  await cacheManager.set(trackingKey, keys);
40
- } catch (error) {
41
- this.logger.error(`Cache tracking failed for ${entityName}`, error instanceof Error ? error.stack : String(error));
27
+ } catch {
28
+ // Silent failure - cache tracking is non-critical
42
29
  }
43
30
  }
44
31
  /**
@@ -49,8 +36,8 @@ export class UtilsService {
49
36
  const keys = await cacheManager.get(trackingKey) || [];
50
37
  await Promise.allSettled(keys.map((key)=>cacheManager.del(key)));
51
38
  await cacheManager.del(trackingKey);
52
- } catch (error) {
53
- this.logger.error(`Cache invalidation failed for ${entityName}`, error instanceof Error ? error.stack : String(error));
39
+ } catch {
40
+ // Silent failure - cache invalidation is non-critical
54
41
  }
55
42
  }
56
43
  // ---------------- STRING HELPERS ----------------
@@ -73,9 +60,6 @@ export class UtilsService {
73
60
  const prefix = this.buildTenantPrefix(tenantId);
74
61
  return entityId ? `${prefix}entity_${entityName}_id_${entityId}_keys` : `${prefix}entity_${entityName}_keys`;
75
62
  }
76
- constructor(){
77
- _define_property(this, "logger", new Logger(UtilsService.name));
78
- }
79
63
  }
80
64
  UtilsService = _ts_decorate([
81
65
  Injectable()
@@ -1,4 +1,3 @@
1
- export * from './error-handler.util';
2
1
  export * from './html-sanitizer.util';
3
2
  export * from './query-helpers.util';
4
3
  export * from './request.util';
@@ -1,4 +1,5 @@
1
1
  import { BadRequestException } from '@nestjs/common';
2
+ import { AUTH_MESSAGES } from '../constants';
2
3
  /**
3
4
  * Apply company filter to a query builder if company feature is enabled.
4
5
  * Centralizes the common pattern of filtering by user's company.
@@ -72,7 +73,13 @@ import { BadRequestException } from '@nestjs/common';
72
73
  */ export function validateCompanyOwnership(entity, user, isCompanyFeatureEnabled, entityName) {
73
74
  if (isCompanyFeatureEnabled && user?.companyId && hasCompanyId(entity)) {
74
75
  if (entity.companyId && entity.companyId !== user.companyId) {
75
- throw new BadRequestException(`${entityName} belongs to another company`);
76
+ throw new BadRequestException({
77
+ message: `${entityName} belongs to another company`,
78
+ messageKey: AUTH_MESSAGES.COMPANY_NO_ACCESS,
79
+ messageParams: {
80
+ entity: entityName
81
+ }
82
+ });
76
83
  }
77
84
  }
78
85
  }
@@ -0,0 +1,10 @@
1
+ import { ArgumentsHost, ExceptionFilter } from '@nestjs/common';
2
+ export declare class GlobalExceptionFilter implements ExceptionFilter {
3
+ private readonly logger;
4
+ catch(exception: unknown, host: ArgumentsHost): void;
5
+ private buildErrorResponse;
6
+ private extractHttpExceptionDetails;
7
+ private getStatusCode;
8
+ private getStack;
9
+ private buildLogMessage;
10
+ }
@@ -0,0 +1 @@
1
+ export * from './global-exception.filter';
@@ -1,14 +1,12 @@
1
1
  import { CanActivate, ExecutionContext } from '@nestjs/common';
2
2
  import { Reflector } from '@nestjs/core';
3
3
  import { HybridCache } from '../classes/hybrid-cache.class';
4
- import { ILogger } from '../interfaces/logger.interface';
5
4
  import { PermissionGuardConfig } from '../interfaces/permission.interface';
6
5
  export declare class PermissionGuard implements CanActivate {
7
6
  private readonly reflector;
8
7
  private readonly cache?;
9
8
  private readonly config;
10
- private readonly logger;
11
- constructor(reflector: Reflector, cache?: HybridCache, config?: PermissionGuardConfig, logger?: ILogger);
9
+ constructor(reflector: Reflector, cache?: HybridCache, config?: PermissionGuardConfig);
12
10
  canActivate(context: ExecutionContext): Promise<boolean>;
13
11
  private normalizeToLogicNode;
14
12
  private evaluateLogicNode;
package/index.d.ts CHANGED
@@ -3,7 +3,9 @@ export * from './constants';
3
3
  export * from './decorators';
4
4
  export * from './dtos';
5
5
  export * from './entities';
6
+ export * from './enums';
6
7
  export * from './exceptions';
8
+ export * from './filters';
7
9
  export * from './guards';
8
10
  export * from './interceptors';
9
11
  export * from './interfaces';
@@ -1,6 +1,5 @@
1
1
  export * from './delete-empty-id-from-body.interceptor';
2
2
  export * from './idempotency.interceptor';
3
- export * from './query-performance.interceptor';
4
3
  export * from './response-meta.interceptor';
5
4
  export * from './set-user-field-on-body.interceptor';
6
5
  export * from './slug.interceptor';
@@ -0,0 +1,43 @@
1
+ import { ParticipantStatus } from '../enums/participant-status.enum';
2
+ import { RecurrenceType } from '../enums/recurrence-type.enum';
3
+ export interface IEventManagerAdapter {
4
+ createEvent(options: CreateEventOptions): Promise<EventResult>;
5
+ addParticipants(eventId: string, userIds: string[], companyId?: string): Promise<void>;
6
+ removeParticipant(eventId: string, userId: string): Promise<void>;
7
+ updateParticipantStatus(participantId: string, status: ParticipantStatus): Promise<void>;
8
+ getEventsForUser?(userId: string, startDate: Date, endDate: Date, companyId?: string): Promise<EventResult[]>;
9
+ getEventById?(eventId: string): Promise<EventResult | null>;
10
+ }
11
+ export interface CreateEventOptions {
12
+ title: string;
13
+ description?: string;
14
+ meetingLink?: string;
15
+ startDateTime: Date;
16
+ endDateTime: Date;
17
+ isAllDay?: boolean;
18
+ recurrenceType?: RecurrenceType;
19
+ recurrenceEndDate?: Date;
20
+ color?: string;
21
+ metadata?: Record<string, unknown>;
22
+ participantIds?: string[];
23
+ organizerId?: string;
24
+ companyId?: string;
25
+ }
26
+ export interface EventResult {
27
+ id: string;
28
+ title: string;
29
+ description?: string | null;
30
+ meetingLink?: string | null;
31
+ startDateTime: Date;
32
+ endDateTime: Date;
33
+ isAllDay: boolean;
34
+ recurrenceType: string;
35
+ recurrenceEndDate?: Date | null;
36
+ color: string;
37
+ isActive: boolean;
38
+ metadata?: Record<string, unknown> | null;
39
+ companyId?: string | null;
40
+ createdAt: Date;
41
+ updatedAt: Date;
42
+ }
43
+ export declare const EVENT_MANAGER_ADAPTER = "EVENT_MANAGER_ADAPTER";
@@ -1,7 +1,9 @@
1
1
  export * from './api.interface';
2
2
  export * from './datasource.interface';
3
+ export * from './event-manager-adapter.interface';
3
4
  export * from './identity.interface';
4
5
  export * from './logged-user-info.interface';
5
6
  export * from './logger.interface';
6
7
  export * from './module-config.interface';
8
+ export * from './notification-adapter.interface';
7
9
  export * from './permission.interface';
@@ -1,7 +1,7 @@
1
1
  export interface ILogger {
2
- log(message: string, context?: string, ...args: any[]): void;
3
- error(message: string, trace?: string, context?: string, ...args: any[]): void;
4
- warn(message: string, context?: string, ...args: any[]): void;
5
- debug(message: string, context?: string, ...args: any[]): void;
6
- verbose(message: string, context?: string, ...args: any[]): void;
2
+ log(message: string, context?: string, ...args: unknown[]): void;
3
+ error(message: string, trace?: string, context?: string, ...args: unknown[]): void;
4
+ warn(message: string, context?: string, ...args: unknown[]): void;
5
+ debug(message: string, context?: string, ...args: unknown[]): void;
6
+ verbose(message: string, context?: string, ...args: unknown[]): void;
7
7
  }
@@ -0,0 +1,22 @@
1
+ import { NotificationType } from '../enums/notification-type.enum';
2
+ export interface INotificationAdapter {
3
+ send(options: NotificationSendOptions): Promise<void>;
4
+ sendToMany(options: NotificationBulkSendOptions): Promise<void>;
5
+ broadcastToCompany?(companyId: string, title: string, message?: string, type?: NotificationType, data?: Record<string, unknown>): Promise<void>;
6
+ isUserOnline?(userId: string): boolean;
7
+ }
8
+ interface NotificationBaseOptions {
9
+ title: string;
10
+ message?: string;
11
+ type?: NotificationType;
12
+ data?: Record<string, unknown>;
13
+ companyId?: string;
14
+ }
15
+ export interface NotificationSendOptions extends NotificationBaseOptions {
16
+ userId: string;
17
+ }
18
+ export interface NotificationBulkSendOptions extends NotificationBaseOptions {
19
+ userIds: string[];
20
+ }
21
+ export declare const NOTIFICATION_ADAPTER = "NOTIFICATION_ADAPTER";
22
+ export {};
@@ -1,11 +1,10 @@
1
1
  import { DatabaseMode, IDatabaseConfig, IDataSourceServiceOptions, ITenantDatabaseConfig } from '@flusys/nestjs-core';
2
- import { Logger, OnModuleDestroy } from '@nestjs/common';
2
+ import { OnModuleDestroy } from '@nestjs/common';
3
3
  import { Request } from 'express';
4
4
  import { DataSource, EntityTarget, ObjectLiteral, Repository } from 'typeorm';
5
5
  export declare class MultiTenantDataSourceService implements OnModuleDestroy {
6
6
  protected readonly options?: IDataSourceServiceOptions;
7
7
  protected readonly request?: Request;
8
- protected readonly logger: Logger;
9
8
  protected static readonly tenantConnections: Map<string, DataSource>;
10
9
  protected static singleDataSource: DataSource | null;
11
10
  protected static readonly tenantsRegistry: Map<string, ITenantDatabaseConfig>;
@@ -1,6 +1,5 @@
1
1
  import { HybridCache } from '../../classes/hybrid-cache.class';
2
2
  export declare class UtilsService {
3
- private readonly logger;
4
3
  getCacheKey(entityName: string, params: any, entityId?: string, tenantId?: string): string;
5
4
  trackCacheKey(cacheKey: string, entityName: string, cacheManager: HybridCache, entityId?: string, tenantId?: string): Promise<void>;
6
5
  clearCache(entityName: string, cacheManager: HybridCache, entityId?: string, tenantId?: string): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flusys/nestjs-shared",
3
- "version": "3.0.0",
3
+ "version": "4.0.0-lts",
4
4
  "description": "Common shared utilities for Flusys NestJS applications",
5
5
  "main": "cjs/index.js",
6
6
  "module": "fesm/index.js",
@@ -46,6 +46,11 @@
46
46
  "import": "./fesm/entities/index.js",
47
47
  "require": "./cjs/entities/index.js"
48
48
  },
49
+ "./enums": {
50
+ "types": "./enums/index.d.ts",
51
+ "import": "./fesm/enums/index.js",
52
+ "require": "./cjs/enums/index.js"
53
+ },
49
54
  "./exceptions": {
50
55
  "types": "./exceptions/index.d.ts",
51
56
  "import": "./fesm/exceptions/index.js",
@@ -102,9 +107,11 @@
102
107
  "keyv": "^5.0.0",
103
108
  "typeorm": "^0.3.0",
104
109
  "winston": "^3.0.0",
105
- "winston-daily-rotate-file": "^5.0.0"
110
+ "winston-daily-rotate-file": "^5.0.0",
111
+ "express": "^4.18.0",
112
+ "rxjs": "^7.8.0"
106
113
  },
107
114
  "dependencies": {
108
- "@flusys/nestjs-core": "3.0.0"
115
+ "@flusys/nestjs-core": "4.0.0-lts"
109
116
  }
110
117
  }
package/utils/index.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- export * from './error-handler.util';
2
1
  export * from './html-sanitizer.util';
3
2
  export * from './query-helpers.util';
4
3
  export * from './request.util';
@@ -1,66 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- Object.defineProperty(exports, "QueryPerformanceInterceptor", {
6
- enumerable: true,
7
- get: function() {
8
- return QueryPerformanceInterceptor;
9
- }
10
- });
11
- const _common = require("@nestjs/common");
12
- const _operators = require("rxjs/operators");
13
- function _define_property(obj, key, value) {
14
- if (key in obj) {
15
- Object.defineProperty(obj, key, {
16
- value: value,
17
- enumerable: true,
18
- configurable: true,
19
- writable: true
20
- });
21
- } else {
22
- obj[key] = value;
23
- }
24
- return obj;
25
- }
26
- function _ts_decorate(decorators, target, key, desc) {
27
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
28
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
29
- else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
30
- return c > 3 && r && Object.defineProperty(target, key, r), r;
31
- }
32
- function _ts_metadata(k, v) {
33
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
34
- }
35
- let QueryPerformanceInterceptor = class QueryPerformanceInterceptor {
36
- intercept(context, next) {
37
- const request = context.switchToHttp().getRequest();
38
- const { method, url } = request;
39
- const handler = context.getHandler().name;
40
- const controller = context.getClass().name;
41
- const startTime = Date.now();
42
- return next.handle().pipe((0, _operators.tap)(()=>{
43
- const executionTime = Date.now() - startTime;
44
- if (executionTime > this.threshold) {
45
- this.logger.warn(`Slow request detected: ${method} ${url} | ` + `Controller: ${controller}.${handler} | ` + `Execution time: ${executionTime}ms`);
46
- } else {
47
- // Optional: Log all requests in debug mode
48
- this.logger.debug(`${method} ${url} | ${controller}.${handler} | ${executionTime}ms`);
49
- }
50
- }));
51
- }
52
- /**
53
- * @param threshold - Time threshold in milliseconds (default: 1000ms)
54
- */ constructor(threshold = 1000){
55
- _define_property(this, "logger", new _common.Logger(QueryPerformanceInterceptor.name));
56
- _define_property(this, "threshold", void 0);
57
- this.threshold = threshold;
58
- }
59
- };
60
- QueryPerformanceInterceptor = _ts_decorate([
61
- (0, _common.Injectable)(),
62
- _ts_metadata("design:type", Function),
63
- _ts_metadata("design:paramtypes", [
64
- Number
65
- ])
66
- ], QueryPerformanceInterceptor);
@@ -1,90 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- Object.defineProperty(exports, "ErrorHandler", {
6
- enumerable: true,
7
- get: function() {
8
- return ErrorHandler;
9
- }
10
- });
11
- /** Sensitive keys that should be redacted from logs */ const SENSITIVE_KEYS = [
12
- 'password',
13
- 'secret',
14
- 'token',
15
- 'apiKey',
16
- 'credential',
17
- 'authorization'
18
- ];
19
- let ErrorHandler = class ErrorHandler {
20
- /**
21
- * Safely extract error message from unknown error.
22
- */ static getErrorMessage(error) {
23
- if (error instanceof Error) {
24
- return error.message;
25
- }
26
- if (typeof error === 'string') {
27
- return error;
28
- }
29
- return 'Unknown error occurred';
30
- }
31
- /**
32
- * Sanitize context data to redact sensitive fields from logs.
33
- */ static sanitizeContextForLogging(context) {
34
- const sanitized = {};
35
- for (const [key, value] of Object.entries(context)){
36
- const isSensitive = SENSITIVE_KEYS.some((sk)=>key.toLowerCase().includes(sk.toLowerCase()));
37
- if (isSensitive) {
38
- sanitized[key] = '[REDACTED]';
39
- } else if (Array.isArray(value)) {
40
- sanitized[key] = value.map((item)=>typeof item === 'object' && item !== null ? this.sanitizeContextForLogging(item) : item);
41
- } else if (typeof value === 'object' && value !== null) {
42
- sanitized[key] = this.sanitizeContextForLogging(value);
43
- } else {
44
- sanitized[key] = value;
45
- }
46
- }
47
- return sanitized;
48
- }
49
- /**
50
- * Create error context object for internal logging.
51
- */ static createErrorContext(error, context) {
52
- const errorContext = {
53
- error: {
54
- message: this.getErrorMessage(error)
55
- }
56
- };
57
- if (error instanceof Error) {
58
- errorContext.error.stack = error.stack;
59
- errorContext.error.name = error.name;
60
- }
61
- if (context && Object.keys(context).length > 0) {
62
- errorContext.context = this.sanitizeContextForLogging(context);
63
- }
64
- return errorContext;
65
- }
66
- /**
67
- * Log error with consistent format.
68
- */ static logError(logger, error, operation, context) {
69
- const errorContext = this.createErrorContext(error, {
70
- operation,
71
- ...context
72
- });
73
- const errorMessage = `Failed to ${operation}: ${errorContext.error.message}`;
74
- logger.error(errorMessage, errorContext.error.stack, errorContext);
75
- }
76
- /**
77
- * Re-throw error with proper type checking.
78
- */ static rethrowError(error) {
79
- if (error instanceof Error) {
80
- throw error;
81
- }
82
- throw new Error(`Unexpected error: ${String(error)}`);
83
- }
84
- /**
85
- * Log error and re-throw (common pattern).
86
- */ static logAndRethrow(logger, error, operation, context) {
87
- this.logError(logger, error, operation, context);
88
- this.rethrowError(error);
89
- }
90
- };
@@ -1,56 +0,0 @@
1
- function _define_property(obj, key, value) {
2
- if (key in obj) {
3
- Object.defineProperty(obj, key, {
4
- value: value,
5
- enumerable: true,
6
- configurable: true,
7
- writable: true
8
- });
9
- } else {
10
- obj[key] = value;
11
- }
12
- return obj;
13
- }
14
- function _ts_decorate(decorators, target, key, desc) {
15
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
16
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
17
- else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
18
- return c > 3 && r && Object.defineProperty(target, key, r), r;
19
- }
20
- function _ts_metadata(k, v) {
21
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
22
- }
23
- import { Injectable, Logger } from '@nestjs/common';
24
- import { tap } from 'rxjs/operators';
25
- export class QueryPerformanceInterceptor {
26
- intercept(context, next) {
27
- const request = context.switchToHttp().getRequest();
28
- const { method, url } = request;
29
- const handler = context.getHandler().name;
30
- const controller = context.getClass().name;
31
- const startTime = Date.now();
32
- return next.handle().pipe(tap(()=>{
33
- const executionTime = Date.now() - startTime;
34
- if (executionTime > this.threshold) {
35
- this.logger.warn(`Slow request detected: ${method} ${url} | ` + `Controller: ${controller}.${handler} | ` + `Execution time: ${executionTime}ms`);
36
- } else {
37
- // Optional: Log all requests in debug mode
38
- this.logger.debug(`${method} ${url} | ${controller}.${handler} | ${executionTime}ms`);
39
- }
40
- }));
41
- }
42
- /**
43
- * @param threshold - Time threshold in milliseconds (default: 1000ms)
44
- */ constructor(threshold = 1000){
45
- _define_property(this, "logger", new Logger(QueryPerformanceInterceptor.name));
46
- _define_property(this, "threshold", void 0);
47
- this.threshold = threshold;
48
- }
49
- }
50
- QueryPerformanceInterceptor = _ts_decorate([
51
- Injectable(),
52
- _ts_metadata("design:type", Function),
53
- _ts_metadata("design:paramtypes", [
54
- Number
55
- ])
56
- ], QueryPerformanceInterceptor);