@tstdl/base 0.93.99 → 0.93.101

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 (85) hide show
  1. package/authentication/client/authentication.service.d.ts +1 -1
  2. package/authentication/client/authentication.service.js +23 -11
  3. package/file/mime-type.d.ts +2 -2
  4. package/file/mime-type.js +2 -3
  5. package/file/server/index.d.ts +1 -0
  6. package/file/server/index.js +1 -0
  7. package/file/server/mime-type.d.ts +2 -0
  8. package/file/server/mime-type.js +5 -0
  9. package/notification/api/index.d.ts +1 -0
  10. package/notification/api/index.js +1 -0
  11. package/notification/api/notification.api.d.ts +8 -16
  12. package/notification/api/notification.api.js +13 -26
  13. package/notification/index.d.ts +1 -1
  14. package/notification/index.js +1 -1
  15. package/notification/models/in-app-notification.model.d.ts +9 -4
  16. package/notification/models/in-app-notification.model.js +25 -10
  17. package/notification/models/index.d.ts +1 -1
  18. package/notification/models/index.js +1 -1
  19. package/notification/models/notification-log.model.d.ts +42 -5
  20. package/notification/models/notification-log.model.js +34 -20
  21. package/notification/models/notification-preference.model.d.ts +2 -2
  22. package/notification/models/notification-preference.model.js +9 -9
  23. package/notification/models/notification-type.model.d.ts +17 -0
  24. package/notification/models/{notification-category.model.js → notification-type.model.js} +12 -13
  25. package/notification/models/web-push-subscription.model.d.ts +2 -2
  26. package/notification/models/web-push-subscription.model.js +8 -7
  27. package/notification/server/api/notification.api-controller.d.ts +2 -2
  28. package/notification/server/api/notification.api-controller.js +4 -3
  29. package/notification/server/drizzle/{0000_glorious_randall.sql → 0000_shiny_the_anarchist.sql} +27 -32
  30. package/notification/server/drizzle/meta/0000_snapshot.json +179 -179
  31. package/notification/server/drizzle/meta/_journal.json +2 -2
  32. package/notification/server/module.d.ts +2 -0
  33. package/notification/server/module.js +1 -0
  34. package/notification/server/providers/channel-provider.d.ts +4 -3
  35. package/notification/server/providers/channel-provider.js +2 -1
  36. package/notification/server/providers/email-channel-provider.d.ts +3 -3
  37. package/notification/server/providers/email-channel-provider.js +7 -9
  38. package/notification/server/providers/in-app-channel-provider.d.ts +5 -5
  39. package/notification/server/providers/in-app-channel-provider.js +15 -16
  40. package/notification/server/providers/index.d.ts +1 -1
  41. package/notification/server/providers/index.js +1 -1
  42. package/notification/server/providers/web-push-channel-provider.d.ts +5 -4
  43. package/notification/server/providers/web-push-channel-provider.js +8 -7
  44. package/notification/server/schemas.d.ts +3 -3
  45. package/notification/server/schemas.js +3 -4
  46. package/notification/server/services/index.d.ts +2 -4
  47. package/notification/server/services/index.js +2 -4
  48. package/notification/server/services/notification-delivery.worker.d.ts +7 -1
  49. package/notification/server/services/notification-delivery.worker.js +49 -37
  50. package/notification/server/services/notification-sse.service.d.ts +4 -7
  51. package/notification/server/services/notification-sse.service.js +4 -11
  52. package/notification/server/services/notification-template.d.ts +2 -2
  53. package/notification/server/services/notification-template.js +3 -1
  54. package/notification/server/services/notification-template.service.d.ts +1 -1
  55. package/notification/server/services/notification-template.service.js +7 -3
  56. package/notification/server/services/notification-type.service.d.ts +11 -0
  57. package/notification/server/services/notification-type.service.js +41 -0
  58. package/notification/server/services/notification.service.d.ts +4 -5
  59. package/notification/server/services/notification.service.js +44 -27
  60. package/notification/tests/notification-api.test.js +95 -0
  61. package/notification/tests/notification-flow.test.js +174 -28
  62. package/notification/tests/notification-type.service.test.d.ts +1 -0
  63. package/notification/tests/notification-type.service.test.js +35 -0
  64. package/package.json +2 -2
  65. package/rate-limit/postgres/postgres-rate-limiter.d.ts +9 -4
  66. package/rate-limit/postgres/postgres-rate-limiter.js +17 -10
  67. package/rate-limit/rate-limiter.d.ts +6 -6
  68. package/rate-limit/tests/postgres-rate-limiter.test.js +1 -1
  69. package/task-queue/postgres/task-queue.js +1 -1
  70. package/task-queue/task-queue.d.ts +3 -3
  71. package/task-queue/tests/extensive-dependencies.test.d.ts +1 -0
  72. package/task-queue/tests/extensive-dependencies.test.js +234 -0
  73. package/notification/enums.d.ts +0 -22
  74. package/notification/enums.js +0 -19
  75. package/notification/models/notification-category.model.d.ts +0 -17
  76. package/notification/server/services/notification-category.service.d.ts +0 -11
  77. package/notification/server/services/notification-category.service.js +0 -41
  78. package/notification/server/services/notification-delivery.task.d.ts +0 -9
  79. package/notification/server/services/notification-delivery.task.js +0 -1
  80. package/notification/server/services/singleton.d.ts +0 -3
  81. package/notification/server/services/singleton.js +0 -10
  82. package/notification/tests/notification-category.service.test.js +0 -36
  83. package/notification/tests/test-notification.model.d.ts +0 -4
  84. package/notification/tests/test-notification.model.js +0 -25
  85. /package/notification/tests/{notification-category.service.test.d.ts → notification-api.test.d.ts} +0 -0
@@ -0,0 +1,234 @@
1
+ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest';
2
+ import { DependencyJoinMode, TaskQueueProvider, TaskStatus } from '../../task-queue/index.js';
3
+ import { setupIntegrationTest } from '../../unit-test/index.js';
4
+ import { timeout } from '../../utils/timing.js';
5
+ describe('Extensive Task Queue Dependency Tests', () => {
6
+ let injector;
7
+ let queue;
8
+ beforeAll(async () => {
9
+ ({ injector } = await setupIntegrationTest({ modules: { taskQueue: true } }));
10
+ });
11
+ beforeEach(() => {
12
+ const queueProvider = injector.resolve(TaskQueueProvider);
13
+ const queueName = `extensive-dep-queue-${Date.now()}-${Math.random()}`;
14
+ queue = queueProvider.get(queueName, {
15
+ visibilityTimeout: 1000,
16
+ });
17
+ });
18
+ afterEach(async () => {
19
+ await queue.clear();
20
+ });
21
+ afterAll(async () => {
22
+ await injector?.dispose();
23
+ });
24
+ async function waitForStatus(id, status) {
25
+ for (let i = 0; i < 100; i++) {
26
+ const task = await queue.getTask(id);
27
+ if (task?.status === status)
28
+ return;
29
+ await queue.processPendingFanIn();
30
+ await timeout(50);
31
+ }
32
+ const finalTask = await queue.getTask(id);
33
+ throw new Error(`Task ${id} did not reach status ${status}. Current status: ${finalTask?.status}`);
34
+ }
35
+ async function completeTask(type) {
36
+ const dequeued = await queue.dequeue({ types: [type] });
37
+ if (!dequeued)
38
+ throw new Error(`Could not dequeue task of type ${type}`);
39
+ await queue.complete(dequeued);
40
+ await queue.processPendingFanIn();
41
+ }
42
+ it('should handle complex mixed chain: A -> (B -> D, C -> E) -> F', async () => {
43
+ // F depends on D and E
44
+ const taskF = await queue.enqueue('F', {}, { scheduleAfterTags: ['mc-d', 'mc-e'] });
45
+ // D depends on B, E depends on C
46
+ const taskD = await queue.enqueue('D', {}, { tags: ['mc-d'], scheduleAfterTags: ['mc-b'] });
47
+ const taskE = await queue.enqueue('E', {}, { tags: ['mc-e'], scheduleAfterTags: ['mc-c'] });
48
+ // B and C depend on A
49
+ const taskB = await queue.enqueue('B', {}, { tags: ['mc-b'], scheduleAfterTags: ['mc-a'] });
50
+ const taskC = await queue.enqueue('C', {}, { tags: ['mc-c'], scheduleAfterTags: ['mc-a'] });
51
+ // A is the root
52
+ const taskA = await queue.enqueue('A', {}, { tags: ['mc-a'] });
53
+ expect(taskA.status).toBe(TaskStatus.Pending);
54
+ expect(taskB.status).toBe(TaskStatus.Waiting);
55
+ expect(taskC.status).toBe(TaskStatus.Waiting);
56
+ expect(taskD.status).toBe(TaskStatus.Waiting);
57
+ expect(taskE.status).toBe(TaskStatus.Waiting);
58
+ expect(taskF.status).toBe(TaskStatus.Waiting);
59
+ // 1. Complete A
60
+ await completeTask('A');
61
+ await waitForStatus(taskB.id, TaskStatus.Pending);
62
+ await waitForStatus(taskC.id, TaskStatus.Pending);
63
+ // 2. Complete B
64
+ await completeTask('B');
65
+ await waitForStatus(taskD.id, TaskStatus.Pending);
66
+ // 3. Complete C
67
+ await completeTask('C');
68
+ await waitForStatus(taskE.id, TaskStatus.Pending);
69
+ // F should still be waiting
70
+ const uF = await queue.getTask(taskF.id);
71
+ expect(uF?.status).toBe(TaskStatus.Waiting);
72
+ // 4. Complete D
73
+ await completeTask('D');
74
+ // F still waiting for E
75
+ expect((await queue.getTask(taskF.id))?.status).toBe(TaskStatus.Waiting);
76
+ // 5. Complete E
77
+ await completeTask('E');
78
+ await waitForStatus(taskF.id, TaskStatus.Pending);
79
+ });
80
+ it('should handle requested pattern: A -> B & C -> D', async () => {
81
+ const taskD = await queue.enqueue('D', {}, { scheduleAfterTags: ['B', 'C'] });
82
+ const taskB = await queue.enqueue('B', {}, { tags: ['B'], scheduleAfterTags: ['A'] });
83
+ const taskC = await queue.enqueue('C', {}, { tags: ['C'], scheduleAfterTags: ['A'] });
84
+ const taskA = await queue.enqueue('A', {}, { tags: ['A'] });
85
+ // Initial check
86
+ expect(taskA.status).toBe(TaskStatus.Pending);
87
+ expect(taskB.status).toBe(TaskStatus.Waiting);
88
+ expect(taskC.status).toBe(TaskStatus.Waiting);
89
+ expect(taskD.status).toBe(TaskStatus.Waiting);
90
+ // Complete A
91
+ await completeTask('A');
92
+ await waitForStatus(taskB.id, TaskStatus.Pending);
93
+ await waitForStatus(taskC.id, TaskStatus.Pending);
94
+ // B and C can be dequeued now (in any order)
95
+ const dB = await queue.dequeue({ types: ['B'] });
96
+ const dC = await queue.dequeue({ types: ['C'] });
97
+ expect(dB).toBeDefined();
98
+ expect(dC).toBeDefined();
99
+ // D still waiting
100
+ expect((await queue.getTask(taskD.id))?.status).toBe(TaskStatus.Waiting);
101
+ // Complete B
102
+ await queue.complete(dB);
103
+ await queue.processPendingFanIn();
104
+ expect((await queue.getTask(taskD.id))?.status).toBe(TaskStatus.Waiting);
105
+ // Complete C
106
+ await queue.complete(dC);
107
+ await waitForStatus(taskD.id, TaskStatus.Pending);
108
+ });
109
+ it('should strictly adhere to order: A -> B -> C', async () => {
110
+ const taskC = await queue.enqueue('C', {}, { tags: ['C'], scheduleAfterTags: ['B'] });
111
+ const taskB = await queue.enqueue('B', {}, { tags: ['B'], scheduleAfterTags: ['A'] });
112
+ const taskA = await queue.enqueue('A', {}, { tags: ['A'] });
113
+ // Try to dequeue B and C - should fail
114
+ const dC = await queue.dequeue({ types: ['C'] });
115
+ const dB = await queue.dequeue({ types: ['B'] });
116
+ expect(dC).toBeUndefined();
117
+ expect(dB).toBeUndefined();
118
+ // Complete A
119
+ const dA = await queue.dequeue({ types: ['A'] });
120
+ expect(dA?.id).toBe(taskA.id);
121
+ await queue.complete(dA);
122
+ await queue.processPendingFanIn();
123
+ // Now B should be available, but C still not
124
+ const dC_2 = await queue.dequeue({ types: ['C'] });
125
+ expect(dC_2).toBeUndefined();
126
+ const dB_2 = await queue.dequeue({ types: ['B'] });
127
+ expect(dB_2?.id).toBe(taskB.id);
128
+ await queue.complete(dB_2);
129
+ await queue.processPendingFanIn();
130
+ // Now C should be available
131
+ const dC_3 = await queue.dequeue({ types: ['C'] });
132
+ expect(dC_3?.id).toBe(taskC.id);
133
+ await queue.complete(dC_3);
134
+ });
135
+ it('should handle large fan-in: (T1, T2, T3, T4, T5) -> Result', async () => {
136
+ const tags = ['t1', 't2', 't3', 't4', 't5'];
137
+ const taskResult = await queue.enqueue('Result', {}, { scheduleAfterTags: tags });
138
+ for (const tag of tags) {
139
+ await queue.enqueue(`Task-${tag}`, {}, { tags: [tag] });
140
+ }
141
+ // Complete all but one
142
+ for (let i = 0; i < 4; i++) {
143
+ await completeTask(`Task-${tags[i]}`);
144
+ expect((await queue.getTask(taskResult.id))?.status).toBe(TaskStatus.Waiting);
145
+ }
146
+ // Complete the last one
147
+ await completeTask(`Task-${tags[4]}`);
148
+ await waitForStatus(taskResult.id, TaskStatus.Pending);
149
+ });
150
+ it('should handle large fan-in with OR: (T1, T2, T3, T4, T5) -> Result', async () => {
151
+ const tags = ['o1', 'o2', 'o3', 'o4', 'o5'];
152
+ const taskResult = await queue.enqueue('Result', {}, {
153
+ scheduleAfterTags: tags,
154
+ dependencyJoinMode: DependencyJoinMode.Or
155
+ });
156
+ for (const tag of tags) {
157
+ await queue.enqueue(`Task-${tag}`, {}, { tags: [tag] });
158
+ }
159
+ // Complete one
160
+ await completeTask(`Task-${tags[2]}`);
161
+ await waitForStatus(taskResult.id, TaskStatus.Pending);
162
+ });
163
+ it('should handle Diamond of Diamonds: A -> (B1, B2) -> C -> (D1, D2) -> E', async () => {
164
+ const taskE = await queue.enqueue('E', {}, { scheduleAfterTags: ['tag-d1', 'tag-d2'] });
165
+ const taskD1 = await queue.enqueue('D1', {}, { tags: ['tag-d1'], scheduleAfterTags: ['tag-c'] });
166
+ const taskD2 = await queue.enqueue('D2', {}, { tags: ['tag-d2'], scheduleAfterTags: ['tag-c'] });
167
+ const taskC = await queue.enqueue('C', {}, { tags: ['tag-c'], scheduleAfterTags: ['tag-b1', 'tag-b2'] });
168
+ const taskB1 = await queue.enqueue('B1', {}, { tags: ['tag-b1'], scheduleAfterTags: ['tag-a'] });
169
+ const taskB2 = await queue.enqueue('B2', {}, { tags: ['tag-b2'], scheduleAfterTags: ['tag-a'] });
170
+ const taskA = await queue.enqueue('A', {}, { tags: ['tag-a'] });
171
+ // Step by step completion
172
+ await completeTask('A');
173
+ await waitForStatus(taskB1.id, TaskStatus.Pending);
174
+ await waitForStatus(taskB2.id, TaskStatus.Pending);
175
+ await completeTask('B1');
176
+ expect((await queue.getTask(taskC.id))?.status).toBe(TaskStatus.Waiting);
177
+ await completeTask('B2');
178
+ await waitForStatus(taskC.id, TaskStatus.Pending);
179
+ await completeTask('C');
180
+ await waitForStatus(taskD1.id, TaskStatus.Pending);
181
+ await waitForStatus(taskD2.id, TaskStatus.Pending);
182
+ await completeTask('D1');
183
+ expect((await queue.getTask(taskE.id))?.status).toBe(TaskStatus.Waiting);
184
+ await completeTask('D2');
185
+ await waitForStatus(taskE.id, TaskStatus.Pending);
186
+ });
187
+ it('should fail-fast entire branch if one dependency fails fatal', async () => {
188
+ // A -> B -> C
189
+ // -> D -> E
190
+ // (C & E) -> F
191
+ const taskF = await queue.enqueue('F', {}, { scheduleAfterTags: ['C', 'E'], failFast: true });
192
+ const taskC = await queue.enqueue('C', {}, { tags: ['C'], scheduleAfterTags: ['B'], failFast: true });
193
+ const taskE = await queue.enqueue('E', {}, { tags: ['E'], scheduleAfterTags: ['D'], failFast: true });
194
+ const taskB = await queue.enqueue('B', {}, { tags: ['B'], scheduleAfterTags: ['A'], failFast: true });
195
+ const taskD = await queue.enqueue('D', {}, { tags: ['D'], scheduleAfterTags: ['A'], failFast: true });
196
+ const taskA = await queue.enqueue('A', {}, { tags: ['A'] });
197
+ await completeTask('A');
198
+ await waitForStatus(taskB.id, TaskStatus.Pending);
199
+ await waitForStatus(taskD.id, TaskStatus.Pending);
200
+ // Fail B fatally
201
+ const dB = await queue.dequeue({ types: ['B'] });
202
+ await queue.fail(dB, new Error('fatal B'), { fatal: true });
203
+ await queue.processPendingFanIn();
204
+ // B failed fatally -> C should die -> F should die
205
+ await waitForStatus(taskC.id, TaskStatus.Dead);
206
+ await waitForStatus(taskF.id, TaskStatus.Dead);
207
+ // D and E should be unaffected (except E is still waiting for D)
208
+ const uD = await queue.getTask(taskD.id);
209
+ expect(uD?.status).toBe(TaskStatus.Pending);
210
+ });
211
+ it('should handle many-to-many dependencies', async () => {
212
+ // {A, B} -> {C, D} -> {E, F}
213
+ // Each of C, D depends on BOTH A and B.
214
+ // Each of E, F depends on BOTH C and D.
215
+ const taskE = await queue.enqueue('E', {}, { scheduleAfterTags: ['C', 'D'] });
216
+ const taskF = await queue.enqueue('F', {}, { scheduleAfterTags: ['C', 'D'] });
217
+ const taskC = await queue.enqueue('C', {}, { tags: ['C'], scheduleAfterTags: ['A', 'B'] });
218
+ const taskD = await queue.enqueue('D', {}, { tags: ['D'], scheduleAfterTags: ['A', 'B'] });
219
+ const taskA = await queue.enqueue('A', {}, { tags: ['A'] });
220
+ const taskB = await queue.enqueue('B', {}, { tags: ['B'] });
221
+ await completeTask('A');
222
+ expect((await queue.getTask(taskC.id))?.status).toBe(TaskStatus.Waiting);
223
+ expect((await queue.getTask(taskD.id))?.status).toBe(TaskStatus.Waiting);
224
+ await completeTask('B');
225
+ await waitForStatus(taskC.id, TaskStatus.Pending);
226
+ await waitForStatus(taskD.id, TaskStatus.Pending);
227
+ await completeTask('C');
228
+ expect((await queue.getTask(taskE.id))?.status).toBe(TaskStatus.Waiting);
229
+ expect((await queue.getTask(taskF.id))?.status).toBe(TaskStatus.Waiting);
230
+ await completeTask('D');
231
+ await waitForStatus(taskE.id, TaskStatus.Pending);
232
+ await waitForStatus(taskF.id, TaskStatus.Pending);
233
+ });
234
+ });
@@ -1,22 +0,0 @@
1
- import { type EnumType } from '../enumeration/index.js';
2
- export declare const NotificationChannel: {
3
- readonly InApp: "in-app";
4
- readonly Email: "email";
5
- readonly WebPush: "web-push";
6
- };
7
- export type NotificationChannel = EnumType<typeof NotificationChannel>;
8
- export declare const NotificationPriority: {
9
- readonly Low: "low";
10
- readonly Medium: "medium";
11
- readonly High: "high";
12
- readonly Urgent: "urgent";
13
- };
14
- export type NotificationPriority = EnumType<typeof NotificationPriority>;
15
- export declare const NotificationStatus: {
16
- readonly Pending: "pending";
17
- readonly Sent: "sent";
18
- readonly Delivered: "delivered";
19
- readonly Read: "read";
20
- readonly Failed: "failed";
21
- };
22
- export type NotificationStatus = EnumType<typeof NotificationStatus>;
@@ -1,19 +0,0 @@
1
- import { defineEnum } from '../enumeration/index.js';
2
- export const NotificationChannel = defineEnum('NotificationChannel', {
3
- InApp: 'in-app',
4
- Email: 'email',
5
- WebPush: 'web-push',
6
- });
7
- export const NotificationPriority = defineEnum('NotificationPriority', {
8
- Low: 'low',
9
- Medium: 'medium',
10
- High: 'high',
11
- Urgent: 'urgent',
12
- });
13
- export const NotificationStatus = defineEnum('NotificationStatus', {
14
- Pending: 'pending',
15
- Sent: 'sent',
16
- Delivered: 'delivered',
17
- Read: 'read',
18
- Failed: 'failed',
19
- });
@@ -1,17 +0,0 @@
1
- import { TenantEntity } from '../../orm/index.js';
2
- import { NotificationChannel } from '../enums.js';
3
- export type ThrottlingConfig = {
4
- limit: number;
5
- intervalMs: number;
6
- };
7
- export type EscalationRule = {
8
- delayMs: number;
9
- channel: NotificationChannel;
10
- };
11
- export declare class NotificationCategory extends TenantEntity {
12
- static readonly entityName = "NotificationCategory";
13
- label: string;
14
- key: string;
15
- throttling: ThrottlingConfig | null;
16
- escalations: EscalationRule[] | null;
17
- }
@@ -1,11 +0,0 @@
1
- import { Transactional } from '../../../orm/server/index.js';
2
- import { NotificationCategory, type EscalationRule, type ThrottlingConfig } from '../../models/index.js';
3
- export type CategoryInitializationData = {
4
- label: string;
5
- throttling?: ThrottlingConfig;
6
- escalations?: EscalationRule[];
7
- };
8
- export declare class NotificationCategoryService extends Transactional {
9
- readonly repository: import("../../../orm/server/index.js").EntityRepository<NotificationCategory>;
10
- initializeCategories<T extends string>(tenantId: string, categoryData: Record<T, CategoryInitializationData>): Promise<Record<T, NotificationCategory>>;
11
- }
@@ -1,41 +0,0 @@
1
- var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
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;
5
- return c > 3 && r && Object.defineProperty(target, key, r), r;
6
- };
7
- import { injectRepository, Transactional } from '../../../orm/server/index.js';
8
- import { fromEntries, objectEntries } from '../../../utils/object/index.js';
9
- import { assertDefinedPass, isUndefined } from '../../../utils/type-guards.js';
10
- import { NotificationCategory } from '../../models/index.js';
11
- import { NotificationSingleton } from './singleton.js';
12
- let NotificationCategoryService = class NotificationCategoryService extends Transactional {
13
- repository = injectRepository(NotificationCategory);
14
- async initializeCategories(tenantId, categoryData) {
15
- const categoryEntries = objectEntries(categoryData);
16
- const categoryMap = await this.transaction(async (tx) => {
17
- const dbCategories = await this.repository.withTransaction(tx).loadManyByQuery({ tenantId });
18
- const enumKeyCategoryMap = new Map(dbCategories.map((category) => [category.key, category]));
19
- for (const [key, data] of categoryEntries) {
20
- const category = enumKeyCategoryMap.get(key);
21
- const throttling = data.throttling ?? null;
22
- const escalations = data.escalations ?? null;
23
- if (isUndefined(category)) {
24
- const newCategory = await this.repository.withTransaction(tx).insert({ tenantId, key, label: data.label, throttling, escalations });
25
- enumKeyCategoryMap.set(key, newCategory);
26
- }
27
- else if ((category.label != data.label) || (JSON.stringify(category.throttling) != JSON.stringify(throttling)) || (JSON.stringify(category.escalations) != JSON.stringify(escalations))) {
28
- const updatedCategory = await this.repository.withTransaction(tx).updateByQuery({ tenantId, id: category.id }, { label: data.label, throttling, escalations });
29
- enumKeyCategoryMap.set(key, updatedCategory);
30
- }
31
- }
32
- return enumKeyCategoryMap;
33
- });
34
- const mappedCategories = categoryEntries.map(([key]) => [key, assertDefinedPass(categoryMap.get(key), 'Could not map notification category.')]);
35
- return fromEntries(mappedCategories);
36
- }
37
- };
38
- NotificationCategoryService = __decorate([
39
- NotificationSingleton()
40
- ], NotificationCategoryService);
41
- export { NotificationCategoryService };
@@ -1,9 +0,0 @@
1
- export type NotificationTaskDefinitions = {
2
- 'notification/deliver': {
3
- data: {
4
- notificationId: string;
5
- };
6
- state: void;
7
- result: void;
8
- };
9
- };
@@ -1 +0,0 @@
1
- import { NotificationLog } from '../../models/index.js';
@@ -1,3 +0,0 @@
1
- export declare const notificationDatabaseConfigFactoryProvider: import("../../../injector/provider.js").FactoryProvider<unknown, unknown, import("../../../types/types.js").Record>;
2
- export declare const notificationDatabaseConfigProvider: import("../../../injector/injector.js").ProvidersItem<unknown, unknown, import("../../../types/types.js").Record>;
3
- export declare function NotificationSingleton(): ClassDecorator;
@@ -1,10 +0,0 @@
1
- import { Singleton } from '../../../injector/decorators.js';
2
- import { provide } from '../../../injector/injector.js';
3
- import { factoryProvider } from '../../../injector/provider.js';
4
- import { DatabaseConfig } from '../../../orm/server/index.js';
5
- import { NotificationConfiguration } from '../module.js';
6
- export const notificationDatabaseConfigFactoryProvider = factoryProvider((_, context) => context.resolve(NotificationConfiguration).database ?? context.resolve(DatabaseConfig, undefined, { skipSelf: 2 }));
7
- export const notificationDatabaseConfigProvider = provide(DatabaseConfig, notificationDatabaseConfigFactoryProvider);
8
- export function NotificationSingleton() {
9
- return Singleton({ providers: [notificationDatabaseConfigProvider] });
10
- }
@@ -1,36 +0,0 @@
1
- import { describe, expect, test } from 'vitest';
2
- import { runInInjectionContext } from '../../injector/index.js';
3
- import { setupIntegrationTest, truncateTables } from '../../unit-test/index.js';
4
- import { NotificationCategoryService } from '../server/services/notification-category.service.js';
5
- describe('NotificationCategoryService', () => {
6
- test('should initialize categories correctly', async () => {
7
- const { injector, database } = await setupIntegrationTest({ modules: { notification: true, authentication: true } });
8
- // Cleanup
9
- await truncateTables(database, 'notification', ['category']);
10
- const service = injector.resolve(NotificationCategoryService);
11
- const tenantId = '00000000-0000-0000-0000-000000000001';
12
- const categoryData = {
13
- CAT1: { label: 'Category 1' },
14
- CAT2: { label: 'Category 2', throttling: { limit: 1, intervalMs: 1000 } }
15
- };
16
- await runInInjectionContext(injector, async () => {
17
- const result = await service.initializeCategories(tenantId, categoryData);
18
- expect(result.CAT1.label).toBe('Category 1');
19
- expect(result.CAT2.key).toBe('CAT2');
20
- expect(result.CAT2.throttling?.limit).toBe(1);
21
- // Verify persistence
22
- const dbCategories = await service.repository.loadManyByQuery({ tenantId });
23
- expect(dbCategories).toHaveLength(2);
24
- // Update
25
- const updatedData = {
26
- CAT1: { label: 'Category 1 Updated' },
27
- CAT2: { label: 'Category 2', throttling: { limit: 1, intervalMs: 1000 } }
28
- };
29
- const resultUpdated = await service.initializeCategories(tenantId, updatedData);
30
- expect(resultUpdated.CAT1.label).toBe('Category 1 Updated');
31
- const dbCategoriesUpdated = await service.repository.loadManyByQuery({ tenantId });
32
- expect(dbCategoriesUpdated).toHaveLength(2);
33
- expect(dbCategoriesUpdated.find((c) => c.key == 'CAT1')?.label).toBe('Category 1 Updated');
34
- });
35
- });
36
- });
@@ -1,4 +0,0 @@
1
- import { NotificationLog } from '../models/notification-log.model.js';
2
- export declare class TestNotification extends NotificationLog {
3
- testField: string;
4
- }
@@ -1,25 +0,0 @@
1
- var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
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;
5
- return c > 3 && r && Object.defineProperty(target, key, r), r;
6
- };
7
- var __metadata = (this && this.__metadata) || function (k, v) {
8
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
- };
10
- import { ChildEntity } from '../../orm/index.js';
11
- import { StringProperty } from '../../schema/index.js';
12
- import { NotificationLog } from '../models/notification-log.model.js';
13
- import { NotificationTable } from '../models/notification-table.js';
14
- let TestNotification = class TestNotification extends NotificationLog {
15
- testField;
16
- };
17
- __decorate([
18
- StringProperty(),
19
- __metadata("design:type", String)
20
- ], TestNotification.prototype, "testField", void 0);
21
- TestNotification = __decorate([
22
- NotificationTable({ name: 'test_notification' }),
23
- ChildEntity('test')
24
- ], TestNotification);
25
- export { TestNotification };