vintasend-sendgrid 0.4.3
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 +292 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/sendgrid-notification-adapter.d.ts +24 -0
- package/dist/sendgrid-notification-adapter.d.ts.map +1 -0
- package/dist/sendgrid-notification-adapter.js +53 -0
- package/package.json +26 -0
package/README.md
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
# VintaSend SendGrid Adapter
|
|
2
|
+
|
|
3
|
+
A VintaSend email notification adapter using [SendGrid](https://sendgrid.com/) for reliable email delivery with attachment support.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install vintasend-sendgrid @sendgrid/mail
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Configuration
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { SendgridNotificationAdapterFactory } from 'vintasend-sendgrid';
|
|
15
|
+
|
|
16
|
+
const adapter = new SendgridNotificationAdapterFactory().create(
|
|
17
|
+
templateRenderer,
|
|
18
|
+
false, // enqueueNotifications
|
|
19
|
+
{
|
|
20
|
+
apiKey: process.env.SENDGRID_API_KEY,
|
|
21
|
+
fromEmail: 'noreply@example.com',
|
|
22
|
+
fromName: 'My App', // optional
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Configuration Options
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
interface SendgridConfig {
|
|
31
|
+
apiKey: string; // SendGrid API key
|
|
32
|
+
fromEmail: string; // Default sender email address
|
|
33
|
+
fromName?: string; // Optional sender name
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
### Basic Email
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
await notificationService.createNotification({
|
|
43
|
+
userId: '123',
|
|
44
|
+
notificationType: 'EMAIL',
|
|
45
|
+
contextName: 'welcome',
|
|
46
|
+
contextParameters: { firstName: 'John' },
|
|
47
|
+
title: 'Welcome!',
|
|
48
|
+
bodyTemplate: '/templates/welcome.pug',
|
|
49
|
+
subjectTemplate: '/templates/subjects/welcome.pug',
|
|
50
|
+
sendAfter: new Date(),
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Email with Attachments
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { readFile } from 'fs/promises';
|
|
58
|
+
|
|
59
|
+
await notificationService.createNotification({
|
|
60
|
+
userId: '123',
|
|
61
|
+
notificationType: 'EMAIL',
|
|
62
|
+
contextName: 'invoice',
|
|
63
|
+
contextParameters: { invoiceNumber: 'INV-001' },
|
|
64
|
+
title: 'Your invoice',
|
|
65
|
+
bodyTemplate: '/templates/invoice.pug',
|
|
66
|
+
subjectTemplate: '/templates/subjects/invoice.pug',
|
|
67
|
+
sendAfter: new Date(),
|
|
68
|
+
attachments: [
|
|
69
|
+
{
|
|
70
|
+
file: await readFile('./invoice.pdf'),
|
|
71
|
+
filename: 'invoice.pdf',
|
|
72
|
+
contentType: 'application/pdf',
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### One-Off Notifications
|
|
79
|
+
|
|
80
|
+
Send emails without a user account:
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
await notificationService.createOneOffNotification({
|
|
84
|
+
emailOrPhone: 'customer@example.com',
|
|
85
|
+
firstName: 'Jane',
|
|
86
|
+
lastName: 'Smith',
|
|
87
|
+
notificationType: 'EMAIL',
|
|
88
|
+
contextName: 'order-confirmation',
|
|
89
|
+
contextParameters: { orderNumber: '12345' },
|
|
90
|
+
title: 'Order Confirmation',
|
|
91
|
+
bodyTemplate: '/templates/order-confirmation.pug',
|
|
92
|
+
subjectTemplate: '/templates/subjects/order-confirmation.pug',
|
|
93
|
+
sendAfter: new Date(),
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Features
|
|
98
|
+
|
|
99
|
+
- ✅ Email delivery via SendGrid API
|
|
100
|
+
- ✅ File attachments (automatically base64 encoded)
|
|
101
|
+
- ✅ One-off notifications
|
|
102
|
+
- ✅ Scheduled notifications
|
|
103
|
+
- ✅ Custom sender name and email
|
|
104
|
+
- ✅ HTML email templates
|
|
105
|
+
- ✅ Multiple attachments per email
|
|
106
|
+
|
|
107
|
+
## API Reference
|
|
108
|
+
|
|
109
|
+
### SendgridNotificationAdapterFactory
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
class SendgridNotificationAdapterFactory<Config extends BaseNotificationTypeConfig>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Methods:**
|
|
116
|
+
- `create<TemplateRenderer>(templateRenderer, enqueueNotifications, config)` - Create adapter instance
|
|
117
|
+
|
|
118
|
+
### SendgridNotificationAdapter
|
|
119
|
+
|
|
120
|
+
**Properties:**
|
|
121
|
+
- `key: string` - Returns `'sendgrid'`
|
|
122
|
+
- `notificationType: NotificationType` - Returns `'EMAIL'`
|
|
123
|
+
- `supportsAttachments: boolean` - Returns `true`
|
|
124
|
+
|
|
125
|
+
**Methods:**
|
|
126
|
+
- `send(notification, context)` - Send an email with optional attachments
|
|
127
|
+
|
|
128
|
+
## Environment Variables
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
SENDGRID_API_KEY=SG.your-api-key-here
|
|
132
|
+
FROM_EMAIL=noreply@example.com
|
|
133
|
+
FROM_NAME=My Application
|
|
134
|
+
|
|
135
|
+
The `TemplateAttachmentManager` provides a structure for implementing file attachment storage for notifications.
|
|
136
|
+
|
|
137
|
+
**Supported Storage Backends:**
|
|
138
|
+
- AWS S3 (see [`vintasend-aws-s3-attachments`](../vintasend-aws-s3-attachments))
|
|
139
|
+
- Azure Blob Storage
|
|
140
|
+
- Google Cloud Storage
|
|
141
|
+
- Local Filesystem (development only)
|
|
142
|
+
- Any S3-compatible storage (MinIO, DigitalOcean Spaces, etc.)
|
|
143
|
+
|
|
144
|
+
**Implementation Steps:**
|
|
145
|
+
|
|
146
|
+
1. **Copy this template** to a new package:
|
|
147
|
+
```bash
|
|
148
|
+
cp -r src/implementations/vintasend-implementation-template src/implementations/vintasend-{your-storage}-attachments
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
2. **Update package.json**:
|
|
152
|
+
- Change package name to match your implementation
|
|
153
|
+
- Add storage-specific dependencies (e.g., `@aws-sdk/client-s3` for S3)
|
|
154
|
+
- Update description and keywords
|
|
155
|
+
|
|
156
|
+
4. **Install dependencies**:
|
|
157
|
+
```bash
|
|
158
|
+
npm install
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
5. **Rename the class**:
|
|
162
|
+
- Rename `TemplateAttachmentManager` to your implementation name (e.g., `S3AttachmentManager`)
|
|
163
|
+
- Update all references in exports and tests
|
|
164
|
+
|
|
165
|
+
4. **Implement the required methods**:
|
|
166
|
+
|
|
167
|
+
- `uploadFile(file, filename, contentType?)` - Upload file to your storage backend
|
|
168
|
+
- Convert file to Buffer using `this.fileToBuffer(file)`
|
|
169
|
+
- Calculate checksum using `this.calculateChecksum(buffer)`
|
|
170
|
+
- Auto-detect content type using `this.detectContentType(filename)`
|
|
171
|
+
- Upload to storage and return file record
|
|
172
|
+
|
|
173
|
+
- `getFile(fileId)` - Retrieve file record from database
|
|
174
|
+
- Query your backend's database for file metadata
|
|
175
|
+
- Return `AttachmentFileRecord` or `null`
|
|
176
|
+
|
|
177
|
+
- `deleteFile(fileId)` - Delete file from storage
|
|
178
|
+
- Remove from storage backend
|
|
179
|
+
- Remove file record from database
|
|
180
|
+
- Only called for orphaned files (not referenced by any notifications)
|
|
181
|
+
|
|
182
|
+
- `reconstructAttachmentFile(storageMetadata)` - Recreate file accessor
|
|
183
|
+
- Create and return an `AttachmentFile` instance
|
|
184
|
+
- Use storage metadata to configure access (e.g., S3 bucket/key)
|
|
185
|
+
|
|
186
|
+
- `findFileByChecksum(checksum)` _(optional)_ - Enable file deduplication
|
|
187
|
+
- Query database for existing file with same checksum
|
|
188
|
+
- Return existing `AttachmentFileRecord` or `null`
|
|
189
|
+
- Enables automatic deduplication when uploading identical files
|
|
190
|
+
|
|
191
|
+
5. **Implement the AttachmentFile class**:
|
|
192
|
+
|
|
193
|
+
Create a storage-specific implementation of the `AttachmentFile` interface:
|
|
194
|
+
|
|
195
|
+
- `read()` - Load entire file into memory as Buffer
|
|
196
|
+
- `stream()` - Return ReadableStream for large files
|
|
197
|
+
- `url(expiresIn?)` - Generate presigned/temporary URL for access
|
|
198
|
+
- `delete()` - Delete file from storage
|
|
199
|
+
|
|
200
|
+
6. **Write comprehensive tests**:
|
|
201
|
+
- Use the test template as a starting point
|
|
202
|
+
- Test all methods with various file types
|
|
203
|
+
- Test error handling
|
|
204
|
+
- Use mocks for unit tests
|
|
205
|
+
- Consider integration tests with real storage (or LocalStack/emulators)
|
|
206
|
+
|
|
207
|
+
7. **Document configuration**:
|
|
208
|
+
- Document all configuration options
|
|
209
|
+
- Provide usage examples
|
|
210
|
+
- Document authentication methods
|
|
211
|
+
- Include troubleshooting tips
|
|
212
|
+
|
|
213
|
+
**Example Implementation:**
|
|
214
|
+
|
|
215
|
+
See [`vintasend-aws-s3-attachments`](../vintasend-aws-s3-attachments) for a complete AWS S3 implementation that follows this pattern.
|
|
216
|
+
|
|
217
|
+
**Key Design Patterns:**
|
|
218
|
+
|
|
219
|
+
- **Reusable Files**: Files are stored once in `AttachmentFile` table and referenced by multiple notifications via `NotificationAttachment` join table
|
|
220
|
+
- **Deduplication**: Implement `findFileByChecksum()` to prevent storing duplicate files
|
|
221
|
+
- **Presigned URLs**: Generate temporary URLs for secure file access without exposing credentials
|
|
222
|
+
- **Streaming**: Support streaming for large files to avoid memory issues
|
|
223
|
+
- **Type Safety**: All methods use strict TypeScript types from `vintasend/dist/types/attachment`
|
|
224
|
+
|
|
225
|
+
## Other Components
|
|
226
|
+
|
|
227
|
+
### Adapter
|
|
228
|
+
|
|
229
|
+
Custom notification delivery adapters (email, SMS, push notifications, etc.)
|
|
230
|
+
|
|
231
|
+
**Examples:**
|
|
232
|
+
- `vintasend-nodemailer` - Email via Nodemailer
|
|
233
|
+
- Custom SMS adapter
|
|
234
|
+
- Custom push notification adapter
|
|
235
|
+
|
|
236
|
+
### Backend
|
|
237
|
+
|
|
238
|
+
Custom database persistence layers
|
|
239
|
+
|
|
240
|
+
**Examples:**
|
|
241
|
+
- `vintasend-prisma` - Prisma ORM backend
|
|
242
|
+
- Custom MongoDB backend
|
|
243
|
+
- Custom PostgreSQL backend
|
|
244
|
+
|
|
245
|
+
### Template Renderer
|
|
246
|
+
|
|
247
|
+
Custom notification content rendering
|
|
248
|
+
|
|
249
|
+
**Examples:**
|
|
250
|
+
- `vintasend-pug` - Pug template engine
|
|
251
|
+
- Custom Handlebars renderer
|
|
252
|
+
- Custom React email renderer
|
|
253
|
+
|
|
254
|
+
### Logger
|
|
255
|
+
|
|
256
|
+
Custom logging implementations
|
|
257
|
+
|
|
258
|
+
**Examples:**
|
|
259
|
+
- `vintasend-winston` - Winston logger
|
|
260
|
+
- Custom Pino logger
|
|
261
|
+
- Custom cloud logging service
|
|
262
|
+
|
|
263
|
+
## Getting Started
|
|
264
|
+
|
|
265
|
+
1. Choose the component type you want to implement
|
|
266
|
+
2. Copy this template package to a new directory
|
|
267
|
+
3. Follow the implementation steps for that component type
|
|
268
|
+
4. Write comprehensive tests
|
|
269
|
+
5. Document your implementation
|
|
270
|
+
6. Publish as a separate npm package (optional)
|
|
271
|
+
|
|
272
|
+
## Best Practices
|
|
273
|
+
|
|
274
|
+
- **Type Safety**: Use TypeScript strict mode and leverage VintaSend's type system
|
|
275
|
+
- **Testing**: Aim for high test coverage, including edge cases and error conditions
|
|
276
|
+
- **Documentation**: Document all configuration options and provide clear examples
|
|
277
|
+
- **Error Handling**: Provide clear error messages and proper error types
|
|
278
|
+
- **Performance**: Consider streaming for large files, connection pooling for databases, etc.
|
|
279
|
+
- **Security**: Never expose credentials, use presigned URLs, validate inputs
|
|
280
|
+
|
|
281
|
+
## Contributing
|
|
282
|
+
|
|
283
|
+
When creating implementations:
|
|
284
|
+
- Follow the existing code style (use Biome for linting)
|
|
285
|
+
- Include comprehensive tests
|
|
286
|
+
- Document all public APIs
|
|
287
|
+
- Add examples in README
|
|
288
|
+
- Consider adding to the main VintaSend monorepo
|
|
289
|
+
|
|
290
|
+
## License
|
|
291
|
+
|
|
292
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,kCAAkC,EAAE,MAAM,iCAAiC,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SendgridNotificationAdapter, SendgridNotificationAdapterFactory } from './sendgrid-notification-adapter';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { AttachmentData } from '@sendgrid/helpers/classes/attachment';
|
|
2
|
+
import { BaseNotificationAdapter } from 'vintasend/dist/services/notification-adapters/base-notification-adapter';
|
|
3
|
+
import type { BaseEmailTemplateRenderer } from 'vintasend/dist/services/notification-template-renderers/base-email-template-renderer';
|
|
4
|
+
import type { JsonObject } from 'vintasend/dist/types/json-values';
|
|
5
|
+
import type { AnyDatabaseNotification } from 'vintasend/dist/types/notification';
|
|
6
|
+
import type { BaseNotificationTypeConfig } from 'vintasend/dist/types/notification-type-config';
|
|
7
|
+
import type { StoredAttachment } from 'vintasend/dist/types/attachment';
|
|
8
|
+
export interface SendgridConfig {
|
|
9
|
+
apiKey: string;
|
|
10
|
+
fromEmail: string;
|
|
11
|
+
fromName?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare class SendgridNotificationAdapter<TemplateRenderer extends BaseEmailTemplateRenderer<Config>, Config extends BaseNotificationTypeConfig> extends BaseNotificationAdapter<TemplateRenderer, Config> {
|
|
14
|
+
key: string | null;
|
|
15
|
+
private config;
|
|
16
|
+
constructor(templateRenderer: TemplateRenderer, enqueueNotifications: boolean, config: SendgridConfig);
|
|
17
|
+
get supportsAttachments(): boolean;
|
|
18
|
+
send(notification: AnyDatabaseNotification<Config>, context: JsonObject): Promise<void>;
|
|
19
|
+
protected prepareAttachments(attachments: StoredAttachment[]): Promise<AttachmentData[]>;
|
|
20
|
+
}
|
|
21
|
+
export declare class SendgridNotificationAdapterFactory<Config extends BaseNotificationTypeConfig> {
|
|
22
|
+
create<TemplateRenderer extends BaseEmailTemplateRenderer<Config>>(templateRenderer: TemplateRenderer, enqueueNotifications: boolean, config: SendgridConfig): SendgridNotificationAdapter<TemplateRenderer, Config>;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=sendgrid-notification-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sendgrid-notification-adapter.d.ts","sourceRoot":"","sources":["../src/sendgrid-notification-adapter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AAE3E,OAAO,EAAE,uBAAuB,EAAE,MAAM,yEAAyE,CAAC;AAClH,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,sFAAsF,CAAC;AACtI,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,mCAAmC,CAAC;AACjF,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+CAA+C,CAAC;AAChG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAExE,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,2BAA2B,CACtC,gBAAgB,SAAS,yBAAyB,CAAC,MAAM,CAAC,EAC1D,MAAM,SAAS,0BAA0B,CACzC,SAAQ,uBAAuB,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClD,GAAG,EAAE,MAAM,GAAG,IAAI,CAAc;IACvC,OAAO,CAAC,MAAM,CAAiB;gBAG7B,gBAAgB,EAAE,gBAAgB,EAClC,oBAAoB,EAAE,OAAO,EAC7B,MAAM,EAAE,cAAc;IAOxB,IAAI,mBAAmB,IAAI,OAAO,CAEjC;IAEK,IAAI,CAAC,YAAY,EAAE,uBAAuB,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;cA+B7E,kBAAkB,CAChC,WAAW,EAAE,gBAAgB,EAAE,GAC9B,OAAO,CAAC,cAAc,EAAE,CAAC;CAa7B;AAED,qBAAa,kCAAkC,CAAC,MAAM,SAAS,0BAA0B;IACvF,MAAM,CAAC,gBAAgB,SAAS,yBAAyB,CAAC,MAAM,CAAC,EAC/D,gBAAgB,EAAE,gBAAgB,EAClC,oBAAoB,EAAE,OAAO,EAC7B,MAAM,EAAE,cAAc;CAQzB"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import sgMail from '@sendgrid/mail';
|
|
2
|
+
import { BaseNotificationAdapter } from 'vintasend/dist/services/notification-adapters/base-notification-adapter';
|
|
3
|
+
export class SendgridNotificationAdapter extends BaseNotificationAdapter {
|
|
4
|
+
constructor(templateRenderer, enqueueNotifications, config) {
|
|
5
|
+
super(templateRenderer, 'EMAIL', enqueueNotifications);
|
|
6
|
+
this.key = 'sendgrid';
|
|
7
|
+
this.config = config;
|
|
8
|
+
sgMail.setApiKey(config.apiKey);
|
|
9
|
+
}
|
|
10
|
+
get supportsAttachments() {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
async send(notification, context) {
|
|
14
|
+
if (!this.backend) {
|
|
15
|
+
throw new Error('Backend not injected');
|
|
16
|
+
}
|
|
17
|
+
const template = await this.templateRenderer.render(notification, context);
|
|
18
|
+
if (!notification.id) {
|
|
19
|
+
throw new Error('Notification ID is required');
|
|
20
|
+
}
|
|
21
|
+
// Use the helper method to get recipient email (handles both regular and one-off notifications)
|
|
22
|
+
const recipientEmail = await this.getRecipientEmail(notification);
|
|
23
|
+
const mailData = {
|
|
24
|
+
to: recipientEmail,
|
|
25
|
+
from: this.config.fromName
|
|
26
|
+
? { email: this.config.fromEmail, name: this.config.fromName }
|
|
27
|
+
: this.config.fromEmail,
|
|
28
|
+
subject: template.subject,
|
|
29
|
+
html: template.body,
|
|
30
|
+
};
|
|
31
|
+
// Add attachments if present
|
|
32
|
+
if (notification.attachments && notification.attachments.length > 0) {
|
|
33
|
+
mailData.attachments = await this.prepareAttachments(notification.attachments);
|
|
34
|
+
}
|
|
35
|
+
await sgMail.send(mailData);
|
|
36
|
+
}
|
|
37
|
+
async prepareAttachments(attachments) {
|
|
38
|
+
return Promise.all(attachments.map(async (att) => {
|
|
39
|
+
const content = await att.file.read();
|
|
40
|
+
return {
|
|
41
|
+
filename: att.filename,
|
|
42
|
+
content: content.toString('base64'),
|
|
43
|
+
type: att.contentType,
|
|
44
|
+
disposition: 'attachment',
|
|
45
|
+
};
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export class SendgridNotificationAdapterFactory {
|
|
50
|
+
create(templateRenderer, enqueueNotifications, config) {
|
|
51
|
+
return new SendgridNotificationAdapter(templateRenderer, enqueueNotifications, config);
|
|
52
|
+
}
|
|
53
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vintasend-sendgrid",
|
|
3
|
+
"version": "0.4.3",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsc",
|
|
8
|
+
"prepublishOnly": "npm run build",
|
|
9
|
+
"test": "jest",
|
|
10
|
+
"test:watch": "jest --watch",
|
|
11
|
+
"test:coverage": "jest --coverage"
|
|
12
|
+
},
|
|
13
|
+
"files": ["dist"],
|
|
14
|
+
"author": "Hugo Bessa",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"vintasend": "^0.4.3",
|
|
18
|
+
"@sendgrid/mail": "^8.1.6"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/jest": "^30.0.0",
|
|
22
|
+
"jest": "^30.2.0",
|
|
23
|
+
"ts-jest": "^29.4.6",
|
|
24
|
+
"typescript": "^5.9.3"
|
|
25
|
+
}
|
|
26
|
+
}
|