@pixelbyte-software/pixcode 1.40.9 → 1.41.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.
- package/dist/assets/{index-BIWB3xUE.js → index-DPzxZAAM.js} +6 -6
- package/dist/index.html +1 -1
- package/dist/landing.html +1 -1
- package/dist-server/server/routes/settings.js +11 -0
- package/dist-server/server/routes/settings.js.map +1 -1
- package/dist-server/server/services/notification-orchestrator.js +48 -16
- package/dist-server/server/services/notification-orchestrator.js.map +1 -1
- package/dist-server/server/services/notification-taxonomy.js +177 -0
- package/dist-server/server/services/notification-taxonomy.js.map +1 -0
- package/package.json +1 -1
- package/scripts/smoke/notification-taxonomy.mjs +58 -0
- package/server/routes/settings.js +11 -0
- package/server/services/notification-orchestrator.js +58 -15
- package/server/services/notification-taxonomy.js +204 -0
|
@@ -3,13 +3,12 @@ import webPush from 'web-push';
|
|
|
3
3
|
import { notificationPreferencesDb, pushSubscriptionsDb, sessionNamesDb } from '../database/db.js';
|
|
4
4
|
|
|
5
5
|
import { notifyUser as notifyTelegramUser } from './telegram/bot.js';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
};
|
|
6
|
+
import {
|
|
7
|
+
NOTIFICATION_EVENT_TYPES,
|
|
8
|
+
createNotificationEventContract,
|
|
9
|
+
getNotificationPreferenceKey,
|
|
10
|
+
normalizeNotificationEvent
|
|
11
|
+
} from './notification-taxonomy.js';
|
|
13
12
|
|
|
14
13
|
const PROVIDER_LABELS = {
|
|
15
14
|
claude: 'Claude',
|
|
@@ -40,7 +39,7 @@ const cleanupOldEventKeys = () => {
|
|
|
40
39
|
|
|
41
40
|
function shouldSendPush(preferences, event) {
|
|
42
41
|
const webPushEnabled = Boolean(preferences?.channels?.webPush);
|
|
43
|
-
const prefEventKey =
|
|
42
|
+
const prefEventKey = getNotificationPreferenceKey(event);
|
|
44
43
|
const eventEnabled = prefEventKey ? Boolean(preferences?.events?.[prefEventKey]) : true;
|
|
45
44
|
|
|
46
45
|
return webPushEnabled && eventEnabled;
|
|
@@ -48,7 +47,7 @@ function shouldSendPush(preferences, event) {
|
|
|
48
47
|
|
|
49
48
|
function shouldSendInApp(preferences, event) {
|
|
50
49
|
const inAppEnabled = preferences?.channels?.inApp !== false;
|
|
51
|
-
const prefEventKey =
|
|
50
|
+
const prefEventKey = getNotificationPreferenceKey(event);
|
|
52
51
|
const eventEnabled = prefEventKey ? preferences?.events?.[prefEventKey] !== false : true;
|
|
53
52
|
|
|
54
53
|
return inAppEnabled && eventEnabled;
|
|
@@ -56,7 +55,7 @@ function shouldSendInApp(preferences, event) {
|
|
|
56
55
|
|
|
57
56
|
function shouldSendTelegram(preferences, event) {
|
|
58
57
|
const telegramEnabled = preferences?.channels?.telegram !== false;
|
|
59
|
-
const prefEventKey =
|
|
58
|
+
const prefEventKey = getNotificationPreferenceKey(event);
|
|
60
59
|
const eventEnabled = prefEventKey ? preferences?.events?.[prefEventKey] !== false : true;
|
|
61
60
|
|
|
62
61
|
return telegramEnabled && eventEnabled;
|
|
@@ -75,6 +74,7 @@ function isDuplicate(event) {
|
|
|
75
74
|
function createNotificationEvent({
|
|
76
75
|
provider,
|
|
77
76
|
sessionId = null,
|
|
77
|
+
eventType = null,
|
|
78
78
|
kind = 'info',
|
|
79
79
|
code = 'generic.info',
|
|
80
80
|
meta = {},
|
|
@@ -82,9 +82,10 @@ function createNotificationEvent({
|
|
|
82
82
|
dedupeKey = null,
|
|
83
83
|
requiresUserAction = false
|
|
84
84
|
}) {
|
|
85
|
-
return {
|
|
85
|
+
return normalizeNotificationEvent({
|
|
86
86
|
provider,
|
|
87
87
|
sessionId,
|
|
88
|
+
eventType,
|
|
88
89
|
kind,
|
|
89
90
|
code,
|
|
90
91
|
meta,
|
|
@@ -92,7 +93,7 @@ function createNotificationEvent({
|
|
|
92
93
|
requiresUserAction,
|
|
93
94
|
dedupeKey,
|
|
94
95
|
createdAt: new Date().toISOString()
|
|
95
|
-
};
|
|
96
|
+
});
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
function normalizeErrorMessage(error) {
|
|
@@ -138,6 +139,16 @@ function resolveSessionName(event) {
|
|
|
138
139
|
}
|
|
139
140
|
|
|
140
141
|
function buildPushBody(event) {
|
|
142
|
+
const EVENT_TYPE_MAP = {
|
|
143
|
+
[NOTIFICATION_EVENT_TYPES.CHAT_DONE]: event.meta?.stopReason || 'Chat run completed',
|
|
144
|
+
[NOTIFICATION_EVENT_TYPES.ORCHESTRATION_DONE]: event.meta?.stopReason || 'Orchestration completed',
|
|
145
|
+
[NOTIFICATION_EVENT_TYPES.APPROVAL_NEEDED]: event.meta?.toolName
|
|
146
|
+
? `Action Required: Tool "${event.meta.toolName}" needs approval`
|
|
147
|
+
: 'Action Required: A run needs your approval',
|
|
148
|
+
[NOTIFICATION_EVENT_TYPES.ERROR]: event.meta?.error ? `Error: ${event.meta.error}` : 'Pixcode encountered an error',
|
|
149
|
+
[NOTIFICATION_EVENT_TYPES.TEST_FAILED]: event.meta?.error ? `Test Failed: ${event.meta.error}` : 'A verification command failed',
|
|
150
|
+
[NOTIFICATION_EVENT_TYPES.LIVE_VIEW_FAILED]: event.meta?.error ? `Live View Failed: ${event.meta.error}` : 'Live View failed'
|
|
151
|
+
};
|
|
141
152
|
const CODE_MAP = {
|
|
142
153
|
'permission.required': event.meta?.toolName
|
|
143
154
|
? `Action Required: Tool "${event.meta.toolName}" needs approval`
|
|
@@ -155,16 +166,19 @@ function buildPushBody(event) {
|
|
|
155
166
|
};
|
|
156
167
|
const providerLabel = PROVIDER_LABELS[event.provider] || 'Assistant';
|
|
157
168
|
const sessionName = resolveSessionName(event);
|
|
158
|
-
const message = CODE_MAP[event.code] || 'You have a new notification';
|
|
169
|
+
const message = EVENT_TYPE_MAP[event.eventType] || CODE_MAP[event.code] || 'You have a new notification';
|
|
159
170
|
|
|
160
171
|
return {
|
|
161
172
|
title: sessionName || 'Pixcode',
|
|
162
173
|
body: `${providerLabel}: ${message}`,
|
|
163
174
|
data: {
|
|
164
175
|
sessionId: event.sessionId || null,
|
|
176
|
+
eventType: event.eventType || null,
|
|
165
177
|
code: event.code,
|
|
166
178
|
provider: event.provider || null,
|
|
167
179
|
sessionName,
|
|
180
|
+
category: event.category || null,
|
|
181
|
+
preferenceKey: event.preferenceKey || getNotificationPreferenceKey(event),
|
|
168
182
|
tag: `${event.provider || 'assistant'}:${event.sessionId || 'none'}:${event.code}`
|
|
169
183
|
}
|
|
170
184
|
};
|
|
@@ -172,11 +186,15 @@ function buildPushBody(event) {
|
|
|
172
186
|
|
|
173
187
|
function buildNotificationPayload(event) {
|
|
174
188
|
const pushBody = buildPushBody(event);
|
|
189
|
+
const contract = createNotificationEventContract(event);
|
|
175
190
|
const baseId = event.dedupeKey || `${event.provider || 'system'}:${event.kind || 'info'}:${event.code || 'generic'}:${event.sessionId || 'none'}`;
|
|
176
191
|
return {
|
|
177
192
|
id: `${baseId}:${event.createdAt}`,
|
|
178
193
|
title: pushBody.title,
|
|
179
194
|
body: pushBody.body,
|
|
195
|
+
eventType: contract.eventType,
|
|
196
|
+
category: contract.category,
|
|
197
|
+
preferenceKey: contract.preferenceKey,
|
|
180
198
|
kind: event.kind || 'info',
|
|
181
199
|
code: event.code || 'generic.info',
|
|
182
200
|
severity: event.severity || 'info',
|
|
@@ -188,6 +206,28 @@ function buildNotificationPayload(event) {
|
|
|
188
206
|
};
|
|
189
207
|
}
|
|
190
208
|
|
|
209
|
+
function inferRunStoppedEventType({ provider, stopReason }) {
|
|
210
|
+
const reason = typeof stopReason === 'string' ? stopReason.toLowerCase() : '';
|
|
211
|
+
if (provider === 'system' && reason.includes('orchestration')) {
|
|
212
|
+
return NOTIFICATION_EVENT_TYPES.ORCHESTRATION_DONE;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return NOTIFICATION_EVENT_TYPES.CHAT_DONE;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function inferRunFailedEventType({ provider, error }) {
|
|
219
|
+
const message = normalizeErrorMessage(error).toLowerCase();
|
|
220
|
+
if (message.includes('live view')) {
|
|
221
|
+
return NOTIFICATION_EVENT_TYPES.LIVE_VIEW_FAILED;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (message.includes('test') || message.includes('typecheck') || message.includes('lint') || message.includes('build')) {
|
|
225
|
+
return NOTIFICATION_EVENT_TYPES.TEST_FAILED;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return provider === 'system' ? NOTIFICATION_EVENT_TYPES.ERROR : NOTIFICATION_EVENT_TYPES.RUN_FAILED;
|
|
229
|
+
}
|
|
230
|
+
|
|
191
231
|
function broadcastInAppNotification(userId, event) {
|
|
192
232
|
if (!notificationWebSocketServer || !userId || !event) {
|
|
193
233
|
return;
|
|
@@ -280,12 +320,13 @@ function notifyUserIfEnabled({ userId, event }) {
|
|
|
280
320
|
}
|
|
281
321
|
}
|
|
282
322
|
|
|
283
|
-
function notifyRunStopped({ userId, provider, sessionId = null, stopReason = 'completed', sessionName = null }) {
|
|
323
|
+
function notifyRunStopped({ userId, provider, sessionId = null, stopReason = 'completed', sessionName = null, eventType = null }) {
|
|
284
324
|
notifyUserIfEnabled({
|
|
285
325
|
userId,
|
|
286
326
|
event: createNotificationEvent({
|
|
287
327
|
provider,
|
|
288
328
|
sessionId,
|
|
329
|
+
eventType: eventType || inferRunStoppedEventType({ provider, stopReason }),
|
|
289
330
|
kind: 'stop',
|
|
290
331
|
code: 'run.stopped',
|
|
291
332
|
meta: { stopReason, sessionName },
|
|
@@ -295,7 +336,7 @@ function notifyRunStopped({ userId, provider, sessionId = null, stopReason = 'co
|
|
|
295
336
|
});
|
|
296
337
|
}
|
|
297
338
|
|
|
298
|
-
function notifyRunFailed({ userId, provider, sessionId = null, error, sessionName = null }) {
|
|
339
|
+
function notifyRunFailed({ userId, provider, sessionId = null, error, sessionName = null, eventType = null }) {
|
|
299
340
|
const errorMessage = normalizeErrorMessage(error);
|
|
300
341
|
|
|
301
342
|
notifyUserIfEnabled({
|
|
@@ -303,6 +344,7 @@ function notifyRunFailed({ userId, provider, sessionId = null, error, sessionNam
|
|
|
303
344
|
event: createNotificationEvent({
|
|
304
345
|
provider,
|
|
305
346
|
sessionId,
|
|
347
|
+
eventType: eventType || inferRunFailedEventType({ provider, error }),
|
|
306
348
|
kind: 'error',
|
|
307
349
|
code: 'run.failed',
|
|
308
350
|
meta: { error: errorMessage, sessionName },
|
|
@@ -314,6 +356,7 @@ function notifyRunFailed({ userId, provider, sessionId = null, error, sessionNam
|
|
|
314
356
|
|
|
315
357
|
export {
|
|
316
358
|
createNotificationEvent,
|
|
359
|
+
createNotificationEventContract,
|
|
317
360
|
setNotificationWebSocketServer,
|
|
318
361
|
broadcastInAppNotification,
|
|
319
362
|
notifyUserIfEnabled,
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
const NOTIFICATION_PREFERENCE_KEYS = Object.freeze({
|
|
2
|
+
ACTION_REQUIRED: 'actionRequired',
|
|
3
|
+
STOP: 'stop',
|
|
4
|
+
ERROR: 'error',
|
|
5
|
+
UPDATES: 'updates',
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
const NOTIFICATION_EVENT_TYPES = Object.freeze({
|
|
9
|
+
CHAT_DONE: 'chat.done',
|
|
10
|
+
ORCHESTRATION_DONE: 'orchestration.done',
|
|
11
|
+
APPROVAL_NEEDED: 'approval.needed',
|
|
12
|
+
ERROR: 'error',
|
|
13
|
+
TEST_FAILED: 'test.failed',
|
|
14
|
+
LIVE_VIEW_FAILED: 'live_view.failed',
|
|
15
|
+
RUN_STOPPED: 'run.stopped',
|
|
16
|
+
RUN_FAILED: 'run.failed',
|
|
17
|
+
AGENT_NOTIFICATION: 'agent.notification',
|
|
18
|
+
PUSH_ENABLED: 'push.enabled',
|
|
19
|
+
APP_UPDATE_AVAILABLE: 'app.update.available',
|
|
20
|
+
CLI_UPDATE_AVAILABLE: 'cli.update.available',
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const NOTIFICATION_TAXONOMY = Object.freeze({
|
|
24
|
+
[NOTIFICATION_EVENT_TYPES.CHAT_DONE]: {
|
|
25
|
+
category: 'chat',
|
|
26
|
+
kind: 'stop',
|
|
27
|
+
severity: 'info',
|
|
28
|
+
preferenceKey: NOTIFICATION_PREFERENCE_KEYS.STOP,
|
|
29
|
+
description: 'A chat/provider run completed.',
|
|
30
|
+
},
|
|
31
|
+
[NOTIFICATION_EVENT_TYPES.ORCHESTRATION_DONE]: {
|
|
32
|
+
category: 'orchestration',
|
|
33
|
+
kind: 'stop',
|
|
34
|
+
severity: 'info',
|
|
35
|
+
preferenceKey: NOTIFICATION_PREFERENCE_KEYS.STOP,
|
|
36
|
+
description: 'An orchestration workflow completed.',
|
|
37
|
+
},
|
|
38
|
+
[NOTIFICATION_EVENT_TYPES.APPROVAL_NEEDED]: {
|
|
39
|
+
category: 'approval',
|
|
40
|
+
kind: 'action_required',
|
|
41
|
+
severity: 'warning',
|
|
42
|
+
preferenceKey: NOTIFICATION_PREFERENCE_KEYS.ACTION_REQUIRED,
|
|
43
|
+
requiresUserAction: true,
|
|
44
|
+
description: 'A run is waiting for human approval.',
|
|
45
|
+
},
|
|
46
|
+
[NOTIFICATION_EVENT_TYPES.ERROR]: {
|
|
47
|
+
category: 'system',
|
|
48
|
+
kind: 'error',
|
|
49
|
+
severity: 'error',
|
|
50
|
+
preferenceKey: NOTIFICATION_PREFERENCE_KEYS.ERROR,
|
|
51
|
+
description: 'A generic Pixcode error occurred.',
|
|
52
|
+
},
|
|
53
|
+
[NOTIFICATION_EVENT_TYPES.TEST_FAILED]: {
|
|
54
|
+
category: 'verification',
|
|
55
|
+
kind: 'error',
|
|
56
|
+
severity: 'error',
|
|
57
|
+
preferenceKey: NOTIFICATION_PREFERENCE_KEYS.ERROR,
|
|
58
|
+
description: 'A build, lint, typecheck, or test command failed.',
|
|
59
|
+
},
|
|
60
|
+
[NOTIFICATION_EVENT_TYPES.LIVE_VIEW_FAILED]: {
|
|
61
|
+
category: 'live_view',
|
|
62
|
+
kind: 'error',
|
|
63
|
+
severity: 'error',
|
|
64
|
+
preferenceKey: NOTIFICATION_PREFERENCE_KEYS.ERROR,
|
|
65
|
+
description: 'A Live View preview failed to start or stay healthy.',
|
|
66
|
+
},
|
|
67
|
+
[NOTIFICATION_EVENT_TYPES.RUN_STOPPED]: {
|
|
68
|
+
category: 'run',
|
|
69
|
+
kind: 'stop',
|
|
70
|
+
severity: 'info',
|
|
71
|
+
preferenceKey: NOTIFICATION_PREFERENCE_KEYS.STOP,
|
|
72
|
+
description: 'A run stopped without reporting a failure.',
|
|
73
|
+
},
|
|
74
|
+
[NOTIFICATION_EVENT_TYPES.RUN_FAILED]: {
|
|
75
|
+
category: 'run',
|
|
76
|
+
kind: 'error',
|
|
77
|
+
severity: 'error',
|
|
78
|
+
preferenceKey: NOTIFICATION_PREFERENCE_KEYS.ERROR,
|
|
79
|
+
description: 'A run failed.',
|
|
80
|
+
},
|
|
81
|
+
[NOTIFICATION_EVENT_TYPES.AGENT_NOTIFICATION]: {
|
|
82
|
+
category: 'agent',
|
|
83
|
+
kind: 'action_required',
|
|
84
|
+
severity: 'warning',
|
|
85
|
+
preferenceKey: NOTIFICATION_PREFERENCE_KEYS.ACTION_REQUIRED,
|
|
86
|
+
requiresUserAction: true,
|
|
87
|
+
description: 'An agent emitted a notification for the user.',
|
|
88
|
+
},
|
|
89
|
+
[NOTIFICATION_EVENT_TYPES.PUSH_ENABLED]: {
|
|
90
|
+
category: 'settings',
|
|
91
|
+
kind: 'info',
|
|
92
|
+
severity: 'info',
|
|
93
|
+
preferenceKey: NOTIFICATION_PREFERENCE_KEYS.UPDATES,
|
|
94
|
+
description: 'Push notifications were enabled.',
|
|
95
|
+
},
|
|
96
|
+
[NOTIFICATION_EVENT_TYPES.APP_UPDATE_AVAILABLE]: {
|
|
97
|
+
category: 'update',
|
|
98
|
+
kind: 'update',
|
|
99
|
+
severity: 'info',
|
|
100
|
+
preferenceKey: NOTIFICATION_PREFERENCE_KEYS.UPDATES,
|
|
101
|
+
description: 'A Pixcode app update is available.',
|
|
102
|
+
},
|
|
103
|
+
[NOTIFICATION_EVENT_TYPES.CLI_UPDATE_AVAILABLE]: {
|
|
104
|
+
category: 'update',
|
|
105
|
+
kind: 'update',
|
|
106
|
+
severity: 'info',
|
|
107
|
+
preferenceKey: NOTIFICATION_PREFERENCE_KEYS.UPDATES,
|
|
108
|
+
description: 'A CLI update is available.',
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const LEGACY_CODE_TO_EVENT_TYPE = Object.freeze({
|
|
113
|
+
'permission.required': NOTIFICATION_EVENT_TYPES.APPROVAL_NEEDED,
|
|
114
|
+
'run.stopped': NOTIFICATION_EVENT_TYPES.RUN_STOPPED,
|
|
115
|
+
'run.failed': NOTIFICATION_EVENT_TYPES.RUN_FAILED,
|
|
116
|
+
'agent.notification': NOTIFICATION_EVENT_TYPES.AGENT_NOTIFICATION,
|
|
117
|
+
'push.enabled': NOTIFICATION_EVENT_TYPES.PUSH_ENABLED,
|
|
118
|
+
'app.update.available': NOTIFICATION_EVENT_TYPES.APP_UPDATE_AVAILABLE,
|
|
119
|
+
'cli.update.available': NOTIFICATION_EVENT_TYPES.CLI_UPDATE_AVAILABLE,
|
|
120
|
+
'chat.done': NOTIFICATION_EVENT_TYPES.CHAT_DONE,
|
|
121
|
+
'orchestration.done': NOTIFICATION_EVENT_TYPES.ORCHESTRATION_DONE,
|
|
122
|
+
'test.failed': NOTIFICATION_EVENT_TYPES.TEST_FAILED,
|
|
123
|
+
'live_view.failed': NOTIFICATION_EVENT_TYPES.LIVE_VIEW_FAILED,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const KIND_TO_EVENT_TYPE = Object.freeze({
|
|
127
|
+
action_required: NOTIFICATION_EVENT_TYPES.APPROVAL_NEEDED,
|
|
128
|
+
stop: NOTIFICATION_EVENT_TYPES.RUN_STOPPED,
|
|
129
|
+
error: NOTIFICATION_EVENT_TYPES.ERROR,
|
|
130
|
+
update: NOTIFICATION_EVENT_TYPES.APP_UPDATE_AVAILABLE,
|
|
131
|
+
info: NOTIFICATION_EVENT_TYPES.AGENT_NOTIFICATION,
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
function resolveNotificationEventType(event = {}) {
|
|
135
|
+
if (typeof event.eventType === 'string' && NOTIFICATION_TAXONOMY[event.eventType]) {
|
|
136
|
+
return event.eventType;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (typeof event.code === 'string' && LEGACY_CODE_TO_EVENT_TYPE[event.code]) {
|
|
140
|
+
return LEGACY_CODE_TO_EVENT_TYPE[event.code];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (typeof event.kind === 'string' && KIND_TO_EVENT_TYPE[event.kind]) {
|
|
144
|
+
return KIND_TO_EVENT_TYPE[event.kind];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return NOTIFICATION_EVENT_TYPES.AGENT_NOTIFICATION;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function getNotificationTaxonomy(event = {}) {
|
|
151
|
+
return NOTIFICATION_TAXONOMY[resolveNotificationEventType(event)];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function getNotificationPreferenceKey(event = {}) {
|
|
155
|
+
return getNotificationTaxonomy(event)?.preferenceKey || NOTIFICATION_PREFERENCE_KEYS.UPDATES;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function normalizeNotificationEvent(event = {}) {
|
|
159
|
+
const eventType = resolveNotificationEventType(event);
|
|
160
|
+
const taxonomy = NOTIFICATION_TAXONOMY[eventType];
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
...event,
|
|
164
|
+
eventType,
|
|
165
|
+
category: taxonomy.category,
|
|
166
|
+
kind: event.kind || taxonomy.kind,
|
|
167
|
+
severity: event.severity || taxonomy.severity,
|
|
168
|
+
preferenceKey: taxonomy.preferenceKey,
|
|
169
|
+
requiresUserAction: Boolean(event.requiresUserAction ?? taxonomy.requiresUserAction),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function createNotificationEventContract(event = {}) {
|
|
174
|
+
const normalized = normalizeNotificationEvent(event);
|
|
175
|
+
const taxonomy = NOTIFICATION_TAXONOMY[normalized.eventType];
|
|
176
|
+
|
|
177
|
+
return {
|
|
178
|
+
eventType: normalized.eventType,
|
|
179
|
+
category: normalized.category,
|
|
180
|
+
kind: normalized.kind,
|
|
181
|
+
severity: normalized.severity,
|
|
182
|
+
preferenceKey: normalized.preferenceKey,
|
|
183
|
+
requiresUserAction: normalized.requiresUserAction,
|
|
184
|
+
description: taxonomy.description,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function listNotificationTaxonomy() {
|
|
189
|
+
return Object.keys(NOTIFICATION_TAXONOMY).map((eventType) =>
|
|
190
|
+
createNotificationEventContract({ eventType })
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export {
|
|
195
|
+
NOTIFICATION_EVENT_TYPES,
|
|
196
|
+
NOTIFICATION_PREFERENCE_KEYS,
|
|
197
|
+
NOTIFICATION_TAXONOMY,
|
|
198
|
+
createNotificationEventContract,
|
|
199
|
+
getNotificationPreferenceKey,
|
|
200
|
+
getNotificationTaxonomy,
|
|
201
|
+
listNotificationTaxonomy,
|
|
202
|
+
normalizeNotificationEvent,
|
|
203
|
+
resolveNotificationEventType,
|
|
204
|
+
};
|