@spfn/notification 0.1.0-beta.1 → 0.1.0-beta.10
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/README.md +33 -1
- package/dist/config/index.d.ts +14 -0
- package/dist/{index-D_XTwHmO.d.ts → index-BAe1ZBYQ.d.ts} +45 -1
- package/dist/index.d.ts +1 -1
- package/dist/server.d.ts +102 -5
- package/dist/server.js +294 -4
- package/dist/server.js.map +1 -1
- package/migrations/0000_acoustic_bromley.sql +32 -0
- package/migrations/meta/0000_snapshot.json +278 -0
- package/migrations/meta/_journal.json +13 -0
- package/package.json +12 -2
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Multi-channel notification system for SPFN applications.
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **Multi-channel support**: Email, SMS
|
|
7
|
+
- **Multi-channel support**: Email, SMS, Slack (Push coming soon)
|
|
8
8
|
- **Provider pattern**: Pluggable providers (AWS SES, AWS SNS, etc.)
|
|
9
9
|
- **Template system**: Variable substitution with filters
|
|
10
10
|
- **Scheduled delivery**: Schedule notifications for later via pg-boss
|
|
@@ -282,6 +282,32 @@ const twilioProvider: SMSProvider = {
|
|
|
282
282
|
registerSMSProvider(twilioProvider);
|
|
283
283
|
```
|
|
284
284
|
|
|
285
|
+
## Logging & Error Handling
|
|
286
|
+
|
|
287
|
+
All channels log via `@spfn/core/logger`. Logs are tagged by channel:
|
|
288
|
+
|
|
289
|
+
- `@spfn/notification:email` — Email send success/failure, validation errors
|
|
290
|
+
- `@spfn/notification:sms` — SMS send success/failure, validation errors
|
|
291
|
+
- `@spfn/notification:slack` — Slack send success/failure, validation errors
|
|
292
|
+
- `@spfn/notification:ses` — AWS SES client lifecycle, provider errors
|
|
293
|
+
- `@spfn/notification:sns` — AWS SNS client lifecycle, provider errors
|
|
294
|
+
|
|
295
|
+
`sendEmail`, `sendSMS`, `sendSlack` are designed to **not throw** — they return a `SendResult` object. Always check the result:
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
const result = await sendEmail({
|
|
299
|
+
to: 'user@example.com',
|
|
300
|
+
template: 'welcome',
|
|
301
|
+
data: { name: 'John' },
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
if (!result.success)
|
|
305
|
+
{
|
|
306
|
+
// result.error contains the failure reason
|
|
307
|
+
console.error('Email failed:', result.error);
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
285
311
|
## Notification History
|
|
286
312
|
|
|
287
313
|
Enable history tracking to store all notifications in the database:
|
|
@@ -333,6 +359,7 @@ export type {
|
|
|
333
359
|
SendResult,
|
|
334
360
|
SendEmailParams,
|
|
335
361
|
SendSMSParams,
|
|
362
|
+
SendSlackParams,
|
|
336
363
|
TemplateDefinition,
|
|
337
364
|
TemplateData,
|
|
338
365
|
};
|
|
@@ -353,6 +380,11 @@ export {
|
|
|
353
380
|
sendSMSBulk,
|
|
354
381
|
registerSMSProvider,
|
|
355
382
|
|
|
383
|
+
// Slack
|
|
384
|
+
sendSlack,
|
|
385
|
+
sendSlackBulk,
|
|
386
|
+
registerSlackProvider,
|
|
387
|
+
|
|
356
388
|
// Scheduling
|
|
357
389
|
scheduleEmail,
|
|
358
390
|
scheduleSMS,
|
package/dist/config/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ declare const notificationEnvSchema: {
|
|
|
10
10
|
required: boolean;
|
|
11
11
|
examples: string[];
|
|
12
12
|
type: "string";
|
|
13
|
+
validator: (value: string) => string;
|
|
13
14
|
} & {
|
|
14
15
|
key: "SPFN_NOTIFICATION_EMAIL_PROVIDER";
|
|
15
16
|
};
|
|
@@ -18,6 +19,7 @@ declare const notificationEnvSchema: {
|
|
|
18
19
|
required: boolean;
|
|
19
20
|
examples: string[];
|
|
20
21
|
type: "string";
|
|
22
|
+
validator: (value: string) => string;
|
|
21
23
|
} & {
|
|
22
24
|
key: "SPFN_NOTIFICATION_EMAIL_FROM";
|
|
23
25
|
};
|
|
@@ -27,6 +29,7 @@ declare const notificationEnvSchema: {
|
|
|
27
29
|
required: boolean;
|
|
28
30
|
examples: string[];
|
|
29
31
|
type: "string";
|
|
32
|
+
validator: (value: string) => string;
|
|
30
33
|
} & {
|
|
31
34
|
key: "SPFN_NOTIFICATION_SMS_PROVIDER";
|
|
32
35
|
};
|
|
@@ -35,6 +38,7 @@ declare const notificationEnvSchema: {
|
|
|
35
38
|
required: boolean;
|
|
36
39
|
examples: string[];
|
|
37
40
|
type: "string";
|
|
41
|
+
validator: (value: string) => string;
|
|
38
42
|
} & {
|
|
39
43
|
key: "SPFN_NOTIFICATION_SLACK_WEBHOOK_URL";
|
|
40
44
|
};
|
|
@@ -44,6 +48,7 @@ declare const notificationEnvSchema: {
|
|
|
44
48
|
required: boolean;
|
|
45
49
|
examples: string[];
|
|
46
50
|
type: "string";
|
|
51
|
+
validator: (value: string) => string;
|
|
47
52
|
} & {
|
|
48
53
|
key: "AWS_REGION";
|
|
49
54
|
};
|
|
@@ -52,6 +57,7 @@ declare const notificationEnvSchema: {
|
|
|
52
57
|
required: boolean;
|
|
53
58
|
sensitive: boolean;
|
|
54
59
|
type: "string";
|
|
60
|
+
validator: (value: string) => string;
|
|
55
61
|
} & {
|
|
56
62
|
key: "AWS_ACCESS_KEY_ID";
|
|
57
63
|
};
|
|
@@ -60,6 +66,7 @@ declare const notificationEnvSchema: {
|
|
|
60
66
|
required: boolean;
|
|
61
67
|
sensitive: boolean;
|
|
62
68
|
type: "string";
|
|
69
|
+
validator: (value: string) => string;
|
|
63
70
|
} & {
|
|
64
71
|
key: "AWS_SECRET_ACCESS_KEY";
|
|
65
72
|
};
|
|
@@ -72,6 +79,7 @@ declare const env: _spfn_core_env.InferEnvType<{
|
|
|
72
79
|
required: boolean;
|
|
73
80
|
examples: string[];
|
|
74
81
|
type: "string";
|
|
82
|
+
validator: (value: string) => string;
|
|
75
83
|
} & {
|
|
76
84
|
key: "SPFN_NOTIFICATION_EMAIL_PROVIDER";
|
|
77
85
|
};
|
|
@@ -80,6 +88,7 @@ declare const env: _spfn_core_env.InferEnvType<{
|
|
|
80
88
|
required: boolean;
|
|
81
89
|
examples: string[];
|
|
82
90
|
type: "string";
|
|
91
|
+
validator: (value: string) => string;
|
|
83
92
|
} & {
|
|
84
93
|
key: "SPFN_NOTIFICATION_EMAIL_FROM";
|
|
85
94
|
};
|
|
@@ -89,6 +98,7 @@ declare const env: _spfn_core_env.InferEnvType<{
|
|
|
89
98
|
required: boolean;
|
|
90
99
|
examples: string[];
|
|
91
100
|
type: "string";
|
|
101
|
+
validator: (value: string) => string;
|
|
92
102
|
} & {
|
|
93
103
|
key: "SPFN_NOTIFICATION_SMS_PROVIDER";
|
|
94
104
|
};
|
|
@@ -97,6 +107,7 @@ declare const env: _spfn_core_env.InferEnvType<{
|
|
|
97
107
|
required: boolean;
|
|
98
108
|
examples: string[];
|
|
99
109
|
type: "string";
|
|
110
|
+
validator: (value: string) => string;
|
|
100
111
|
} & {
|
|
101
112
|
key: "SPFN_NOTIFICATION_SLACK_WEBHOOK_URL";
|
|
102
113
|
};
|
|
@@ -106,6 +117,7 @@ declare const env: _spfn_core_env.InferEnvType<{
|
|
|
106
117
|
required: boolean;
|
|
107
118
|
examples: string[];
|
|
108
119
|
type: "string";
|
|
120
|
+
validator: (value: string) => string;
|
|
109
121
|
} & {
|
|
110
122
|
key: "AWS_REGION";
|
|
111
123
|
};
|
|
@@ -114,6 +126,7 @@ declare const env: _spfn_core_env.InferEnvType<{
|
|
|
114
126
|
required: boolean;
|
|
115
127
|
sensitive: boolean;
|
|
116
128
|
type: "string";
|
|
129
|
+
validator: (value: string) => string;
|
|
117
130
|
} & {
|
|
118
131
|
key: "AWS_ACCESS_KEY_ID";
|
|
119
132
|
};
|
|
@@ -122,6 +135,7 @@ declare const env: _spfn_core_env.InferEnvType<{
|
|
|
122
135
|
required: boolean;
|
|
123
136
|
sensitive: boolean;
|
|
124
137
|
type: "string";
|
|
138
|
+
validator: (value: string) => string;
|
|
125
139
|
} & {
|
|
126
140
|
key: "AWS_SECRET_ACCESS_KEY";
|
|
127
141
|
};
|
|
@@ -119,6 +119,50 @@ interface InternalSendSMSParams {
|
|
|
119
119
|
message: string;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
/**
|
|
123
|
+
* @spfn/notification - Slack Channel Types
|
|
124
|
+
*/
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Parameters for sending Slack message
|
|
128
|
+
*/
|
|
129
|
+
interface SendSlackParams {
|
|
130
|
+
/**
|
|
131
|
+
* Webhook URL (overrides env/config default)
|
|
132
|
+
*/
|
|
133
|
+
webhookUrl?: string;
|
|
134
|
+
/**
|
|
135
|
+
* Plain text message
|
|
136
|
+
*/
|
|
137
|
+
text?: string;
|
|
138
|
+
/**
|
|
139
|
+
* Slack Block Kit blocks
|
|
140
|
+
*/
|
|
141
|
+
blocks?: unknown[];
|
|
142
|
+
/**
|
|
143
|
+
* Template name
|
|
144
|
+
*/
|
|
145
|
+
template?: string;
|
|
146
|
+
/**
|
|
147
|
+
* Template data for variable substitution
|
|
148
|
+
*/
|
|
149
|
+
data?: Record<string, unknown>;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Slack provider interface
|
|
153
|
+
*/
|
|
154
|
+
interface SlackProvider extends ChannelProvider<InternalSendSlackParams> {
|
|
155
|
+
name: 'webhook' | string;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Internal send Slack params (after template rendering)
|
|
159
|
+
*/
|
|
160
|
+
interface InternalSendSlackParams {
|
|
161
|
+
webhookUrl: string;
|
|
162
|
+
text?: string;
|
|
163
|
+
blocks?: unknown[];
|
|
164
|
+
}
|
|
165
|
+
|
|
122
166
|
/**
|
|
123
167
|
* @spfn/notification - Template Types
|
|
124
168
|
*/
|
|
@@ -167,4 +211,4 @@ interface RenderedTemplate {
|
|
|
167
211
|
*/
|
|
168
212
|
type TemplateData = Record<string, unknown>;
|
|
169
213
|
|
|
170
|
-
export type { EmailProvider as E, NotificationChannel as N, RenderedTemplate as R, SendEmailParams as S, TemplateDefinition as T, SendResult as a, SendSMSParams as b, SMSProvider as c,
|
|
214
|
+
export type { EmailProvider as E, NotificationChannel as N, RenderedTemplate as R, SendEmailParams as S, TemplateDefinition as T, SendResult as a, SendSMSParams as b, SMSProvider as c, SendSlackParams as d, SlackProvider as e, TemplateData as f, EmailTemplateContent as g, SmsTemplateContent as h, SlackTemplateContent as i };
|
package/dist/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { g as EmailTemplateContent, N as NotificationChannel, S as SendEmailParams, a as SendResult, b as SendSMSParams, d as SendSlackParams, i as SlackTemplateContent, h as SmsTemplateContent, f as TemplateData, T as TemplateDefinition } from './index-BAe1ZBYQ.js';
|
package/dist/server.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { NotificationConfig, configureNotification, getAppName, getEmailFrom, getEmailReplyTo, getNotificationConfig, getSmsDefaultCountryCode } from './config/index.js';
|
|
2
|
-
import { S as SendEmailParams, a as SendResult, E as EmailProvider, b as SendSMSParams, c as SMSProvider, T as TemplateDefinition,
|
|
3
|
-
export {
|
|
2
|
+
import { S as SendEmailParams, a as SendResult, E as EmailProvider, b as SendSMSParams, c as SMSProvider, d as SendSlackParams, e as SlackProvider, T as TemplateDefinition, f as TemplateData, N as NotificationChannel$1, R as RenderedTemplate } from './index-BAe1ZBYQ.js';
|
|
3
|
+
export { g as EmailTemplateContent, i as SlackTemplateContent, h as SmsTemplateContent } from './index-BAe1ZBYQ.js';
|
|
4
4
|
import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
|
|
5
5
|
import * as _spfn_core_job from '@spfn/core/job';
|
|
6
6
|
import '@spfn/core/env';
|
|
@@ -47,6 +47,27 @@ declare function sendSMSBulk(items: SendSMSParams[]): Promise<{
|
|
|
47
47
|
failureCount: number;
|
|
48
48
|
}>;
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* @spfn/notification - Slack Channel
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Register custom Slack provider
|
|
56
|
+
*/
|
|
57
|
+
declare function registerSlackProvider(provider: SlackProvider): void;
|
|
58
|
+
/**
|
|
59
|
+
* Send Slack message
|
|
60
|
+
*/
|
|
61
|
+
declare function sendSlack(params: SendSlackParams): Promise<SendResult>;
|
|
62
|
+
/**
|
|
63
|
+
* Send bulk Slack messages
|
|
64
|
+
*/
|
|
65
|
+
declare function sendSlackBulk(items: SendSlackParams[]): Promise<{
|
|
66
|
+
results: SendResult[];
|
|
67
|
+
successCount: number;
|
|
68
|
+
failureCount: number;
|
|
69
|
+
}>;
|
|
70
|
+
|
|
50
71
|
/**
|
|
51
72
|
* @spfn/notification - Schedule Service
|
|
52
73
|
*
|
|
@@ -606,13 +627,13 @@ declare function cancelNotificationsByReference(referenceType: string, reference
|
|
|
606
627
|
* Scheduled email sending job
|
|
607
628
|
*/
|
|
608
629
|
declare const sendScheduledEmailJob: _spfn_core_job.JobDef<{
|
|
630
|
+
from?: string | undefined;
|
|
609
631
|
subject?: string | undefined;
|
|
610
632
|
html?: string | undefined;
|
|
611
633
|
text?: string | undefined;
|
|
612
634
|
data?: {
|
|
613
635
|
[x: string]: unknown;
|
|
614
636
|
} | undefined;
|
|
615
|
-
from?: string | undefined;
|
|
616
637
|
template?: string | undefined;
|
|
617
638
|
replyTo?: string | undefined;
|
|
618
639
|
to: string | string[];
|
|
@@ -657,13 +678,13 @@ declare const sendScheduledSmsJob: _spfn_core_job.JobDef<{
|
|
|
657
678
|
*/
|
|
658
679
|
declare const notificationJobRouter: _spfn_core_job.JobRouter<{
|
|
659
680
|
sendScheduledEmail: _spfn_core_job.JobDef<{
|
|
681
|
+
from?: string | undefined;
|
|
660
682
|
subject?: string | undefined;
|
|
661
683
|
html?: string | undefined;
|
|
662
684
|
text?: string | undefined;
|
|
663
685
|
data?: {
|
|
664
686
|
[x: string]: unknown;
|
|
665
687
|
} | undefined;
|
|
666
|
-
from?: string | undefined;
|
|
667
688
|
template?: string | undefined;
|
|
668
689
|
replyTo?: string | undefined;
|
|
669
690
|
to: string | string[];
|
|
@@ -680,4 +701,80 @@ declare const notificationJobRouter: _spfn_core_job.JobRouter<{
|
|
|
680
701
|
}, void>;
|
|
681
702
|
}>;
|
|
682
703
|
|
|
683
|
-
|
|
704
|
+
/**
|
|
705
|
+
* @spfn/notification - Error → Slack Integration
|
|
706
|
+
*
|
|
707
|
+
* Factory for creating an onError callback that sends error notifications to Slack.
|
|
708
|
+
*
|
|
709
|
+
* @example
|
|
710
|
+
* ```typescript
|
|
711
|
+
* import { createServer } from '@spfn/core/server';
|
|
712
|
+
* import { createErrorSlackNotifier } from '@spfn/notification/server';
|
|
713
|
+
*
|
|
714
|
+
* await createServer({
|
|
715
|
+
* middleware: {
|
|
716
|
+
* onError: createErrorSlackNotifier({ minStatusCode: 500 }),
|
|
717
|
+
* },
|
|
718
|
+
* });
|
|
719
|
+
* ```
|
|
720
|
+
*/
|
|
721
|
+
interface ErrorContext {
|
|
722
|
+
statusCode: number;
|
|
723
|
+
path: string;
|
|
724
|
+
method: string;
|
|
725
|
+
requestId?: string;
|
|
726
|
+
timestamp: string;
|
|
727
|
+
userId?: string;
|
|
728
|
+
request: {
|
|
729
|
+
headers: Record<string, string>;
|
|
730
|
+
query: Record<string, string>;
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
interface ErrorSlackOptions {
|
|
734
|
+
/**
|
|
735
|
+
* Minimum status code to trigger notification
|
|
736
|
+
* @default 500
|
|
737
|
+
*/
|
|
738
|
+
minStatusCode?: number;
|
|
739
|
+
/**
|
|
740
|
+
* Throttle window in milliseconds.
|
|
741
|
+
* Duplicate errors (same name + statusCode + path) within this window are suppressed.
|
|
742
|
+
* @default 60_000
|
|
743
|
+
*/
|
|
744
|
+
throttleMs?: number;
|
|
745
|
+
/**
|
|
746
|
+
* Webhook URL override (defaults to env/config)
|
|
747
|
+
*/
|
|
748
|
+
webhookUrl?: string;
|
|
749
|
+
/**
|
|
750
|
+
* Custom message formatter
|
|
751
|
+
*/
|
|
752
|
+
formatMessage?: (err: Error, ctx: ErrorContext) => {
|
|
753
|
+
text?: string;
|
|
754
|
+
blocks?: unknown[];
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* Create an onError callback that sends Slack notifications
|
|
759
|
+
*
|
|
760
|
+
* Returns a function matching ErrorHandler's onError signature.
|
|
761
|
+
* Duplicate errors (same name + statusCode + path) within `throttleMs` are suppressed.
|
|
762
|
+
*
|
|
763
|
+
* @deprecated Use `createMonitorErrorHandler()` from `@spfn/monitor/server` instead.
|
|
764
|
+
* It provides DB-backed error tracking with fingerprint deduplication,
|
|
765
|
+
* state-based notifications (new/reopened), and an admin dashboard.
|
|
766
|
+
*
|
|
767
|
+
* Migration:
|
|
768
|
+
* ```typescript
|
|
769
|
+
* // Before
|
|
770
|
+
* import { createErrorSlackNotifier } from '@spfn/notification/server';
|
|
771
|
+
* middleware: { onError: createErrorSlackNotifier() }
|
|
772
|
+
*
|
|
773
|
+
* // After
|
|
774
|
+
* import { createMonitorErrorHandler } from '@spfn/monitor/server';
|
|
775
|
+
* middleware: { onError: createMonitorErrorHandler() }
|
|
776
|
+
* ```
|
|
777
|
+
*/
|
|
778
|
+
declare function createErrorSlackNotifier(options?: ErrorSlackOptions): (err: Error, ctx: ErrorContext) => Promise<void>;
|
|
779
|
+
|
|
780
|
+
export { type CancelResult, EmailProvider, type ErrorSlackOptions, type FindNotificationsOptions, NOTIFICATION_CHANNELS, NOTIFICATION_STATUSES, type NewNotification, type Notification, NotificationChannel$1 as NotificationChannel, type NotificationStats, type NotificationStatus, SMSProvider, type ScheduleOptions, type ScheduleResult, SendEmailParams, SendResult, SendSMSParams, SendSlackParams, SlackProvider, TemplateData, TemplateDefinition, cancelNotification, cancelNotificationsByReference, cancelScheduledNotification, countNotifications, createErrorSlackNotifier, createNotificationRecord, createScheduledNotification, findNotificationByJobId, findNotifications, findScheduledNotifications, getNotificationStats, getTemplate, getTemplateNames, hasTemplate, markNotificationFailed, markNotificationPending, markNotificationSent, notificationJobRouter, notifications, registerBuiltinTemplates, registerEmailProvider, registerFilter, registerSMSProvider, registerSlackProvider, registerTemplate, renderTemplate, scheduleEmail, scheduleSMS, sendEmail, sendEmailBulk, sendSMS, sendSMSBulk, sendScheduledEmailJob, sendScheduledSmsJob, sendSlack, sendSlackBulk, updateNotificationJobId };
|