@flusys/nestjs-shared 9.1.2 → 9.2.0

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 (40) hide show
  1. package/README.md +94 -18
  2. package/classes/api-controller.class.d.ts +1 -0
  3. package/classes/api-service.class.d.ts +7 -2
  4. package/classes/hybrid-cache.class.d.ts +17 -4
  5. package/constants/index.d.ts +1 -0
  6. package/constants/permissions.d.ts +9 -0
  7. package/fesm/183.js +7 -7
  8. package/fesm/{282.js → 47.js} +98 -158
  9. package/fesm/470.js +333 -382
  10. package/fesm/667.js +17 -17
  11. package/fesm/681.js +326 -129
  12. package/fesm/{948.js → 997.js} +45 -23
  13. package/fesm/chunks/123.js +17 -9
  14. package/fesm/chunks/37.js +5 -5
  15. package/fesm/classes/index.js +4 -3
  16. package/fesm/constants/index.js +18 -7
  17. package/fesm/decorators/index.js +120 -39
  18. package/fesm/dtos/index.js +1 -1
  19. package/fesm/entities/index.js +8 -8
  20. package/fesm/enums/index.js +2 -2
  21. package/fesm/exceptions/index.js +12 -12
  22. package/fesm/guards/index.js +201 -68
  23. package/fesm/index.js +50 -41
  24. package/fesm/interceptors/index.js +290 -590
  25. package/fesm/interfaces/index.js +17 -8
  26. package/fesm/middlewares/index.js +4 -4
  27. package/fesm/modules/index.js +2 -2
  28. package/fesm/pipes/index.js +8 -8
  29. package/fesm/utils/index.js +137 -478
  30. package/interceptors/idempotency.interceptor.d.ts +14 -2
  31. package/interceptors/slug.interceptor.d.ts +1 -0
  32. package/interfaces/permission-action-registry.interface.d.ts +1 -0
  33. package/interfaces/permission.interface.d.ts +1 -0
  34. package/modules/shared-permission-cache/shared-permission-cache.service.d.ts +3 -1
  35. package/modules/utils/utils.service.d.ts +6 -6
  36. package/package.json +2 -2
  37. package/utils/date-time.util.d.ts +0 -1
  38. package/utils/event-pattern.util.d.ts +1 -0
  39. package/utils/permission-cache-key.util.d.ts +3 -0
  40. package/utils/query-helpers.util.d.ts +2 -2
package/fesm/470.js CHANGED
@@ -4,8 +4,8 @@ import * as __rspack_external_rxjs_operators_2eaa0997 from "rxjs/operators";
4
4
  export const __rspack_esm_id = 470;
5
5
  export const __rspack_esm_ids = [470];
6
6
  export const __webpack_modules__ = {
7
- 504(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
8
- /* import */ var _flusys_nestjs_core__rspack_import_0 = __webpack_require__(436);
7
+ 8504(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
8
+ /* import */ var _flusys_nestjs_core__rspack_import_0 = __webpack_require__(8436);
9
9
 
10
10
  function getJsonColumnType() {
11
11
  const driver = _flusys_nestjs_core__rspack_import_0.envConfig.getTypeOrmConfig().type;
@@ -34,7 +34,7 @@ __webpack_require__.d(__webpack_exports__, {
34
34
 
35
35
 
36
36
  },
37
- 405(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
37
+ 9405(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
38
38
 
39
39
  // EXPORTS
40
40
  __webpack_require__.d(__webpack_exports__, {
@@ -44,6 +44,7 @@ __webpack_require__.d(__webpack_exports__, {
44
44
  wA: () => (/* reexport */ permissions/* .BRANCH_PERMISSIONS */.wA),
45
45
  aO: () => (/* reexport */ permissions/* .COMPANY_ACTION_PERMISSIONS */.aO),
46
46
  t$: () => (/* reexport */ permissions/* .COMPANY_PERMISSIONS */.t$),
47
+ IX: () => (/* reexport */ permissions/* .CROSS_COMPANY_PERMISSIONS */.IX),
47
48
  xC: () => (/* reexport */ (/* inlined export .DOMAIN_EVENT_VERSION */1)),
48
49
  XI: () => (/* reexport */ disabled_event_bus_class/* .DisabledEventBus */.X),
49
50
  V0: () => (/* reexport */ permissions/* .EMAIL_CONFIG_PERMISSIONS */.V0),
@@ -93,27 +94,27 @@ __webpack_require__.d(__webpack_exports__, {
93
94
  });
94
95
 
95
96
  // EXTERNAL MODULE: external "@nestjs/common"
96
- var common_ = __webpack_require__(157);
97
+ var common_ = __webpack_require__(6157);
97
98
  // EXTERNAL MODULE: external "@nestjs/swagger"
98
- var swagger_ = __webpack_require__(176);
99
+ var swagger_ = __webpack_require__(6176);
99
100
  // EXTERNAL MODULE: external "class-transformer"
100
- var external_class_transformer_ = __webpack_require__(812);
101
+ var external_class_transformer_ = __webpack_require__(5812);
101
102
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/constants/message-keys.ts
102
- var message_keys = __webpack_require__(389);
103
+ var message_keys = __webpack_require__(4389);
103
104
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/decorators/index.ts + 4 modules
104
- var src_decorators = __webpack_require__(523);
105
+ var src_decorators = __webpack_require__(9523);
105
106
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/decorators/api-response.decorator.ts
106
107
  var api_response_decorator = __webpack_require__(997);
107
108
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/dtos/index.ts + 6 modules
108
- var src_dtos = __webpack_require__(746);
109
+ var src_dtos = __webpack_require__(3746);
109
110
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/exceptions/index.ts
110
- var exceptions = __webpack_require__(448);
111
+ var exceptions = __webpack_require__(9448);
111
112
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/guards/index.ts + 4 modules
112
- var guards = __webpack_require__(510);
113
+ var guards = __webpack_require__(3510);
113
114
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/interceptors/index.ts + 7 modules
114
- var interceptors = __webpack_require__(863);
115
+ var interceptors = __webpack_require__(6863);
115
116
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/pipes/index.ts + 1 modules
116
- var pipes = __webpack_require__(928);
117
+ var pipes = __webpack_require__(3928);
117
118
  ;// CONCATENATED MODULE: ./projects/nestjs-shared/src/classes/api-controller.class.ts
118
119
  function _ts_decorate(decorators, target, key, desc) {
119
120
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -149,61 +150,147 @@ function _ts_param(paramIndex, decorator) {
149
150
 
150
151
 
151
152
 
152
- /**
153
- * Helper to normalize security config
154
- */ function normalizeSecurity(config) {
155
- if (!config) return {
156
- level: 'public'
157
- };
158
- if (typeof config === 'string') return {
153
+ const ENDPOINT_KEYS = [
154
+ 'insert',
155
+ 'insertMany',
156
+ 'getById',
157
+ 'getByIds',
158
+ 'getAll',
159
+ 'bulkUpsert',
160
+ 'getByFilter',
161
+ 'update',
162
+ 'updateMany',
163
+ 'delete'
164
+ ];
165
+ const DEFAULT_SECURITY = {
166
+ level: 'jwt'
167
+ };
168
+ const SECURITY_RANK = {
169
+ public: 0,
170
+ jwt: 1,
171
+ permission: 2
172
+ };
173
+ /** Where an endpoint left out of a per-endpoint map takes its security from. */ const INHERITED_SECURITY = {
174
+ insertMany: [
175
+ 'insert'
176
+ ],
177
+ updateMany: [
178
+ 'update'
179
+ ],
180
+ getByIds: [
181
+ 'getById',
182
+ 'getAll'
183
+ ],
184
+ getByFilter: [
185
+ 'getById',
186
+ 'getAll'
187
+ ],
188
+ bulkUpsert: [
189
+ 'insert',
190
+ 'update'
191
+ ]
192
+ };
193
+ function normalizeSecurity(config) {
194
+ return typeof config === 'string' ? {
159
195
  level: config
196
+ } : config;
197
+ }
198
+ function toLogicNode(security) {
199
+ if (security.logic) {
200
+ return typeof security.logic === 'string' ? {
201
+ type: 'action',
202
+ actionId: security.logic
203
+ } : security.logic;
204
+ }
205
+ if (!security.permissions?.length) return null;
206
+ return {
207
+ type: 'group',
208
+ operator: security.operator ?? 'AND',
209
+ children: security.permissions.map((actionId)=>({
210
+ type: 'action',
211
+ actionId
212
+ }))
213
+ };
214
+ }
215
+ /** Every requirement of every source must hold - an endpoint doing both an insert and an update needs both. */ function combineSecurity(sources) {
216
+ const strictest = sources.reduce((a, b)=>SECURITY_RANK[b.level] > SECURITY_RANK[a.level] ? b : a);
217
+ if (strictest.level !== 'permission') return strictest;
218
+ const nodes = sources.filter((source)=>source.level === 'permission').map(toLogicNode).filter((node)=>node !== null);
219
+ if (nodes.length <= 1) return nodes.length ? {
220
+ level: 'permission',
221
+ logic: nodes[0]
222
+ } : strictest;
223
+ return {
224
+ level: 'permission',
225
+ logic: {
226
+ type: 'group',
227
+ operator: 'AND',
228
+ children: nodes
229
+ }
160
230
  };
161
- return config;
162
231
  }
163
232
  /**
164
- * Helper to check if endpoint is enabled
165
- */ function isEndpointEnabled(endpoint, enabledEndpoints) {
233
+ * A string or a lone `{ level }` object applies to every endpoint; otherwise it is a per-endpoint map.
234
+ * An endpoint left out of the map inherits from the endpoints it does the work of (`getByFilter` from
235
+ * `getById` / `getAll`, `bulkUpsert` from both `insert` and `update`, ...), and anything still unset -
236
+ * no `security` at all included - requires a JWT, so a forgotten endpoint is never public.
237
+ */ function resolveEndpointSecurity(config) {
238
+ const isGlobal = typeof config === 'string' || !!config && 'level' in config && !ENDPOINT_KEYS.some((key)=>key in config);
239
+ if (isGlobal) {
240
+ const security = normalizeSecurity(config);
241
+ return Object.fromEntries(ENDPOINT_KEYS.map((key)=>[
242
+ key,
243
+ security
244
+ ]));
245
+ }
246
+ const map = config ?? {};
247
+ const explicit = (key)=>{
248
+ const value = map[key];
249
+ return value ? normalizeSecurity(value) : undefined;
250
+ };
251
+ const resolve = (key)=>{
252
+ const own = explicit(key);
253
+ if (own) return own;
254
+ const inherited = (INHERITED_SECURITY[key] ?? []).map(explicit).filter((source)=>source !== undefined);
255
+ if (!inherited.length) return DEFAULT_SECURITY;
256
+ return key === 'bulkUpsert' ? combineSecurity(inherited) : inherited[0];
257
+ };
258
+ return Object.fromEntries(ENDPOINT_KEYS.map((key)=>[
259
+ key,
260
+ resolve(key)
261
+ ]));
262
+ }
263
+ function isEndpointEnabled(endpoint, enabledEndpoints) {
166
264
  if (!enabledEndpoints || enabledEndpoints === 'all') return true;
167
265
  return enabledEndpoints.includes(endpoint);
168
266
  }
169
- /**
170
- * Helper to conditionally exclude endpoint from Swagger if disabled
171
- */ function createEndpointDecorators(security, endpoint, enabledEndpoints) {
267
+ function createEndpointDecorators(security, endpoint, enabledEndpoints) {
172
268
  const decorators = [];
173
- // Add security decorators
174
269
  decorators.push(createSecurityDecorators(security));
175
- // Exclude from Swagger if endpoint is disabled
176
270
  if (!isEndpointEnabled(endpoint, enabledEndpoints)) {
177
271
  decorators.push((0,swagger_.ApiExcludeEndpoint)());
178
272
  }
179
273
  return (0,common_.applyDecorators)(...decorators);
180
274
  }
181
- /**
182
- * Creates security decorators based on configuration
183
- */ function createSecurityDecorators(security) {
275
+ function createSecurityDecorators(security) {
184
276
  const decorators = [];
185
277
  switch(security.level){
186
278
  case 'public':
187
279
  decorators.push((0,src_decorators/* .Public */.w3)());
188
280
  break;
189
281
  case 'jwt':
190
- // Apply JWT authentication guard
191
282
  decorators.push((0,common_.UseGuards)(guards/* .JwtAuthGuard */.TD));
192
283
  decorators.push((0,swagger_.ApiBearerAuth)());
193
284
  break;
194
285
  case 'permission':
195
- // Apply JWT authentication guard (required for permission checks)
196
- // Apply PermissionGuard to check permissions from cache
197
286
  decorators.push((0,common_.UseGuards)(guards/* .JwtAuthGuard */.TD, guards/* .PermissionGuard */.LQ));
198
287
  decorators.push((0,swagger_.ApiBearerAuth)());
199
- // Check for complex logic first
200
288
  if (security.logic) {
201
289
  decorators.push((0,src_decorators/* .RequirePermissionLogic */.rY)(security.logic));
202
290
  } else if (security.permissions && security.permissions.length > 0) {
203
291
  if (security.operator === 'OR') {
204
292
  decorators.push((0,src_decorators/* .RequireAnyPermission */.Dz)(...security.permissions));
205
293
  } else {
206
- // AND is default and most secure
207
294
  decorators.push((0,src_decorators/* .RequirePermission */.Tw)(...security.permissions));
208
295
  }
209
296
  }
@@ -218,7 +305,6 @@ function _ts_param(paramIndex, decorator) {
218
305
  excludeExtraneousValues
219
306
  });
220
307
  }
221
- // Entity name for message keys (defaults to 'item')
222
308
  const entityName = options.entityName ?? 'item';
223
309
  const insertBodyPipe = pipes/* .DtoBodyValidationPipe["for"] */.zT["for"](createDtoClass, [
224
310
  'createdById'
@@ -236,42 +322,8 @@ function _ts_param(paramIndex, decorator) {
236
322
  'createdById',
237
323
  'updatedById'
238
324
  ], true);
239
- // Determine if security is global (applies to all) or per-endpoint
240
- const securityConfig = options.security;
241
- const endpointKeys = [
242
- 'insert',
243
- 'insertMany',
244
- 'getById',
245
- 'getByIds',
246
- 'getAll',
247
- 'bulkUpsert',
248
- 'getByFilter',
249
- 'update',
250
- 'updateMany',
251
- 'delete'
252
- ];
253
- // Global security if:
254
- // 1. It's a string (SecurityLevel)
255
- // 2. It's an object with 'level' property but no endpoint keys
256
- const isGlobalSecurity = typeof securityConfig === 'string' || securityConfig && typeof securityConfig === 'object' && 'level' in securityConfig && !endpointKeys.some((key)=>key in securityConfig);
257
- // Normalize security config for each endpoint
258
- // IMPORTANT: When per-endpoint security is specified, default to 'jwt' for unconfigured endpoints
259
- // to prevent accidentally exposing endpoints without authentication
260
- const defaultSecurity = isGlobalSecurity ? normalizeSecurity(securityConfig) : {
261
- level: 'jwt'
262
- };
263
- const security = {
264
- insert: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.insert),
265
- insertMany: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.insertMany),
266
- getById: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.getById),
267
- getByIds: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.getByIds),
268
- getAll: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.getAll),
269
- getByFilter: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.getByFilter),
270
- bulkUpsert: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.bulkUpsert),
271
- update: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.update),
272
- updateMany: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.updateMany),
273
- delete: isGlobalSecurity ? defaultSecurity : normalizeSecurity(securityConfig?.delete)
274
- };
325
+ const security = resolveEndpointSecurity(options.security);
326
+ const label = entityName.charAt(0).toUpperCase() + entityName.slice(1);
275
327
  let ApiController = class ApiController {
276
328
  service;
277
329
  enabledEndpoints = options.enabledEndpoints ?? 'all';
@@ -282,37 +334,30 @@ function _ts_param(paramIndex, decorator) {
282
334
  if (this.enabledEndpoints === 'all') return true;
283
335
  return Array.isArray(this.enabledEndpoints) && this.enabledEndpoints.includes(endpoint);
284
336
  }
337
+ assertEnabled(endpoint) {
338
+ if (this.isEnabled(endpoint)) return;
339
+ throw new exceptions/* .BaseAppException */.m8({
340
+ message: `Endpoint '${endpoint}' is disabled`,
341
+ messageKey: message_keys/* .ERROR_MESSAGES.ENDPOINT_DISABLED */.UU.ENDPOINT_DISABLED,
342
+ messageVariables: {
343
+ endpoint
344
+ },
345
+ status: common_.HttpStatus.FORBIDDEN
346
+ });
347
+ }
285
348
  async insert(addDto, user) {
286
- if (!this.isEnabled('insert')) {
287
- throw new exceptions/* .BaseAppException */.m8({
288
- message: `Endpoint 'insert' is disabled`,
289
- messageKey: message_keys/* .ERROR_MESSAGES.ENDPOINT_DISABLED */.UU.ENDPOINT_DISABLED,
290
- messageVariables: {
291
- endpoint: 'insert'
292
- },
293
- status: common_.HttpStatus.FORBIDDEN
294
- });
295
- }
349
+ this.assertEnabled('insert');
296
350
  const entity = await this.service.insert(addDto, user);
297
351
  const data = toDto(responseDtoClass, entity);
298
352
  return {
299
353
  success: true,
300
- message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} created successfully`,
354
+ message: `${label} created successfully`,
301
355
  messageKey: `${entityName}.create.success`,
302
356
  data
303
357
  };
304
358
  }
305
359
  async insertMany(addDto, user) {
306
- if (!this.isEnabled('insertMany')) {
307
- throw new exceptions/* .BaseAppException */.m8({
308
- message: `Endpoint 'insertMany' is disabled`,
309
- messageKey: message_keys/* .ERROR_MESSAGES.ENDPOINT_DISABLED */.UU.ENDPOINT_DISABLED,
310
- messageVariables: {
311
- endpoint: 'insertMany'
312
- },
313
- status: common_.HttpStatus.FORBIDDEN
314
- });
315
- }
360
+ this.assertEnabled('insertMany');
316
361
  const entities = await this.service.insertMany(addDto, user);
317
362
  const data = entities.map((item)=>toDto(responseDtoClass, item));
318
363
  return {
@@ -331,36 +376,18 @@ function _ts_param(paramIndex, decorator) {
331
376
  };
332
377
  }
333
378
  async getById(id, body, user) {
334
- if (!this.isEnabled('getById')) {
335
- throw new exceptions/* .BaseAppException */.m8({
336
- message: `Endpoint 'getById' is disabled`,
337
- messageKey: message_keys/* .ERROR_MESSAGES.ENDPOINT_DISABLED */.UU.ENDPOINT_DISABLED,
338
- messageVariables: {
339
- endpoint: 'getById'
340
- },
341
- status: common_.HttpStatus.FORBIDDEN
342
- });
343
- }
379
+ this.assertEnabled('getById');
344
380
  const entity = await this.service.findById(id, user, body?.select);
345
381
  const data = toDto(responseDtoClass, entity);
346
382
  return {
347
383
  success: true,
348
- message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} retrieved successfully`,
384
+ message: `${label} retrieved successfully`,
349
385
  messageKey: `${entityName}.get.success`,
350
386
  data
351
387
  };
352
388
  }
353
389
  async getByIds(body, user) {
354
- if (!this.isEnabled('getByIds')) {
355
- throw new exceptions/* .BaseAppException */.m8({
356
- message: `Endpoint 'getByIds' is disabled`,
357
- messageKey: message_keys/* .ERROR_MESSAGES.ENDPOINT_DISABLED */.UU.ENDPOINT_DISABLED,
358
- messageVariables: {
359
- endpoint: 'getByIds'
360
- },
361
- status: common_.HttpStatus.FORBIDDEN
362
- });
363
- }
390
+ this.assertEnabled('getByIds');
364
391
  const entities = await this.service.findByIds(body.ids, user, body?.select);
365
392
  const data = entities.map((item)=>toDto(responseDtoClass, item));
366
393
  return {
@@ -378,36 +405,18 @@ function _ts_param(paramIndex, decorator) {
378
405
  };
379
406
  }
380
407
  async update(updateDto, user) {
381
- if (!this.isEnabled('update')) {
382
- throw new exceptions/* .BaseAppException */.m8({
383
- message: `Endpoint 'update' is disabled`,
384
- messageKey: message_keys/* .ERROR_MESSAGES.ENDPOINT_DISABLED */.UU.ENDPOINT_DISABLED,
385
- messageVariables: {
386
- endpoint: 'update'
387
- },
388
- status: common_.HttpStatus.FORBIDDEN
389
- });
390
- }
408
+ this.assertEnabled('update');
391
409
  const entity = await this.service.update(updateDto, user);
392
410
  const data = toDto(responseDtoClass, entity);
393
411
  return {
394
412
  success: true,
395
- message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} updated successfully`,
413
+ message: `${label} updated successfully`,
396
414
  messageKey: `${entityName}.update.success`,
397
415
  data
398
416
  };
399
417
  }
400
418
  async updateMany(updateDtos, user) {
401
- if (!this.isEnabled('updateMany')) {
402
- throw new exceptions/* .BaseAppException */.m8({
403
- message: `Endpoint 'updateMany' is disabled`,
404
- messageKey: message_keys/* .ERROR_MESSAGES.ENDPOINT_DISABLED */.UU.ENDPOINT_DISABLED,
405
- messageVariables: {
406
- endpoint: 'updateMany'
407
- },
408
- status: common_.HttpStatus.FORBIDDEN
409
- });
410
- }
419
+ this.assertEnabled('updateMany');
411
420
  const entities = await this.service.updateMany(updateDtos, user);
412
421
  const data = entities.map((item)=>toDto(responseDtoClass, item));
413
422
  return {
@@ -426,16 +435,7 @@ function _ts_param(paramIndex, decorator) {
426
435
  };
427
436
  }
428
437
  async bulkUpsert(dtos, user) {
429
- if (!this.isEnabled('bulkUpsert')) {
430
- throw new exceptions/* .BaseAppException */.m8({
431
- message: `Endpoint 'bulkUpsert' is disabled`,
432
- messageKey: message_keys/* .ERROR_MESSAGES.ENDPOINT_DISABLED */.UU.ENDPOINT_DISABLED,
433
- messageVariables: {
434
- endpoint: 'bulkUpsert'
435
- },
436
- status: common_.HttpStatus.FORBIDDEN
437
- });
438
- }
438
+ this.assertEnabled('bulkUpsert');
439
439
  const toInsert = [];
440
440
  const toUpdate = [];
441
441
  for (const dto of dtos){
@@ -463,16 +463,7 @@ function _ts_param(paramIndex, decorator) {
463
463
  };
464
464
  }
465
465
  async getByFilter(filter, user) {
466
- if (!this.isEnabled('getByFilter')) {
467
- throw new exceptions/* .BaseAppException */.m8({
468
- message: `Endpoint 'getByFilter' is disabled`,
469
- messageKey: message_keys/* .ERROR_MESSAGES.ENDPOINT_DISABLED */.UU.ENDPOINT_DISABLED,
470
- messageVariables: {
471
- endpoint: 'getByFilter'
472
- },
473
- status: common_.HttpStatus.FORBIDDEN
474
- });
475
- }
466
+ this.assertEnabled('getByFilter');
476
467
  const result = await this.service.getAll('', {
477
468
  filter,
478
469
  pagination: {
@@ -487,22 +478,13 @@ function _ts_param(paramIndex, decorator) {
487
478
  const data = toDto(responseDtoClass, entity);
488
479
  return {
489
480
  success: true,
490
- message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)} retrieved successfully`,
481
+ message: `${label} retrieved successfully`,
491
482
  messageKey: `${entityName}.get.by.filter.success`,
492
483
  data
493
484
  };
494
485
  }
495
486
  async getAll(filterAndPaginationDto, user, search) {
496
- if (!this.isEnabled('getAll')) {
497
- throw new exceptions/* .BaseAppException */.m8({
498
- message: `Endpoint 'getAll' is disabled`,
499
- messageKey: message_keys/* .ERROR_MESSAGES.ENDPOINT_DISABLED */.UU.ENDPOINT_DISABLED,
500
- messageVariables: {
501
- endpoint: 'getAll'
502
- },
503
- status: common_.HttpStatus.FORBIDDEN
504
- });
505
- }
487
+ this.assertEnabled('getAll');
506
488
  const result = await this.service.getAll(search ?? '', filterAndPaginationDto, user);
507
489
  const data = result.data.map((item)=>toDto(responseDtoClass, item));
508
490
  const page = filterAndPaginationDto.pagination?.currentPage ?? 0;
@@ -510,7 +492,7 @@ function _ts_param(paramIndex, decorator) {
510
492
  const totalPages = pageSize > 0 ? Math.ceil(result.total / pageSize) : 1;
511
493
  return {
512
494
  success: true,
513
- message: `${entityName.charAt(0).toUpperCase() + entityName.slice(1)}s retrieved successfully`,
495
+ message: `${label}s retrieved successfully`,
514
496
  messageKey: `${entityName}.get.all.success`,
515
497
  data,
516
498
  meta: {
@@ -524,16 +506,7 @@ function _ts_param(paramIndex, decorator) {
524
506
  };
525
507
  }
526
508
  async delete(deleteDto, user) {
527
- if (!this.isEnabled('delete')) {
528
- throw new exceptions/* .BaseAppException */.m8({
529
- message: `Endpoint 'delete' is disabled`,
530
- messageKey: message_keys/* .ERROR_MESSAGES.ENDPOINT_DISABLED */.UU.ENDPOINT_DISABLED,
531
- messageVariables: {
532
- endpoint: 'delete'
533
- },
534
- status: common_.HttpStatus.FORBIDDEN
535
- });
536
- }
509
+ this.assertEnabled('delete');
537
510
  await this.service.delete(deleteDto, user);
538
511
  const count = Array.isArray(deleteDto.id) ? deleteDto.id.length : 1;
539
512
  const action = deleteDto.type === 'restore' ? 'restore' : 'delete';
@@ -847,23 +820,23 @@ function _ts_param(paramIndex, decorator) {
847
820
  }
848
821
 
849
822
  // EXTERNAL MODULE: external "typeorm"
850
- var external_typeorm_ = __webpack_require__(158);
823
+ var external_typeorm_ = __webpack_require__(7158);
851
824
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/constants/index.ts
852
- var constants = __webpack_require__(43);
825
+ var constants = __webpack_require__(6043);
853
826
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/decorators/log-action.decorator.ts
854
- var log_action_decorator = __webpack_require__(946);
827
+ var log_action_decorator = __webpack_require__(1946);
855
828
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/modules/utils/utils.service.ts
856
- var utils_service = __webpack_require__(169);
829
+ var utils_service = __webpack_require__(1169);
857
830
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/utils/cache-tenant.util.ts
858
- var cache_tenant_util = __webpack_require__(154);
831
+ var cache_tenant_util = __webpack_require__(8154);
859
832
  // EXTERNAL MODULE: external "crypto"
860
- var external_crypto_ = __webpack_require__(823);
861
- // EXTERNAL MODULE: ./projects/nestjs-shared/src/middlewares/logger.middleware.ts + 2 modules
862
- var logger_middleware = __webpack_require__(591);
833
+ var external_crypto_ = __webpack_require__(7823);
834
+ // EXTERNAL MODULE: ./projects/nestjs-shared/src/middlewares/logger.middleware.ts + 1 modules
835
+ var logger_middleware = __webpack_require__(8244);
863
836
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/utils/event-pattern.util.ts
864
- var event_pattern_util = __webpack_require__(456);
837
+ var event_pattern_util = __webpack_require__(8456);
865
838
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/classes/event-bus.registry.ts
866
- var event_bus_registry = __webpack_require__(228);
839
+ var event_bus_registry = __webpack_require__(3228);
867
840
  ;// CONCATENATED MODULE: ./projects/nestjs-shared/src/classes/domain-event.publisher.ts
868
841
 
869
842
 
@@ -930,7 +903,7 @@ function collectIds(rows) {
930
903
  }
931
904
 
932
905
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/classes/hybrid-cache.class.ts + 3 modules
933
- var hybrid_cache_class = __webpack_require__(375);
906
+ var hybrid_cache_class = __webpack_require__(5375);
934
907
  ;// CONCATENATED MODULE: ./projects/nestjs-shared/src/classes/api-service.class.ts
935
908
  function api_service_class_ts_decorate(decorators, target, key, desc) {
936
909
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -975,6 +948,7 @@ class ApiService {
975
948
  repositoryInitialized = false;
976
949
  _entity;
977
950
  _dataSourceProvider;
951
+ logger = new common_.Logger(ApiService.name);
978
952
  /**
979
953
  * `entity` is the table this service reads and writes. A service whose table
980
954
  * depends on runtime config - the company feature, say - passes the ternary
@@ -995,6 +969,46 @@ class ApiService {
995
969
  /** Cached rows belong to the current tenant; pass this to every `UtilsService` cache helper. */ cacheTenant() {
996
970
  return (0,cache_tenant_util/* .cacheTenantId */.q)(this._dataSourceProvider);
997
971
  }
972
+ /**
973
+ * Every entity whose rows end up in this service's cached reads. Override to add the entities a
974
+ * read joins or embeds, so a write to any of them also invalidates this service's cache.
975
+ */ cacheDependencies() {
976
+ return [
977
+ this.entityName
978
+ ];
979
+ }
980
+ /**
981
+ * Override to send one list read straight to the database - e.g. a filter that reads tables whose
982
+ * writes never move this service's stamps. Decided per call, so concurrent reads never interfere.
983
+ */ bypassListCache(_filterAndPaginationDto) {
984
+ return false;
985
+ }
986
+ /** Invalidates this entity's cached reads - and every service that lists it as a dependency */ async invalidateCache() {
987
+ await this.utilsService.clearCache(this.entityName, this.cacheManager, this.cacheTenant());
988
+ }
989
+ /** null = caching is off or the cache is unreachable; the read then goes straight to the database */ async readCacheStamp(bypass = false) {
990
+ if (!this.isCacheable || bypass) return null;
991
+ try {
992
+ return await this.utilsService.getCacheStamp(this.cacheManager, this.cacheDependencies(), this.cacheTenant());
993
+ } catch (error) {
994
+ this.logger.warn(`Cache unavailable, reading '${this.entityName}' from the database: ${error.message}`);
995
+ return null;
996
+ }
997
+ }
998
+ async readCached(key) {
999
+ try {
1000
+ return await this.cacheManager.get(key);
1001
+ } catch {
1002
+ return undefined;
1003
+ }
1004
+ }
1005
+ async writeCached(key, value) {
1006
+ try {
1007
+ await this.cacheManager.set(key, value);
1008
+ } catch (error) {
1009
+ this.logger.warn(`Cache write failed for '${this.entityName}': ${error.message}`);
1010
+ }
1011
+ }
998
1012
  async ensureDataSourceRepository() {
999
1013
  if (!this.repositoryInitialized) {
1000
1014
  const provider = this.getDataSourceProvider();
@@ -1124,6 +1138,7 @@ class ApiService {
1124
1138
  await this.ensureDataSourceRepository();
1125
1139
  try {
1126
1140
  select?.forEach((field)=>this.assertColumnProperty(field));
1141
+ const stamp = await this.readCacheStamp();
1127
1142
  const query = this.repository.createQueryBuilder(this.entityName);
1128
1143
  query.where({
1129
1144
  id
@@ -1131,27 +1146,26 @@ class ApiService {
1131
1146
  const { query: finalQuery, isRaw } = await this.getSelectQuery(query, user, select);
1132
1147
  let result;
1133
1148
  let cacheKey = '';
1134
- if (this.isCacheable) {
1135
- cacheKey = this.utilsService.getCacheKey(this.entityName, finalQuery.getQueryAndParameters(), id, this.cacheTenant());
1136
- result = await this.cacheManager.get(cacheKey);
1149
+ if (stamp !== null) {
1150
+ cacheKey = this.utilsService.getCacheKey(this.entityName, {
1151
+ raw: isRaw,
1152
+ query: finalQuery.getQueryAndParameters()
1153
+ }, id, this.cacheTenant(), stamp);
1154
+ result = await this.readCached(cacheKey);
1137
1155
  }
1138
1156
  if (!result) {
1139
1157
  if (isRaw) {
1140
1158
  const rawOutput = await finalQuery.getRawMany();
1141
- const convertResult = this.convertEntityListToResponseListDto(rawOutput, isRaw);
1142
- if (convertResult.length) result = convertResult[0];
1159
+ result = this.convertEntityListToResponseListDto(rawOutput, isRaw)[0];
1143
1160
  } else {
1144
1161
  const singleOutput = await finalQuery.getOne();
1145
- if (!singleOutput) throw new common_.NotFoundException({
1146
- message: 'No Data Found',
1147
- messageKey: constants/* .ERROR_MESSAGES.NOT_FOUND */.UU.NOT_FOUND
1148
- });
1149
- result = this.convertEntityToResponseDto(singleOutput, false);
1150
- }
1151
- if (this.isCacheable) {
1152
- await this.cacheManager.set(cacheKey, result);
1153
- await this.utilsService.trackCacheKey(cacheKey, this.entityName, this.cacheManager, id, this.cacheTenant());
1162
+ if (singleOutput) result = this.convertEntityToResponseDto(singleOutput, false);
1154
1163
  }
1164
+ if (!result) throw new common_.NotFoundException({
1165
+ message: 'No Data Found',
1166
+ messageKey: constants/* .ERROR_MESSAGES.NOT_FOUND */.UU.NOT_FOUND
1167
+ });
1168
+ if (stamp !== null) await this.writeCached(cacheKey, result);
1155
1169
  }
1156
1170
  return result;
1157
1171
  } catch (error) {
@@ -1168,6 +1182,7 @@ class ApiService {
1168
1182
  let total;
1169
1183
  let isRaw = false;
1170
1184
  filterAndPaginationDto.select?.forEach((field)=>this.assertColumnProperty(field));
1185
+ const stamp = await this.readCacheStamp(this.bypassListCache(filterAndPaginationDto));
1171
1186
  let query = this.repository.createQueryBuilder(this.entityName);
1172
1187
  if (filterAndPaginationDto.filter) {
1173
1188
  const { query: filterQuery, isRaw: filterIsRaw } = await this.getFilterQuery(query, filterAndPaginationDto.filter, user);
@@ -1201,13 +1216,13 @@ class ApiService {
1201
1216
  }
1202
1217
  }
1203
1218
  let cacheKey = '';
1204
- if (this.isCacheable) {
1219
+ if (stamp !== null) {
1205
1220
  cacheKey = this.utilsService.getCacheKey(this.entityName, {
1206
1221
  raw: isRaw,
1207
1222
  pagination: filterAndPaginationDto.pagination,
1208
1223
  query: query.getQueryAndParameters()
1209
- }, undefined, this.cacheTenant());
1210
- const cached = await this.cacheManager.get(cacheKey);
1224
+ }, undefined, this.cacheTenant(), stamp);
1225
+ const cached = await this.readCached(cacheKey);
1211
1226
  if (cached) {
1212
1227
  return cached;
1213
1228
  }
@@ -1216,32 +1231,16 @@ class ApiService {
1216
1231
  const rawSubQuery = query.clone().offset(undefined).limit(undefined);
1217
1232
  rawSubQuery.expressionMap.orderBys = {};
1218
1233
  rawSubQuery.distinct(true);
1234
+ const [subQuerySql, parameters] = rawSubQuery.getQueryAndParameters();
1219
1235
  const countSql = `
1220
1236
  SELECT COUNT(*) as count
1221
1237
  FROM (
1222
1238
  SELECT COUNT(*) as count
1223
- FROM (${rawSubQuery.getQuery()}) AS subquery
1239
+ FROM (${subQuerySql}) AS subquery
1224
1240
  GROUP BY subquery.${this.entityName}_id
1225
1241
  ) AS final
1226
1242
  `;
1227
- const parameters = rawSubQuery.getParameters();
1228
- const orderedValues = [];
1229
- const isPostgres = this.repository.manager.connection.options.type === 'postgres';
1230
- let paramIndex = 0;
1231
- const convertedQuery = countSql.replace(/:(\w+)/g, (_, key)=>{
1232
- if (!(key in parameters)) {
1233
- throw new common_.InternalServerErrorException({
1234
- message: `Missing parameter value for: ${key}`,
1235
- messageKey: constants/* .SYSTEM_MESSAGES.MISSING_PARAMETER */.hg.MISSING_PARAMETER,
1236
- messageVariables: {
1237
- key
1238
- }
1239
- });
1240
- }
1241
- orderedValues.push(parameters[key]);
1242
- return isPostgres ? `$${++paramIndex}` : '?';
1243
- });
1244
- const result = await this.repository.query(convertedQuery, orderedValues);
1243
+ const result = await this.repository.query(countSql, parameters);
1245
1244
  total = Number(result[0]?.count || 0);
1246
1245
  data = await query.getRawMany();
1247
1246
  } else {
@@ -1252,10 +1251,7 @@ class ApiService {
1252
1251
  data: convertedFinalData,
1253
1252
  total
1254
1253
  };
1255
- if (this.isCacheable) {
1256
- await this.cacheManager.set(cacheKey, finalResponse);
1257
- await this.utilsService.trackCacheKey(cacheKey, this.entityName, this.cacheManager, undefined, this.cacheTenant());
1258
- }
1254
+ if (stamp !== null) await this.writeCached(cacheKey, finalResponse);
1259
1255
  return finalResponse;
1260
1256
  } catch (error) {
1261
1257
  this.handleError(error);
@@ -1290,10 +1286,7 @@ class ApiService {
1290
1286
  });
1291
1287
  await this.afterDeleteOperation(ids, option, user, queryRunner);
1292
1288
  await queryRunner.commitTransaction();
1293
- await Promise.all([
1294
- this.clearCacheForAll(),
1295
- this.clearCacheForId(ids)
1296
- ]);
1289
+ await this.invalidateCache();
1297
1290
  await this.publishLifecycleEvents([
1298
1291
  {
1299
1292
  action: DELETE_EVENT_ACTIONS[option.type],
@@ -1335,12 +1328,6 @@ class ApiService {
1335
1328
  action
1336
1329
  });
1337
1330
  }
1338
- async clearCacheForAll() {
1339
- await this.utilsService.clearCache(this.entityName, this.cacheManager, undefined, this.cacheTenant());
1340
- }
1341
- async clearCacheForId(entities) {
1342
- await Promise.all(entities.map((item)=>this.utilsService.clearCache(this.entityName, this.cacheManager, item.id, this.cacheTenant())));
1343
- }
1344
1331
  handleError(error) {
1345
1332
  if (error instanceof Error) {
1346
1333
  throw error;
@@ -1368,12 +1355,7 @@ class ApiService {
1368
1355
  const { saved, returnFirst, eventGroups } = await fn(qr);
1369
1356
  await qr.commitTransaction();
1370
1357
  const savedArray = this.ensureArray(saved);
1371
- if (this.isCacheable) {
1372
- await Promise.all([
1373
- this.clearCacheForAll(),
1374
- this.clearCacheForId(savedArray)
1375
- ]);
1376
- }
1358
+ await this.invalidateCache();
1377
1359
  await this.publishLifecycleEvents(eventGroups ?? (action ? [
1378
1360
  {
1379
1361
  action,
@@ -1576,11 +1558,11 @@ ApiService = api_service_class_ts_decorate([
1576
1558
  ], ApiService);
1577
1559
 
1578
1560
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/classes/disabled-event-bus.class.ts
1579
- var disabled_event_bus_class = __webpack_require__(864);
1561
+ var disabled_event_bus_class = __webpack_require__(1864);
1580
1562
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/classes/event-transports/event-transport.registry.ts
1581
- var event_transport_registry = __webpack_require__(149);
1563
+ var event_transport_registry = __webpack_require__(7149);
1582
1564
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/classes/event-transports/memory-transport.ts + 1 modules
1583
- var memory_transport = __webpack_require__(346);
1565
+ var memory_transport = __webpack_require__(9346);
1584
1566
  ;// CONCATENATED MODULE: ./projects/nestjs-shared/src/classes/event-transports/index.ts
1585
1567
 
1586
1568
  // Built-in transport (no external dependencies)
@@ -1590,13 +1572,13 @@ var memory_transport = __webpack_require__(346);
1590
1572
  // - import { KafkaTransport } from '@flusys/nestjs-shared/classes/event-transports/kafka-transport.optional';
1591
1573
 
1592
1574
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/classes/handle-custome-db.ts
1593
- var handle_custome_db = __webpack_require__(504);
1575
+ var handle_custome_db = __webpack_require__(8504);
1594
1576
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/classes/hybrid-event-bus.class.ts
1595
- var hybrid_event_bus_class = __webpack_require__(710);
1577
+ var hybrid_event_bus_class = __webpack_require__(5710);
1596
1578
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/classes/winston.logger.class.ts + 5 modules
1597
- var winston_logger_class = __webpack_require__(245);
1579
+ var winston_logger_class = __webpack_require__(6245);
1598
1580
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/constants/permissions.ts
1599
- var permissions = __webpack_require__(975);
1581
+ var permissions = __webpack_require__(8975);
1600
1582
  ;// CONCATENATED MODULE: ./projects/nestjs-shared/src/classes/index.ts
1601
1583
 
1602
1584
 
@@ -1614,9 +1596,9 @@ var permissions = __webpack_require__(975);
1614
1596
 
1615
1597
  },
1616
1598
  997(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
1617
- /* import */ var _dtos_index_js__rspack_import_0 = __webpack_require__(746);
1618
- /* import */ var _nestjs_common__rspack_import_1 = __webpack_require__(157);
1619
- /* import */ var _nestjs_swagger__rspack_import_2 = __webpack_require__(176);
1599
+ /* import */ var _dtos_index_js__rspack_import_0 = __webpack_require__(3746);
1600
+ /* import */ var _nestjs_common__rspack_import_1 = __webpack_require__(6157);
1601
+ /* import */ var _nestjs_swagger__rspack_import_2 = __webpack_require__(6176);
1620
1602
 
1621
1603
 
1622
1604
 
@@ -1667,7 +1649,7 @@ __webpack_require__.d(__webpack_exports__, {
1667
1649
 
1668
1650
 
1669
1651
  },
1670
- 523(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
1652
+ 9523(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
1671
1653
 
1672
1654
  // EXPORTS
1673
1655
  __webpack_require__.d(__webpack_exports__, {
@@ -1684,7 +1666,7 @@ __webpack_require__.d(__webpack_exports__, {
1684
1666
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/decorators/api-response.decorator.ts
1685
1667
  var api_response_decorator = __webpack_require__(997);
1686
1668
  // EXTERNAL MODULE: external "@nestjs/common"
1687
- var common_ = __webpack_require__(157);
1669
+ var common_ = __webpack_require__(6157);
1688
1670
  ;// CONCATENATED MODULE: ./projects/nestjs-shared/src/decorators/current-user.decorator.ts
1689
1671
 
1690
1672
  /**
@@ -1706,9 +1688,9 @@ var common_ = __webpack_require__(157);
1706
1688
  });
1707
1689
 
1708
1690
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/decorators/log-action.decorator.ts
1709
- var log_action_decorator = __webpack_require__(946);
1691
+ var log_action_decorator = __webpack_require__(1946);
1710
1692
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/constants/index.ts
1711
- var constants = __webpack_require__(43);
1693
+ var constants = __webpack_require__(6043);
1712
1694
  ;// CONCATENATED MODULE: ./projects/nestjs-shared/src/decorators/on-domain-event.decorator.ts
1713
1695
 
1714
1696
 
@@ -1782,10 +1764,12 @@ var constants = __webpack_require__(43);
1782
1764
 
1783
1765
 
1784
1766
  },
1785
- 946(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
1786
- /* import */ var _nestjs_common__rspack_import_0 = __webpack_require__(157);
1787
- /* import */ var _classes_winston_logger_class_js__rspack_import_1 = __webpack_require__(245);
1788
- /* import */ var _middlewares_logger_middleware_js__rspack_import_2 = __webpack_require__(591);
1767
+ 1946(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
1768
+ /* import */ var _nestjs_common__rspack_import_0 = __webpack_require__(6157);
1769
+ /* import */ var _classes_winston_logger_class_js__rspack_import_1 = __webpack_require__(6245);
1770
+ /* import */ var _middlewares_logger_middleware_js__rspack_import_2 = __webpack_require__(8244);
1771
+ /* import */ var _utils_event_pattern_util_js__rspack_import_3 = __webpack_require__(8456);
1772
+
1789
1773
 
1790
1774
 
1791
1775
 
@@ -1825,7 +1809,6 @@ function LogAction(options = {}) {
1825
1809
  const methodName = String(propertyKey);
1826
1810
  descriptor.value = async function(...args) {
1827
1811
  const startTime = Date.now();
1828
- // Resolve module: decorator option > instance property > undefined
1829
1812
  const moduleName = options.module || this.moduleName;
1830
1813
  const logger = createLoggerAdapter(className, moduleName);
1831
1814
  const action = options.action || `${this.entityName || className}.${methodName}`;
@@ -1841,7 +1824,7 @@ function LogAction(options = {}) {
1841
1824
  const startMessage = `Executing action method "${action}"`;
1842
1825
  const startContext = options.includeParams ? {
1843
1826
  ...context,
1844
- params: sanitizeParams(args)
1827
+ params: (0,_utils_event_pattern_util_js__rspack_import_3/* .sanitizeEventPayload */.IN)(args)
1845
1828
  } : context;
1846
1829
  logger.log(logLevel, startMessage, startContext);
1847
1830
  try {
@@ -1875,30 +1858,6 @@ function LogAction(options = {}) {
1875
1858
  return descriptor;
1876
1859
  };
1877
1860
  }
1878
- function sanitizeParams(args) {
1879
- return args.map((arg)=>{
1880
- if (arg === null || arg === undefined) return arg;
1881
- if (typeof arg === 'object') {
1882
- const sanitized = {
1883
- ...arg
1884
- };
1885
- const sensitiveKeys = [
1886
- 'password',
1887
- 'secret',
1888
- 'token',
1889
- 'apiKey',
1890
- 'authorization'
1891
- ];
1892
- for (const key of Object.keys(sanitized)){
1893
- if (sensitiveKeys.some((sk)=>key.toLowerCase().includes(sk))) {
1894
- sanitized[key] = '[REDACTED]';
1895
- }
1896
- }
1897
- return sanitized;
1898
- }
1899
- return arg;
1900
- });
1901
- }
1902
1861
  function getResultType(result) {
1903
1862
  if (result === null) return 'null';
1904
1863
  if (result === undefined) return 'undefined';
@@ -1932,9 +1891,9 @@ __webpack_require__.d(__webpack_exports__, {
1932
1891
 
1933
1892
 
1934
1893
  },
1935
- 514(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
1936
- /* import */ var _nestjs_common__rspack_import_0 = __webpack_require__(157);
1937
- /* import */ var _constants_message_keys_js__rspack_import_1 = __webpack_require__(389);
1894
+ 5514(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
1895
+ /* import */ var _nestjs_common__rspack_import_0 = __webpack_require__(6157);
1896
+ /* import */ var _constants_message_keys_js__rspack_import_1 = __webpack_require__(4389);
1938
1897
 
1939
1898
 
1940
1899
  class BaseAppException extends _nestjs_common__rspack_import_0.HttpException {
@@ -2055,9 +2014,9 @@ __webpack_require__.d(__webpack_exports__, {
2055
2014
 
2056
2015
 
2057
2016
  },
2058
- 448(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
2059
- /* import */ var _base_app_exception_js__rspack_import_0 = __webpack_require__(514);
2060
- /* import */ var _permission_exception_js__rspack_import_1 = __webpack_require__(374);
2017
+ 9448(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
2018
+ /* import */ var _base_app_exception_js__rspack_import_0 = __webpack_require__(5514);
2019
+ /* import */ var _permission_exception_js__rspack_import_1 = __webpack_require__(8374);
2061
2020
 
2062
2021
 
2063
2022
 
@@ -2077,7 +2036,7 @@ __webpack_require__.d(__webpack_exports__, {
2077
2036
 
2078
2037
 
2079
2038
  },
2080
- 510(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
2039
+ 3510(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
2081
2040
 
2082
2041
  // EXPORTS
2083
2042
  __webpack_require__.d(__webpack_exports__, {
@@ -2087,13 +2046,13 @@ __webpack_require__.d(__webpack_exports__, {
2087
2046
  });
2088
2047
 
2089
2048
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/constants/index.ts
2090
- var constants = __webpack_require__(43);
2091
- // EXTERNAL MODULE: ./projects/nestjs-shared/src/middlewares/logger.middleware.ts + 2 modules
2092
- var logger_middleware = __webpack_require__(591);
2049
+ var constants = __webpack_require__(6043);
2050
+ // EXTERNAL MODULE: ./projects/nestjs-shared/src/middlewares/logger.middleware.ts + 1 modules
2051
+ var logger_middleware = __webpack_require__(8244);
2093
2052
  // EXTERNAL MODULE: external "@nestjs/common"
2094
- var common_ = __webpack_require__(157);
2053
+ var common_ = __webpack_require__(6157);
2095
2054
  // EXTERNAL MODULE: external "@nestjs/core"
2096
- var core_ = __webpack_require__(75);
2055
+ var core_ = __webpack_require__(3075);
2097
2056
  ;// CONCATENATED MODULE: external "@nestjs/passport"
2098
2057
 
2099
2058
  ;// CONCATENATED MODULE: ./projects/nestjs-shared/src/guards/jwt-auth.guard.ts
@@ -2221,9 +2180,9 @@ OptionalJwtGuard = optional_jwt_auth_guard_ts_decorate([
2221
2180
  ], OptionalJwtGuard);
2222
2181
 
2223
2182
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/exceptions/permission.exception.ts
2224
- var permission_exception = __webpack_require__(374);
2183
+ var permission_exception = __webpack_require__(8374);
2225
2184
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/modules/shared-permission-cache/shared-permission-cache.service.ts
2226
- var shared_permission_cache_service = __webpack_require__(657);
2185
+ var shared_permission_cache_service = __webpack_require__(4657);
2227
2186
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/utils/permission-match.util.ts
2228
2187
  var permission_match_util = __webpack_require__(788);
2229
2188
  ;// CONCATENATED MODULE: ./projects/nestjs-shared/src/guards/permission.guard.ts
@@ -2273,13 +2232,15 @@ class PermissionGuard {
2273
2232
  context.getHandler(),
2274
2233
  context.getClass()
2275
2234
  ]);
2276
- if (!permissionConfig) return true;
2235
+ if (permissionConfig === undefined || permissionConfig === null) return true;
2277
2236
  const request = context.switchToHttp().getRequest();
2278
2237
  const user = request.user;
2279
2238
  if (!user) throw new common_.UnauthorizedException({
2280
2239
  message: 'Authentication required',
2281
2240
  messageKey: constants/* .AUTH_MESSAGES.TOKEN_REQUIRED */.mK.TOKEN_REQUIRED
2282
2241
  });
2242
+ const logicNode = this.normalizeToLogicNode(permissionConfig);
2243
+ if (!logicNode) throw new permission_exception/* .InsufficientPermissionsException */.z8([], 'AND');
2283
2244
  if (!this.permissionCacheService?.isCacheConfigured()) {
2284
2245
  throw new permission_exception/* .PermissionSystemUnavailableException */.GS();
2285
2246
  }
@@ -2291,8 +2252,6 @@ class PermissionGuard {
2291
2252
  if (!userPermissions || userPermissions.length === 0) {
2292
2253
  throw new permission_exception/* .NoPermissionsFoundException */.d$();
2293
2254
  }
2294
- const logicNode = this.normalizeToLogicNode(permissionConfig);
2295
- if (!logicNode) return true;
2296
2255
  const result = (0,permission_match_util/* .evaluatePermissionLogic */.Ng)(logicNode, userPermissions);
2297
2256
  if (!result.passed) {
2298
2257
  throw new permission_exception/* .InsufficientPermissionsException */.z8(result.missingPermissions, result.operator);
@@ -2300,14 +2259,12 @@ class PermissionGuard {
2300
2259
  return true;
2301
2260
  }
2302
2261
  normalizeToLogicNode(config) {
2303
- // string - single permission
2304
2262
  if (typeof config === 'string') {
2305
2263
  return config ? {
2306
2264
  type: 'action',
2307
2265
  actionId: config
2308
2266
  } : null;
2309
2267
  }
2310
- // string[] - treat as AND of multiple permissions
2311
2268
  if (Array.isArray(config)) {
2312
2269
  if (config.length === 0) return null;
2313
2270
  if (config.length === 1) return {
@@ -2323,7 +2280,6 @@ class PermissionGuard {
2323
2280
  }))
2324
2281
  };
2325
2282
  }
2326
- // SimplePermissionConfig - { permissions: string[], operator: 'AND'|'OR' }
2327
2283
  if ('permissions' in config && Array.isArray(config.permissions)) {
2328
2284
  const simple = config;
2329
2285
  if (simple.permissions.length === 0) return null;
@@ -2340,7 +2296,6 @@ class PermissionGuard {
2340
2296
  }))
2341
2297
  };
2342
2298
  }
2343
- // ILogicNode
2344
2299
  return config;
2345
2300
  }
2346
2301
  }
@@ -2363,7 +2318,7 @@ PermissionGuard = permission_guard_ts_decorate([
2363
2318
 
2364
2319
 
2365
2320
  },
2366
- 863(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
2321
+ 6863(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
2367
2322
 
2368
2323
  // EXPORTS
2369
2324
  __webpack_require__.d(__webpack_exports__, {
@@ -2374,11 +2329,12 @@ __webpack_require__.d(__webpack_exports__, {
2374
2329
  Ul: () => (/* reexport */ SetDeletedByOnBody),
2375
2330
  _x: () => (/* reexport */ SetUpdateByOnBody),
2376
2331
  Mw: () => (/* reexport */ Slug),
2332
+ NM: () => (/* reexport */ buildIdempotencyCacheKey),
2377
2333
  BR: () => (/* reexport */ createSetUserFieldInterceptor)
2378
2334
  });
2379
2335
 
2380
2336
  // EXTERNAL MODULE: external "@nestjs/common"
2381
- var common_ = __webpack_require__(157);
2337
+ var common_ = __webpack_require__(6157);
2382
2338
  ;// CONCATENATED MODULE: ./projects/nestjs-shared/src/interceptors/delete-empty-id-from-body.interceptor.ts
2383
2339
  function _ts_decorate(decorators, target, key, desc) {
2384
2340
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -2397,19 +2353,13 @@ function _ts_decorate(decorators, target, key, desc) {
2397
2353
  class DeleteEmptyIdFromBodyInterceptor {
2398
2354
  intercept(context, next) {
2399
2355
  const request = context.switchToHttp().getRequest();
2400
- if (Array.isArray(request.body)) {
2401
- request.body = request.body.map((item)=>{
2402
- if (!item?.id || item?.id === '' || item?.id === null) {
2403
- delete item.id;
2404
- }
2405
- return item;
2406
- });
2407
- } else if (typeof request.body === 'object' && request.body !== null) {
2408
- const item = request.body;
2409
- if (!item?.id || item?.id === '' || item?.id === null) {
2410
- delete item.id;
2356
+ const items = Array.isArray(request.body) ? request.body : [
2357
+ request.body
2358
+ ];
2359
+ for (const item of items){
2360
+ if (typeof item === 'object' && item !== null && !item['id']) {
2361
+ delete item['id'];
2411
2362
  }
2412
- request.body = item;
2413
2363
  }
2414
2364
  return next.handle();
2415
2365
  }
@@ -2423,9 +2373,11 @@ DeleteEmptyIdFromBodyInterceptor = _ts_decorate([
2423
2373
  ;// CONCATENATED MODULE: external "rxjs/operators"
2424
2374
 
2425
2375
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/classes/hybrid-cache.class.ts + 3 modules
2426
- var hybrid_cache_class = __webpack_require__(375);
2376
+ var hybrid_cache_class = __webpack_require__(5375);
2427
2377
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/constants/index.ts
2428
- var constants = __webpack_require__(43);
2378
+ var constants = __webpack_require__(6043);
2379
+ // EXTERNAL MODULE: ./projects/nestjs-shared/src/middlewares/logger.middleware.ts + 1 modules
2380
+ var logger_middleware = __webpack_require__(8244);
2429
2381
  ;// CONCATENATED MODULE: ./projects/nestjs-shared/src/interceptors/idempotency.interceptor.ts
2430
2382
  function idempotency_interceptor_ts_decorate(decorators, target, key, desc) {
2431
2383
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -2455,47 +2407,54 @@ function _ts_param(paramIndex, decorator) {
2455
2407
 
2456
2408
 
2457
2409
 
2410
+
2411
+ const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
2412
+ /** Bounds how long a crashed instance can hold a key; no request runs this long */ const PROCESSING_TTL_MS = 10 * 60 * 1000;
2413
+ /**
2414
+ * Replays the stored response for a repeated `X-Idempotency-Key`. A key only ever replays for the
2415
+ * same tenant, user and endpoint, and a failed request releases it so the client can retry.
2416
+ */ function buildIdempotencyCacheKey(request, idempotencyKey) {
2417
+ const tenant = (0,logger_middleware/* .getTenantId */.EO)() ?? '-';
2418
+ const user = request.user?.id ?? 'anonymous';
2419
+ const endpoint = `${request.method ?? ''} ${request.route?.path ?? request.originalUrl ?? request.url ?? ''}`;
2420
+ return `${constants/* .IDEMPOTENCY_CACHE_PREFIX */.mg}:${tenant}:${user}:${endpoint}:${idempotencyKey}`;
2421
+ }
2458
2422
  class IdempotencyInterceptor {
2459
2423
  cache;
2460
- CACHE_TTL = 24 * 60 * 60 * 1000;
2461
- PROCESSING_MARKER = '__PROCESSING__';
2462
2424
  constructor(cache){
2463
2425
  this.cache = cache;
2464
2426
  }
2465
2427
  async intercept(context, next) {
2466
- // Skip if no cache available
2467
- if (!this.cache) {
2468
- return next.handle();
2469
- }
2428
+ const cache = this.cache;
2429
+ if (!cache) return next.handle();
2470
2430
  const request = context.switchToHttp().getRequest();
2471
2431
  const idempotencyKey = request.headers[constants/* .IDEMPOTENCY_KEY_HEADER */.iH];
2472
- // Skip if no idempotency key provided
2473
- if (!idempotencyKey) {
2474
- return next.handle();
2432
+ if (!idempotencyKey || typeof idempotencyKey !== 'string') return next.handle();
2433
+ const cacheKey = buildIdempotencyCacheKey(request, idempotencyKey);
2434
+ const stored = await cache.get(cacheKey, {
2435
+ skipMemory: true
2436
+ });
2437
+ if (stored !== undefined) return (0,__rspack_external_rxjs.of)(stored);
2438
+ // Atomic across instances: of two concurrent requests with one key, exactly one runs
2439
+ if (!await cache.claim(cacheKey, PROCESSING_TTL_MS)) {
2440
+ throw new common_.ConflictException({
2441
+ success: false,
2442
+ message: 'Request is already being processed',
2443
+ messageKey: constants/* .SYSTEM_MESSAGES.DUPLICATE_REQUEST */.hg.DUPLICATE_REQUEST
2444
+ });
2475
2445
  }
2476
- const cacheKey = `${constants/* .IDEMPOTENCY_CACHE_PREFIX */.mg}:${idempotencyKey}`;
2477
- // Check if request already exists
2478
- const cached = await this.cache.get(cacheKey);
2479
- if (cached) {
2480
- // If still processing, throw conflict
2481
- if (cached === this.PROCESSING_MARKER) {
2482
- throw new common_.ConflictException({
2483
- success: false,
2484
- message: 'Request is already being processed',
2485
- messageKey: constants/* .SYSTEM_MESSAGES.DUPLICATE_REQUEST */.hg.DUPLICATE_REQUEST
2486
- });
2487
- }
2488
- // Return cached response
2489
- return (0,__rspack_external_rxjs.of)(cached);
2446
+ const release = ()=>cache.releaseClaim(cacheKey).catch(()=>undefined);
2447
+ // The first request may have stored its response and released the claim between our read and claim
2448
+ const storedMeanwhile = await cache.get(cacheKey, {
2449
+ skipMemory: true
2450
+ }).catch(()=>undefined);
2451
+ if (storedMeanwhile !== undefined) {
2452
+ await release();
2453
+ return (0,__rspack_external_rxjs.of)(storedMeanwhile);
2490
2454
  }
2491
- // Mark as processing
2492
- const cache = this.cache;
2493
- await cache.set(cacheKey, this.PROCESSING_MARKER, this.CACHE_TTL);
2494
- // Process request and cache response
2495
- return next.handle().pipe((0,__rspack_external_rxjs_operators_2eaa0997.tap)(async (response)=>{
2496
- // Cache the successful response
2497
- await cache.set(cacheKey, response, this.CACHE_TTL);
2498
- }));
2455
+ return next.handle().pipe((0,__rspack_external_rxjs_operators_2eaa0997.concatMap)((response)=>(0,__rspack_external_rxjs.from)(cache.set(cacheKey, response, CACHE_TTL_MS, {
2456
+ skipMemory: true
2457
+ }).catch(()=>undefined).then(release).then(()=>response))), (0,__rspack_external_rxjs_operators_2eaa0997.catchError)((error)=>(0,__rspack_external_rxjs.from)(release()).pipe((0,__rspack_external_rxjs_operators_2eaa0997.concatMap)(()=>(0,__rspack_external_rxjs.throwError)(()=>error)))));
2499
2458
  }
2500
2459
  }
2501
2460
  IdempotencyInterceptor = idempotency_interceptor_ts_decorate([
@@ -2509,7 +2468,7 @@ IdempotencyInterceptor = idempotency_interceptor_ts_decorate([
2509
2468
  ], IdempotencyInterceptor);
2510
2469
 
2511
2470
  // EXTERNAL MODULE: external "crypto"
2512
- var external_crypto_ = __webpack_require__(823);
2471
+ var external_crypto_ = __webpack_require__(7823);
2513
2472
  ;// CONCATENATED MODULE: ./projects/nestjs-shared/src/interceptors/response-meta.interceptor.ts
2514
2473
  function response_meta_interceptor_ts_decorate(decorators, target, key, desc) {
2515
2474
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -2600,7 +2559,7 @@ const SetUpdateByOnBody = createSetUserFieldInterceptor('updatedById');
2600
2559
  const SetDeletedByOnBody = createSetUserFieldInterceptor('deletedById');
2601
2560
 
2602
2561
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/modules/utils/utils.service.ts
2603
- var utils_service = __webpack_require__(169);
2562
+ var utils_service = __webpack_require__(1169);
2604
2563
  ;// CONCATENATED MODULE: ./projects/nestjs-shared/src/interceptors/slug.interceptor.ts
2605
2564
  function slug_interceptor_ts_decorate(decorators, target, key, desc) {
2606
2565
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -2634,26 +2593,18 @@ class Slug {
2634
2593
  }
2635
2594
  intercept(context, next) {
2636
2595
  const request = context.switchToHttp().getRequest();
2637
- if (Array.isArray(request.body)) {
2638
- request.body = request.body.map((item)=>{
2639
- // Only generate slug if not provided and name exists
2640
- if (!item.slug && item?.name) {
2641
- return {
2642
- ...item,
2643
- slug: this.utilsService.transformToSlug(item.name)
2644
- };
2645
- }
2646
- return item;
2647
- });
2648
- } else if (!request.body?.slug && request.body?.name) {
2649
- // Only generate slug if not provided and name exists
2650
- request.body = {
2651
- ...request.body,
2652
- slug: this.utilsService.transformToSlug(request.body.name)
2653
- };
2654
- }
2596
+ request.body = Array.isArray(request.body) ? request.body.map((item)=>this.withSlug(item)) : this.withSlug(request.body);
2655
2597
  return next.handle();
2656
2598
  }
2599
+ withSlug(item) {
2600
+ if (typeof item !== 'object' || item === null || Array.isArray(item)) return item;
2601
+ const body = item;
2602
+ if (body['slug'] || typeof body['name'] !== 'string' || !body['name']) return item;
2603
+ return {
2604
+ ...body,
2605
+ slug: this.utilsService.transformToSlug(body['name'])
2606
+ };
2607
+ }
2657
2608
  }
2658
2609
  Slug = slug_interceptor_ts_decorate([
2659
2610
  (0,common_.Injectable)(),
@@ -2673,7 +2624,7 @@ Slug = slug_interceptor_ts_decorate([
2673
2624
 
2674
2625
 
2675
2626
  },
2676
- 928(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
2627
+ 3928(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
2677
2628
 
2678
2629
  // EXPORTS
2679
2630
  __webpack_require__.d(__webpack_exports__, {
@@ -2683,11 +2634,11 @@ __webpack_require__.d(__webpack_exports__, {
2683
2634
  });
2684
2635
 
2685
2636
  // EXTERNAL MODULE: external "@nestjs/common"
2686
- var common_ = __webpack_require__(157);
2637
+ var common_ = __webpack_require__(6157);
2687
2638
  // EXTERNAL MODULE: external "class-validator"
2688
- var external_class_validator_ = __webpack_require__(421);
2639
+ var external_class_validator_ = __webpack_require__(5421);
2689
2640
  // EXTERNAL MODULE: ./projects/nestjs-shared/src/constants/message-keys.ts
2690
- var message_keys = __webpack_require__(389);
2641
+ var message_keys = __webpack_require__(4389);
2691
2642
  ;// CONCATENATED MODULE: ./projects/nestjs-shared/src/pipes/dto-body-validation.pipe.ts
2692
2643
 
2693
2644
 
@@ -2782,7 +2733,7 @@ function declaresProperty(dto, key) {
2782
2733
 
2783
2734
 
2784
2735
  },
2785
- 154(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
2736
+ 8154(__unused_rspack_module, __webpack_exports__, __webpack_require__) {
2786
2737
  /**
2787
2738
  * The tenant a cache key belongs to: the provider's current tenant in multi-tenant mode, else
2788
2739
  * undefined. A single database never prefixes, so a stray tenant header cannot split its cache.