@dismissible/nestjs-dismissible 0.0.2-canary.c91edbc.0 → 0.0.2-canary.d2f56d7.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 (41) hide show
  1. package/README.md +51 -67
  2. package/package.json +3 -3
  3. package/src/api/dismissible-item-response.dto.ts +0 -8
  4. package/src/api/dismissible-item.mapper.spec.ts +0 -12
  5. package/src/api/dismissible-item.mapper.ts +2 -8
  6. package/src/api/index.ts +3 -0
  7. package/src/api/use-cases/dismiss/dismiss.controller.spec.ts +1 -2
  8. package/src/api/use-cases/dismiss/dismiss.controller.ts +8 -8
  9. package/src/api/use-cases/get-or-create/get-or-create.controller.spec.ts +2 -42
  10. package/src/api/use-cases/get-or-create/get-or-create.controller.ts +10 -56
  11. package/src/api/use-cases/get-or-create/index.ts +0 -1
  12. package/src/api/use-cases/restore/restore.controller.spec.ts +1 -2
  13. package/src/api/use-cases/restore/restore.controller.ts +8 -8
  14. package/src/api/validation/index.ts +2 -0
  15. package/src/api/validation/param-validation.pipe.spec.ts +317 -0
  16. package/src/api/validation/param-validation.pipe.ts +42 -0
  17. package/src/api/validation/param.decorators.ts +32 -0
  18. package/src/core/dismissible-core.service.spec.ts +3 -45
  19. package/src/core/dismissible-core.service.ts +10 -27
  20. package/src/core/dismissible.service.spec.ts +23 -16
  21. package/src/core/dismissible.service.ts +28 -11
  22. package/src/core/hook-runner.service.spec.ts +369 -19
  23. package/src/core/hook-runner.service.ts +17 -17
  24. package/src/core/index.ts +0 -1
  25. package/src/core/lifecycle-hook.interface.ts +8 -8
  26. package/src/core/service-responses.interface.ts +9 -9
  27. package/src/dismissible.module.integration.spec.ts +685 -0
  28. package/src/dismissible.module.ts +6 -10
  29. package/src/events/dismissible.events.ts +16 -39
  30. package/src/index.ts +1 -0
  31. package/src/request/request-context.decorator.ts +1 -0
  32. package/src/request/request-context.interface.ts +6 -0
  33. package/src/response/http-exception-filter.spec.ts +213 -0
  34. package/src/testing/factories.ts +5 -8
  35. package/src/utils/dismissible.helper.ts +2 -2
  36. package/src/validation/dismissible-input.dto.ts +47 -0
  37. package/src/validation/index.ts +1 -0
  38. package/tsconfig.json +3 -0
  39. package/tsconfig.spec.json +12 -0
  40. package/src/api/use-cases/get-or-create/get-or-create.request.dto.ts +0 -17
  41. package/src/core/create-options.ts +0 -9
@@ -2,12 +2,11 @@ import { mock } from 'ts-jest-mocker';
2
2
  import { ForbiddenException } from '@nestjs/common';
3
3
  import { HookRunner } from './hook-runner.service';
4
4
  import { IDismissibleLifecycleHook } from './lifecycle-hook.interface';
5
- import { BaseMetadata } from '@dismissible/nestjs-dismissible-item';
6
5
  import { createTestItem, createTestContext } from '../testing/factories';
7
6
  import { IDismissibleLogger } from '@dismissible/nestjs-logger';
8
7
 
9
8
  describe('HookRunner', () => {
10
- let hookRunner: HookRunner<BaseMetadata>;
9
+ let hookRunner: HookRunner;
11
10
  let mockLogger: jest.Mocked<IDismissibleLogger>;
12
11
 
13
12
  const testUserId = 'test-user-id';
@@ -45,7 +44,7 @@ describe('HookRunner', () => {
45
44
  it('should execute hooks in priority order (low to high) for pre-hooks', async () => {
46
45
  const executionOrder: number[] = [];
47
46
 
48
- const hook1: IDismissibleLifecycleHook<BaseMetadata> = {
47
+ const hook1: IDismissibleLifecycleHook = {
49
48
  priority: 10,
50
49
  onBeforeRequest: jest.fn(async () => {
51
50
  executionOrder.push(10);
@@ -53,7 +52,7 @@ describe('HookRunner', () => {
53
52
  }),
54
53
  };
55
54
 
56
- const hook2: IDismissibleLifecycleHook<BaseMetadata> = {
55
+ const hook2: IDismissibleLifecycleHook = {
57
56
  priority: 5,
58
57
  onBeforeRequest: jest.fn(async () => {
59
58
  executionOrder.push(5);
@@ -61,7 +60,7 @@ describe('HookRunner', () => {
61
60
  }),
62
61
  };
63
62
 
64
- const hook3: IDismissibleLifecycleHook<BaseMetadata> = {
63
+ const hook3: IDismissibleLifecycleHook = {
65
64
  priority: 15,
66
65
  onBeforeRequest: jest.fn(async () => {
67
66
  executionOrder.push(15);
@@ -78,21 +77,21 @@ describe('HookRunner', () => {
78
77
  it('should execute hooks in reverse priority order for post-hooks', async () => {
79
78
  const executionOrder: number[] = [];
80
79
 
81
- const hook1: IDismissibleLifecycleHook<BaseMetadata> = {
80
+ const hook1: IDismissibleLifecycleHook = {
82
81
  priority: 10,
83
82
  onAfterRequest: jest.fn(async () => {
84
83
  executionOrder.push(10);
85
84
  }),
86
85
  };
87
86
 
88
- const hook2: IDismissibleLifecycleHook<BaseMetadata> = {
87
+ const hook2: IDismissibleLifecycleHook = {
89
88
  priority: 5,
90
89
  onAfterRequest: jest.fn(async () => {
91
90
  executionOrder.push(5);
92
91
  }),
93
92
  };
94
93
 
95
- const hook3: IDismissibleLifecycleHook<BaseMetadata> = {
94
+ const hook3: IDismissibleLifecycleHook = {
96
95
  priority: 15,
97
96
  onAfterRequest: jest.fn(async () => {
98
97
  executionOrder.push(15);
@@ -106,7 +105,7 @@ describe('HookRunner', () => {
106
105
  });
107
106
 
108
107
  it('should block operation when pre-hook returns proceed: false', async () => {
109
- const blockingHook: IDismissibleLifecycleHook<BaseMetadata> = {
108
+ const blockingHook: IDismissibleLifecycleHook = {
110
109
  onBeforeRequest: jest.fn(async () => ({
111
110
  proceed: false,
112
111
  reason: 'Rate limit exceeded',
@@ -121,7 +120,7 @@ describe('HookRunner', () => {
121
120
  });
122
121
 
123
122
  it('should apply mutations from pre-hooks', async () => {
124
- const mutatingHook: IDismissibleLifecycleHook<BaseMetadata> = {
123
+ const mutatingHook: IDismissibleLifecycleHook = {
125
124
  onBeforeRequest: jest.fn(async () => ({
126
125
  proceed: true,
127
126
  mutations: {
@@ -139,7 +138,7 @@ describe('HookRunner', () => {
139
138
  });
140
139
 
141
140
  it('should pass mutations through multiple hooks', async () => {
142
- const hook1: IDismissibleLifecycleHook<BaseMetadata> = {
141
+ const hook1: IDismissibleLifecycleHook = {
143
142
  priority: 1,
144
143
  onBeforeRequest: jest.fn(async (itemId) => ({
145
144
  proceed: true,
@@ -147,7 +146,7 @@ describe('HookRunner', () => {
147
146
  })),
148
147
  };
149
148
 
150
- const hook2: IDismissibleLifecycleHook<BaseMetadata> = {
149
+ const hook2: IDismissibleLifecycleHook = {
151
150
  priority: 2,
152
151
  onBeforeRequest: jest.fn(async (itemId) => ({
153
152
  proceed: true,
@@ -160,11 +159,70 @@ describe('HookRunner', () => {
160
159
 
161
160
  expect(result.id).toBe('original-hook1-hook2');
162
161
  });
162
+
163
+ it('should apply context mutations from pre-hooks', async () => {
164
+ const context = createTestContext();
165
+
166
+ const mutatingHook: IDismissibleLifecycleHook = {
167
+ onBeforeRequest: jest.fn(async () => ({
168
+ proceed: true,
169
+ mutations: {
170
+ context: { authorizationHeader: 'Bearer mutated-token' },
171
+ },
172
+ })),
173
+ };
174
+
175
+ hookRunner = new HookRunner([mutatingHook], mockLogger);
176
+ const result = await hookRunner.runPreRequest('test-id', testUserId, context);
177
+
178
+ expect(result.context).toEqual({ ...context, authorizationHeader: 'Bearer mutated-token' });
179
+ });
180
+
181
+ it('should ignore context mutation when no context provided', async () => {
182
+ const mutatingHook: IDismissibleLifecycleHook = {
183
+ onBeforeRequest: jest.fn(async () => ({
184
+ proceed: true,
185
+ mutations: {
186
+ context: { authorizationHeader: 'Bearer token' },
187
+ },
188
+ })),
189
+ };
190
+
191
+ hookRunner = new HookRunner([mutatingHook], mockLogger);
192
+ const result = await hookRunner.runPreRequest('test-id', testUserId, undefined);
193
+
194
+ expect(result.context).toBeUndefined();
195
+ });
196
+
197
+ it('should handle hooks with default priority (undefined)', async () => {
198
+ const executionOrder: string[] = [];
199
+
200
+ const hook1: IDismissibleLifecycleHook = {
201
+ priority: 5,
202
+ onBeforeRequest: jest.fn(async () => {
203
+ executionOrder.push('priority-5');
204
+ return { proceed: true };
205
+ }),
206
+ };
207
+
208
+ const hook2: IDismissibleLifecycleHook = {
209
+ // No priority - defaults to 0
210
+ onBeforeRequest: jest.fn(async () => {
211
+ executionOrder.push('priority-default');
212
+ return { proceed: true };
213
+ }),
214
+ };
215
+
216
+ hookRunner = new HookRunner([hook1, hook2], mockLogger);
217
+ await hookRunner.runPreRequest('test-id', testUserId, createTestContext());
218
+
219
+ expect(executionOrder).toEqual(['priority-default', 'priority-5']);
220
+ });
163
221
  });
164
222
 
165
223
  describe('error handling', () => {
166
224
  it('should throw error from pre-hook', async () => {
167
- const errorHook: IDismissibleLifecycleHook<BaseMetadata> = {
225
+ const errorHook: IDismissibleLifecycleHook = {
168
226
  onBeforeRequest: jest.fn(async () => {
169
227
  throw new Error('Hook error');
170
228
  }),
@@ -179,8 +237,24 @@ describe('HookRunner', () => {
179
237
  expect(mockLogger.error).toHaveBeenCalled();
180
238
  });
181
239
 
240
+ it('should throw non-Error from pre-hook', async () => {
241
+ const errorHook: IDismissibleLifecycleHook = {
242
+ onBeforeRequest: jest.fn(async () => {
243
+ throw 'string error';
244
+ }),
245
+ };
246
+
247
+ hookRunner = new HookRunner([errorHook], mockLogger);
248
+
249
+ await expect(
250
+ hookRunner.runPreRequest('test-id', testUserId, createTestContext()),
251
+ ).rejects.toBe('string error');
252
+
253
+ expect(mockLogger.error).toHaveBeenCalled();
254
+ });
255
+
182
256
  it('should log but not throw errors from post-hooks', async () => {
183
- const errorHook: IDismissibleLifecycleHook<BaseMetadata> = {
257
+ const errorHook: IDismissibleLifecycleHook = {
184
258
  onAfterRequest: jest.fn(async () => {
185
259
  throw new Error('Post-hook error');
186
260
  }),
@@ -194,6 +268,22 @@ describe('HookRunner', () => {
194
268
 
195
269
  expect(mockLogger.error).toHaveBeenCalled();
196
270
  });
271
+
272
+ it('should log but not throw non-Error from post-hooks', async () => {
273
+ const errorHook: IDismissibleLifecycleHook = {
274
+ onAfterRequest: jest.fn(async () => {
275
+ throw 'string post-hook error';
276
+ }),
277
+ };
278
+
279
+ hookRunner = new HookRunner([errorHook], mockLogger);
280
+
281
+ await expect(
282
+ hookRunner.runPostRequest('test-id', createTestItem(), testUserId, createTestContext()),
283
+ ).resolves.not.toThrow();
284
+
285
+ expect(mockLogger.error).toHaveBeenCalled();
286
+ });
197
287
  });
198
288
 
199
289
  describe('throwIfBlocked', () => {
@@ -209,6 +299,17 @@ describe('HookRunner', () => {
209
299
  ).toThrow(ForbiddenException);
210
300
  });
211
301
 
302
+ it('should throw ForbiddenException with default reason when blocked without reason', () => {
303
+ expect(() =>
304
+ HookRunner.throwIfBlocked({
305
+ proceed: false,
306
+ id: 'test',
307
+ userId: testUserId,
308
+ context: createTestContext(),
309
+ }),
310
+ ).toThrow('Operation blocked by lifecycle hook');
311
+ });
312
+
212
313
  it('should not throw when not blocked', () => {
213
314
  expect(() =>
214
315
  HookRunner.throwIfBlocked({
@@ -226,7 +327,7 @@ describe('HookRunner', () => {
226
327
  const item = createTestItem();
227
328
  const context = createTestContext();
228
329
 
229
- const hook: IDismissibleLifecycleHook<BaseMetadata> = {
330
+ const hook: IDismissibleLifecycleHook = {
230
331
  onBeforeGet: jest.fn().mockResolvedValue({ proceed: true }),
231
332
  };
232
333
 
@@ -240,7 +341,7 @@ describe('HookRunner', () => {
240
341
  const item = createTestItem();
241
342
  const context = createTestContext();
242
343
 
243
- const blockingHook: IDismissibleLifecycleHook<BaseMetadata> = {
344
+ const blockingHook: IDismissibleLifecycleHook = {
244
345
  onBeforeGet: jest.fn().mockResolvedValue({
245
346
  proceed: false,
246
347
  reason: 'Item is in invalid state',
@@ -258,7 +359,7 @@ describe('HookRunner', () => {
258
359
  const item = createTestItem();
259
360
  const context = createTestContext();
260
361
 
261
- const hook: IDismissibleLifecycleHook<BaseMetadata> = {
362
+ const hook: IDismissibleLifecycleHook = {
262
363
  onAfterGet: jest.fn(),
263
364
  };
264
365
 
@@ -272,7 +373,7 @@ describe('HookRunner', () => {
272
373
  const item = createTestItem();
273
374
  const context = createTestContext();
274
375
 
275
- const mutatingHook: IDismissibleLifecycleHook<BaseMetadata> = {
376
+ const mutatingHook: IDismissibleLifecycleHook = {
276
377
  onBeforeGet: jest.fn().mockResolvedValue({
277
378
  proceed: true,
278
379
  mutations: {
@@ -286,10 +387,259 @@ describe('HookRunner', () => {
286
387
 
287
388
  expect(result.id).toBe('mutated-id');
288
389
  });
390
+
391
+ it('should apply userId mutation from onBeforeGet hook', async () => {
392
+ const item = createTestItem();
393
+ const context = createTestContext();
394
+
395
+ const mutatingHook: IDismissibleLifecycleHook = {
396
+ onBeforeGet: jest.fn().mockResolvedValue({
397
+ proceed: true,
398
+ mutations: {
399
+ userId: 'mutated-user-id',
400
+ },
401
+ }),
402
+ };
403
+
404
+ hookRunner = new HookRunner([mutatingHook], mockLogger);
405
+ const result = await hookRunner.runPreGet('test-id', item, testUserId, context);
406
+
407
+ expect(result.userId).toBe('mutated-user-id');
408
+ });
409
+
410
+ it('should apply context mutation from onBeforeGet hook', async () => {
411
+ const item = createTestItem();
412
+ const context = createTestContext();
413
+
414
+ const mutatingHook: IDismissibleLifecycleHook = {
415
+ onBeforeGet: jest.fn().mockResolvedValue({
416
+ proceed: true,
417
+ mutations: {
418
+ context: { authorizationHeader: 'Bearer custom-token' },
419
+ },
420
+ }),
421
+ };
422
+
423
+ hookRunner = new HookRunner([mutatingHook], mockLogger);
424
+ const result = await hookRunner.runPreGet('test-id', item, testUserId, context);
425
+
426
+ expect(result.context).toEqual({ ...context, authorizationHeader: 'Bearer custom-token' });
427
+ });
428
+
429
+ it('should ignore context mutation when no context provided to onBeforeGet', async () => {
430
+ const item = createTestItem();
431
+
432
+ const mutatingHook: IDismissibleLifecycleHook = {
433
+ onBeforeGet: jest.fn().mockResolvedValue({
434
+ proceed: true,
435
+ mutations: {
436
+ context: { authorizationHeader: 'Bearer token' },
437
+ },
438
+ }),
439
+ };
440
+
441
+ hookRunner = new HookRunner([mutatingHook], mockLogger);
442
+ const result = await hookRunner.runPreGet('test-id', item, testUserId, undefined);
443
+
444
+ expect(result.context).toBeUndefined();
445
+ });
446
+
447
+ it('should throw error from onBeforeGet hook', async () => {
448
+ const item = createTestItem();
449
+ const context = createTestContext();
450
+
451
+ const errorHook: IDismissibleLifecycleHook = {
452
+ onBeforeGet: jest.fn().mockRejectedValue(new Error('Get hook error')),
453
+ };
454
+
455
+ hookRunner = new HookRunner([errorHook], mockLogger);
456
+
457
+ await expect(hookRunner.runPreGet('test-id', item, testUserId, context)).rejects.toThrow(
458
+ 'Get hook error',
459
+ );
460
+
461
+ expect(mockLogger.error).toHaveBeenCalled();
462
+ });
463
+
464
+ it('should throw non-Error from onBeforeGet hook', async () => {
465
+ const item = createTestItem();
466
+ const context = createTestContext();
467
+
468
+ const errorHook: IDismissibleLifecycleHook = {
469
+ onBeforeGet: jest.fn().mockRejectedValue('string error'),
470
+ };
471
+
472
+ hookRunner = new HookRunner([errorHook], mockLogger);
473
+
474
+ await expect(hookRunner.runPreGet('test-id', item, testUserId, context)).rejects.toBe(
475
+ 'string error',
476
+ );
477
+
478
+ expect(mockLogger.error).toHaveBeenCalled();
479
+ });
480
+ });
481
+
482
+ describe('runPreCreate, runPreDismiss, runPreRestore mutations', () => {
483
+ it('should apply mutations from onBeforeCreate hook', async () => {
484
+ const context = createTestContext();
485
+
486
+ const mutatingHook: IDismissibleLifecycleHook = {
487
+ onBeforeCreate: jest.fn().mockResolvedValue({
488
+ proceed: true,
489
+ mutations: {
490
+ id: 'mutated-create-id',
491
+ userId: 'mutated-create-user',
492
+ },
493
+ }),
494
+ };
495
+
496
+ hookRunner = new HookRunner([mutatingHook], mockLogger);
497
+ const result = await hookRunner.runPreCreate('original-id', testUserId, context);
498
+
499
+ expect(result.id).toBe('mutated-create-id');
500
+ expect(result.userId).toBe('mutated-create-user');
501
+ });
502
+
503
+ it('should apply context mutation from onBeforeCreate when context is provided', async () => {
504
+ const context = createTestContext();
505
+
506
+ const mutatingHook: IDismissibleLifecycleHook = {
507
+ onBeforeCreate: jest.fn().mockResolvedValue({
508
+ proceed: true,
509
+ mutations: {
510
+ context: { authorizationHeader: 'Bearer create-token' },
511
+ },
512
+ }),
513
+ };
514
+
515
+ hookRunner = new HookRunner([mutatingHook], mockLogger);
516
+ const result = await hookRunner.runPreCreate('test-id', testUserId, context);
517
+
518
+ expect(result.context).toEqual({ ...context, authorizationHeader: 'Bearer create-token' });
519
+ });
520
+
521
+ it('should block operation when onBeforeDismiss returns proceed: false', async () => {
522
+ const blockingHook: IDismissibleLifecycleHook = {
523
+ onBeforeDismiss: jest.fn().mockResolvedValue({
524
+ proceed: false,
525
+ reason: 'Cannot dismiss this item',
526
+ }),
527
+ };
528
+
529
+ hookRunner = new HookRunner([blockingHook], mockLogger);
530
+ const result = await hookRunner.runPreDismiss('test-id', testUserId, createTestContext());
531
+
532
+ expect(result.proceed).toBe(false);
533
+ expect(result.reason).toBe('Cannot dismiss this item');
534
+ });
535
+
536
+ it('should apply mutations from onBeforeRestore hook', async () => {
537
+ const context = createTestContext();
538
+
539
+ const mutatingHook: IDismissibleLifecycleHook = {
540
+ onBeforeRestore: jest.fn().mockResolvedValue({
541
+ proceed: true,
542
+ mutations: {
543
+ id: 'mutated-restore-id',
544
+ },
545
+ }),
546
+ };
547
+
548
+ hookRunner = new HookRunner([mutatingHook], mockLogger);
549
+ const result = await hookRunner.runPreRestore('original-id', testUserId, context);
550
+
551
+ expect(result.id).toBe('mutated-restore-id');
552
+ });
553
+
554
+ it('should throw error from onBeforeCreate hook', async () => {
555
+ const errorHook: IDismissibleLifecycleHook = {
556
+ onBeforeCreate: jest.fn().mockRejectedValue(new Error('Create hook error')),
557
+ };
558
+
559
+ hookRunner = new HookRunner([errorHook], mockLogger);
560
+
561
+ await expect(
562
+ hookRunner.runPreCreate('test-id', testUserId, createTestContext()),
563
+ ).rejects.toThrow('Create hook error');
564
+
565
+ expect(mockLogger.error).toHaveBeenCalled();
566
+ });
567
+
568
+ it('should throw non-Error from onBeforeDismiss hook', async () => {
569
+ const errorHook: IDismissibleLifecycleHook = {
570
+ onBeforeDismiss: jest.fn().mockRejectedValue('dismiss string error'),
571
+ };
572
+
573
+ hookRunner = new HookRunner([errorHook], mockLogger);
574
+
575
+ await expect(
576
+ hookRunner.runPreDismiss('test-id', testUserId, createTestContext()),
577
+ ).rejects.toBe('dismiss string error');
578
+
579
+ expect(mockLogger.error).toHaveBeenCalled();
580
+ });
581
+ });
582
+
583
+ describe('post-hook error handling', () => {
584
+ it('should log but not throw Error from onAfterCreate', async () => {
585
+ const errorHook: IDismissibleLifecycleHook = {
586
+ onAfterCreate: jest.fn().mockRejectedValue(new Error('After create error')),
587
+ };
588
+
589
+ hookRunner = new HookRunner([errorHook], mockLogger);
590
+
591
+ await expect(
592
+ hookRunner.runPostCreate('test-id', createTestItem(), testUserId, createTestContext()),
593
+ ).resolves.not.toThrow();
594
+
595
+ expect(mockLogger.error).toHaveBeenCalled();
596
+ });
597
+
598
+ it('should log but not throw non-Error from onAfterDismiss', async () => {
599
+ const errorHook: IDismissibleLifecycleHook = {
600
+ onAfterDismiss: jest.fn().mockRejectedValue('dismiss post error'),
601
+ };
602
+
603
+ hookRunner = new HookRunner([errorHook], mockLogger);
604
+
605
+ await expect(
606
+ hookRunner.runPostDismiss('test-id', createTestItem(), testUserId, createTestContext()),
607
+ ).resolves.not.toThrow();
608
+
609
+ expect(mockLogger.error).toHaveBeenCalled();
610
+ });
611
+
612
+ it('should log but not throw Error from onAfterRestore', async () => {
613
+ const errorHook: IDismissibleLifecycleHook = {
614
+ onAfterRestore: jest.fn().mockRejectedValue(new Error('After restore error')),
615
+ };
616
+
617
+ hookRunner = new HookRunner([errorHook], mockLogger);
618
+
619
+ await expect(
620
+ hookRunner.runPostRestore('test-id', createTestItem(), testUserId, createTestContext()),
621
+ ).resolves.not.toThrow();
622
+
623
+ expect(mockLogger.error).toHaveBeenCalled();
624
+ });
625
+
626
+ it('should log but not throw non-Error from onAfterGet', async () => {
627
+ const errorHook: IDismissibleLifecycleHook = {
628
+ onAfterGet: jest.fn().mockRejectedValue('get post error'),
629
+ };
630
+
631
+ hookRunner = new HookRunner([errorHook], mockLogger);
632
+
633
+ await expect(
634
+ hookRunner.runPostGet('test-id', createTestItem(), testUserId, createTestContext()),
635
+ ).resolves.not.toThrow();
636
+
637
+ expect(mockLogger.error).toHaveBeenCalled();
638
+ });
289
639
  });
290
640
 
291
641
  describe('all hook methods', () => {
292
- let allMethodsHook: IDismissibleLifecycleHook<BaseMetadata>;
642
+ let allMethodsHook: IDismissibleLifecycleHook;
293
643
 
294
644
  beforeEach(() => {
295
645
  allMethodsHook = {
@@ -5,7 +5,7 @@ import {
5
5
  IHookResult,
6
6
  } from './lifecycle-hook.interface';
7
7
  import { DISMISSIBLE_LOGGER, IDismissibleLogger } from '@dismissible/nestjs-logger';
8
- import { BaseMetadata, DismissibleItemDto } from '@dismissible/nestjs-dismissible-item';
8
+ import { DismissibleItemDto } from '@dismissible/nestjs-dismissible-item';
9
9
  import { IRequestContext } from '../request/request-context.interface';
10
10
 
11
11
  /**
@@ -32,13 +32,13 @@ export interface IHookRunResult {
32
32
  * Service responsible for running lifecycle hooks.
33
33
  */
34
34
  @Injectable()
35
- export class HookRunner<TMetadata extends BaseMetadata = BaseMetadata> {
36
- private readonly sortedHooks: IDismissibleLifecycleHook<TMetadata>[];
35
+ export class HookRunner {
36
+ private readonly sortedHooks: IDismissibleLifecycleHook[];
37
37
 
38
38
  constructor(
39
39
  @Optional()
40
40
  @Inject(DISMISSIBLE_HOOKS)
41
- hooks: IDismissibleLifecycleHook<TMetadata>[] = [],
41
+ hooks: IDismissibleLifecycleHook[] = [],
42
42
  @Inject(DISMISSIBLE_LOGGER)
43
43
  private readonly logger: IDismissibleLogger,
44
44
  ) {
@@ -68,7 +68,7 @@ export class HookRunner<TMetadata extends BaseMetadata = BaseMetadata> {
68
68
  */
69
69
  async runPostRequest(
70
70
  itemId: string,
71
- item: DismissibleItemDto<TMetadata>,
71
+ item: DismissibleItemDto,
72
72
  userId: string,
73
73
  context?: IRequestContext,
74
74
  ): Promise<void> {
@@ -85,7 +85,7 @@ export class HookRunner<TMetadata extends BaseMetadata = BaseMetadata> {
85
85
  */
86
86
  async runPreGet(
87
87
  itemId: string,
88
- item: DismissibleItemDto<TMetadata>,
88
+ item: DismissibleItemDto,
89
89
  userId: string,
90
90
  context?: IRequestContext,
91
91
  ): Promise<IHookRunResult> {
@@ -97,7 +97,7 @@ export class HookRunner<TMetadata extends BaseMetadata = BaseMetadata> {
97
97
  */
98
98
  async runPostGet(
99
99
  itemId: string,
100
- item: DismissibleItemDto<TMetadata>,
100
+ item: DismissibleItemDto,
101
101
  userId: string,
102
102
  context?: IRequestContext,
103
103
  ): Promise<void> {
@@ -124,7 +124,7 @@ export class HookRunner<TMetadata extends BaseMetadata = BaseMetadata> {
124
124
  */
125
125
  async runPostCreate(
126
126
  itemId: string,
127
- item: DismissibleItemDto<TMetadata>,
127
+ item: DismissibleItemDto,
128
128
  userId: string,
129
129
  context?: IRequestContext,
130
130
  ): Promise<void> {
@@ -151,7 +151,7 @@ export class HookRunner<TMetadata extends BaseMetadata = BaseMetadata> {
151
151
  */
152
152
  async runPostDismiss(
153
153
  itemId: string,
154
- item: DismissibleItemDto<TMetadata>,
154
+ item: DismissibleItemDto,
155
155
  userId: string,
156
156
  context?: IRequestContext,
157
157
  ): Promise<void> {
@@ -178,7 +178,7 @@ export class HookRunner<TMetadata extends BaseMetadata = BaseMetadata> {
178
178
  */
179
179
  async runPostRestore(
180
180
  itemId: string,
181
- item: DismissibleItemDto<TMetadata>,
181
+ item: DismissibleItemDto,
182
182
  userId: string,
183
183
  context?: IRequestContext,
184
184
  ): Promise<void> {
@@ -189,7 +189,7 @@ export class HookRunner<TMetadata extends BaseMetadata = BaseMetadata> {
189
189
  * Internal method to run pre-hooks.
190
190
  */
191
191
  private async runPreHooks(
192
- hookName: keyof IDismissibleLifecycleHook<TMetadata>,
192
+ hookName: keyof IDismissibleLifecycleHook,
193
193
  itemId: string,
194
194
  userId: string,
195
195
  context?: IRequestContext,
@@ -266,9 +266,9 @@ export class HookRunner<TMetadata extends BaseMetadata = BaseMetadata> {
266
266
  * Unlike standard pre-hooks, these receive the item for inspection/access control.
267
267
  */
268
268
  private async runPreHooksWithItem(
269
- hookName: keyof IDismissibleLifecycleHook<TMetadata>,
269
+ hookName: keyof IDismissibleLifecycleHook,
270
270
  itemId: string,
271
- item: DismissibleItemDto<TMetadata>,
271
+ item: DismissibleItemDto,
272
272
  userId: string,
273
273
  context?: IRequestContext,
274
274
  ): Promise<IHookRunResult> {
@@ -280,7 +280,7 @@ export class HookRunner<TMetadata extends BaseMetadata = BaseMetadata> {
280
280
  const hookFn = hook[hookName] as
281
281
  | ((
282
282
  itemId: string,
283
- item: DismissibleItemDto<TMetadata>,
283
+ item: DismissibleItemDto,
284
284
  userId: string,
285
285
  context?: IRequestContext,
286
286
  ) => Promise<IHookResult> | IHookResult)
@@ -345,9 +345,9 @@ export class HookRunner<TMetadata extends BaseMetadata = BaseMetadata> {
345
345
  * Post-hooks run in reverse priority order.
346
346
  */
347
347
  private async runPostHooks(
348
- hookName: keyof IDismissibleLifecycleHook<TMetadata>,
348
+ hookName: keyof IDismissibleLifecycleHook,
349
349
  itemId: string,
350
- item: DismissibleItemDto<TMetadata>,
350
+ item: DismissibleItemDto,
351
351
  userId: string,
352
352
  context?: IRequestContext,
353
353
  ): Promise<void> {
@@ -358,7 +358,7 @@ export class HookRunner<TMetadata extends BaseMetadata = BaseMetadata> {
358
358
  const hookFn = hook[hookName] as
359
359
  | ((
360
360
  itemId: string,
361
- item: DismissibleItemDto<TMetadata>,
361
+ item: DismissibleItemDto,
362
362
  userId: string,
363
363
  context?: IRequestContext,
364
364
  ) => Promise<void> | void)
package/src/core/index.ts CHANGED
@@ -3,4 +3,3 @@ export * from './service-responses.interface';
3
3
  export * from './dismissible-core.service';
4
4
  export * from './hook-runner.service';
5
5
  export * from './dismissible.service';
6
- export * from './create-options';