{"id":30776277,"url":"https://github.com/vintasoftware/vintasend-ts","last_synced_at":"2026-03-07T05:05:52.299Z","repository":{"id":277109724,"uuid":"931337314","full_name":"vintasoftware/vintasend-ts","owner":"vintasoftware","description":"A flexible package for implementing transactional notifications","archived":false,"fork":false,"pushed_at":"2026-03-03T18:19:16.000Z","size":759,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2026-03-03T21:59:33.928Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vintasoftware.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-02-12T05:24:20.000Z","updated_at":"2026-03-03T18:19:19.000Z","dependencies_parsed_at":"2025-02-12T07:46:33.976Z","dependency_job_id":"49c41dc3-e937-4b0f-89a6-2db39391ba15","html_url":"https://github.com/vintasoftware/vintasend-ts","commit_stats":null,"previous_names":["vintasoftware/vintasend-ts"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/vintasoftware/vintasend-ts","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vintasoftware%2Fvintasend-ts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vintasoftware%2Fvintasend-ts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vintasoftware%2Fvintasend-ts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vintasoftware%2Fvintasend-ts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vintasoftware","download_url":"https://codeload.github.com/vintasoftware/vintasend-ts/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vintasoftware%2Fvintasend-ts/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30208730,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-07T03:24:23.086Z","status":"ssl_error","status_checked_at":"2026-03-07T03:23:11.444Z","response_time":53,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-09-05T04:32:30.241Z","updated_at":"2026-03-07T05:05:52.278Z","avatar_url":"https://github.com/vintasoftware.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# VintaSend TypeScript\n\nA flexible package for implementing transactional notifications in TypeScript.\n\n## Features\n\n* **Storing notifications in a Database**: This package relies on a data store to record all the notifications that will be sent. It also keeps its state column up to date.\n* **One-off notifications**: Send notifications directly to email addresses or phone numbers without requiring a user account. Perfect for prospects, guests, or external contacts.\n* **File Attachments**: Attach files to notifications with flexible storage backend support (S3, Azure, GCS, local filesystem, etc.), automatic deduplication, and reusable file references.\n* **Scheduling notifications**: Storing notifications to be sent in the future. The notification's context for rendering the template is only evaluated at the moment the notification is sent due to the lib's context generation registry.\n* **Notification context fetched at send time**: On scheduled notifications, the package only gets the notification context (information to render the templates) at the send time, so we always get the most up-to-date information.\n* **Flexible backend**: Your project's database is getting slow after you created the first million notifications? You can migrate to a faster NoSQL database in the blink of an eye without affecting how you send the notifications.\n* **Flexible adapters**: Your project probably will need to change how it sends notifications over time. This package allows you to change the adapter without having to change how notification templates are rendered or how the notification themselves are stored.\n* **Flexible template renderers**: Wanna start managing your templates with a third party tool (so non-technical people can help maintain them)? Or even choose a more powerful rendering engine? You can do it independently of how you send the notifications or store them in the database.\n* **Sending notifications in background jobs**: This package supports using job queues to send notifications from separate processes. This may be helpful to free up the HTTP server of processing heavy notifications during the request time.\n\n## How does it work?\n\nThe VintaSend package provides a NotificationService class that allows the user to store and send notifications, scheduled or not. It relies on Dependency Injection to define how to store/retrieve, render the notification templates, and send notifications. This architecture allows us to swap each part without changing the code we actually use to send the notifications.\n\n### Scheduled Notifications\n\nVintaSend schedules notifications by creating them on the database for sending when the `sendAfter` value has passed. The sending isn't done automatically but we have a service method called `sendPendingNotifications` to send all pending notifications found in the database.\n\nYou need to call the `sendPendingNotifications` service method in a cron job or a tool for running periodic jobs.\n\n#### Keeping the content up-to-date in scheduled notifications\n\nThe VintaSend class stores every notification in a database. This helps us to audit and manage our notifications. At the same time, notifications usually have a context that's used to hydrate its template with data. If we stored the context directly on the notification records, we'd have to update it anytime the context changes. \n\n## Installation\n\n```bash\nnpm install vintasend\n# or\nyarn add vintasend\n```\n\n## Getting Started\n\nTo start using VintaSend you just need to initialize the notification service and start using it to manage your notifications.\n\n\n```typescript\nimport { VintaSendFactory } from 'vintasend';\nimport type { ContextGenerator } from 'vintasend';\n\n// context generator for Welcome notification\nclass WelcomeContextGenerator extends ContextGenerator {\n  async generate ({ userId }: { userId: number }): { firstName: string } {\n    const user = await getUserById(userId);  // example\n    return {\n      firstName: user.firstName,\n    };\n  }\n}\n\n// context map for generating the context of each notification\nexport const contextGeneratorsMap = {\n  welcome: new WelcomeContextGenerator(),\n} as const;\n\n// type config definition, so all modules use the same types\nexport type NotificationTypeConfig = {\n  ContextMap: typeof contextGeneratorsMap;\n  NotificationIdType: number;\n  UserIdType: number;\n};\n\nexport function getNotificationService() {\n  /* \n   Function to instanciate the notificationService \n   The Backend, Template Renderer, Logger, and Adapter used here are not included\n     here and should be installed and imported separately or manually defined if \n     the existing implementations don't support the specific use-case.\n  */\n  const backend = new MyNotificationBackendFactory\u003cNotificationTypeConfig\u003e().create();\n  const templateRenderer = new MyNotificationAdapterFactory\u003cNotificationTypeConfig\u003e().create();\n  const adapter = new MyNotificationAdapterFactory\u003cNotificationTypeConfig\u003e().create(\n    templateRenderer, true\n  );\n  return new VintaSendFactory\u003cNotificationTypeConfig\u003e().create(\n    [adapter],\n    backend,\n    new MyLogger(loggerOptions),\n    contextGeneratorsMap,\n  );\n}\n\nexport function sendWelcomeEmail(userId: number) {\n  /* sends the Welcome email to a user */\n  const vintasend = getNotificationService();\n  const now = new Date();\n\n  vintasend.createNotification({\n    userId: user.id,\n    notificationType: 'EMAIL',\n    title: 'Welcome Email',\n    contextName: 'welcome',\n    contextParameters: { userId },\n    sendAfter: now,\n    bodyTemplate: './src/email-templates/auth/welcome/welcome-body.html.template',\n    subjectTemplate: './src/email-templates/auth/welcome/welcome-subject.txt.template',\n    extraParams: {},\n  });\n} \n```\n\n## Multi-Backend Configuration\n\nVintaSend supports configuring multiple backends for redundancy, data distribution, and migration use cases.\n\n### Basic Setup\n\n```typescript\nimport { VintaSendFactory } from 'vintasend';\n\nconst vintasend = new VintaSendFactory\u003cNotificationTypeConfig\u003e().create({\n  adapters,\n  backend: primaryBackend,\n  additionalBackends: [replicaBackend],\n  logger,\n  contextGeneratorsMap,\n});\n```\n\n### How It Works\n\n- **Writes**: VintaSend writes to the primary backend first, then replicates to additional backends either inline (default) or through a per-backend replication queue.\n- **Reads**: Read methods use the primary backend by default, but support optional backend targeting by identifier.\n\n```typescript\n// Read from primary backend (default)\nconst notification = await vintasend.getNotification(notificationId);\n\n// Read from a specific backend\nconst notificationFromReplica = await vintasend.getNotification(\n  notificationId,\n  false,\n  'replica-backend',\n);\n```\n\n### Backend Management Operations\n\n```typescript\nconst report = await vintasend.verifyNotificationSync(notificationId);\n\nif (!report.synced) {\n  await vintasend.replicateNotification(notificationId);\n}\n\nconst backendStats = await vintasend.getBackendSyncStats();\n```\n\n### Asynchronous Replication Queue (Per Backend)\n\nVintaSend supports asynchronous replication to additional backends with one queued task per destination backend.\n\n#### Replication mode\n\nUse `replicationMode: 'queued'` to enqueue replication instead of replicating inline in the request path.\n\n```typescript\nconst vintasend = new VintaSendFactory\u003cNotificationTypeConfig\u003e().create({\n  adapters,\n  backend: primaryBackend,\n  additionalBackends: [replicaA, replicaB],\n  logger,\n  contextGeneratorsMap,\n  replicationQueueService,\n  options: {\n    raiseErrorOnFailedSend: false,\n    replicationMode: 'queued',\n  },\n});\n```\n\n#### Replication queue contract\n\nThe replication queue service receives both the notification id and the target backend identifier:\n\n```typescript\nexport interface BaseNotificationReplicationQueueService\u003cConfig extends BaseNotificationTypeConfig\u003e {\n  enqueueReplication(notificationId: Config['NotificationIdType'], backendIdentifier: string): Promise\u003cvoid\u003e;\n}\n```\n\nWhen queued replication is enabled, VintaSend enqueues one replication task for each additional backend.\n\n#### Worker processing\n\nWorkers should process replication with a backend target to apply replication only for the queued destination:\n\n```typescript\nawait vintasend.processReplication(notificationId, backendIdentifier);\n```\n\nYou can still call `processReplication(notificationId)` without a target to process all additional backends.\n\n#### Ordering and idempotency safety\n\nTo reduce out-of-order replication issues, backends can optionally implement conditional apply:\n\n```typescript\napplyReplicationSnapshotIfNewer?(snapshot): Promise\u003c{ applied: boolean }\u003e;\n```\n\n- If destination state is newer/equal, replication is skipped (`applied: false`).\n- If destination is older, snapshot is applied.\n- Duplicate-create race conditions are handled with create→update fallback in worker replication flow.\n\nOfficial backends `vintasend-prisma` and `vintasend-medplum` implement this conditional apply behavior.\n\n### Failure Handling\n\n- Primary backend failures fail the operation.\n- Additional backend replication failures are logged and do not fail the primary operation.\n- In queued mode, enqueue failures fall back to inline replication for affected backends.\n- This keeps primary workflows available while still enabling redundancy and eventual consistency.\n\n## Filtering and Ordering Notifications\n\nUse `filterNotifications` to query notifications with pagination and optional ordering.\n\n```typescript\nconst notifications = await vintasend.filterNotifications(\n  {\n    status: 'PENDING_SEND',\n  },\n  1,\n  25,\n  {\n    field: 'createdAt',\n    direction: 'desc',\n  },\n);\n```\n\n### `orderBy` shape\n\n```typescript\ntype NotificationOrderBy = {\n  field: 'sendAfter' | 'sentAt' | 'readAt' | 'createdAt' | 'updatedAt';\n  direction: 'asc' | 'desc';\n};\n```\n\nExamples:\n- `{ field: 'createdAt', direction: 'desc' }`\n- `{ field: 'sendAfter', direction: 'asc' }`\n\n### Checking backend support\n\nUse `getBackendSupportedFilterCapabilities()` to detect support gaps per backend.\n\n```typescript\nconst capabilities = await vintasend.getBackendSupportedFilterCapabilities();\n\nif (!capabilities['orderBy.readAt']) {\n  // Fallback to another ordering field\n}\n```\n\nWhen using multiple backends, you can check capabilities for a specific backend identifier:\n\n```typescript\nconst replicaCapabilities = await vintasend.getBackendSupportedFilterCapabilities('replica-backend');\n```\n\n`vintasend-medplum` currently does not support `orderBy.readAt`, and reports `orderBy.readAt: false`.\n\n## Attachment Support\n\nVintaSend supports file attachments for notifications with an extensible architecture that allows you to choose your preferred storage backend.\n\n### Key Features\n\n- ✅ **Flexible Storage** - Support for multiple backends (AWS S3, Azure Blob, Google Cloud Storage, local filesystem, etc.)\n- ✅ **Reusable Files** - Upload once, attach to multiple notifications\n- ✅ **Automatic Deduplication** - Files with identical content stored only once\n- ✅ **Streaming Support** - Efficient handling of large files\n- ✅ **Presigned URLs** - Secure, time-limited file access (backend-dependent)\n- ✅ **Custom Backends** - Extensible interface to implement any storage service\n\n### Quick Start\n\n```typescript\n// Example using S3 AttachmentManager (see available implementations below)\nimport { S3AttachmentManager } from 'vintasend-aws-s3-attachments';\n\n// Create attachment manager (configuration varies by implementation)\nconst attachmentManager = new S3AttachmentManager({\n  bucket: 'my-app-notifications',\n  region: 'us-east-1',\n  keyPrefix: 'attachments/',\n});\n\n// Create VintaSend with attachment support\nconst vintaSend = factory.create(\n  adapters,\n  backend,\n  templateRenderer,\n  contextGeneratorsMap,\n  logger,\n  attachmentManager, // Pass your chosen attachment manager\n);\n\n// Send notification with inline file upload\nawait vintaSend.sendNotification({\n  notificationTypeId: 'order-confirmation',\n  userId: '123',\n  context: { orderNumber: 'ORD-12345' },\n  attachments: [\n    {\n      file: invoiceBuffer,\n      filename: 'invoice.pdf',\n      contentType: 'application/pdf',\n    },\n  ],\n});\n\n// Send notification with pre-uploaded file reference\nawait vintaSend.sendNotification({\n  notificationTypeId: 'welcome-email',\n  userId: '456',\n  context: { userName: 'John' },\n  attachments: [\n    {\n      fileId: 'file-abc-123', // Reference to existing file\n      description: 'Company brochure',\n    },\n  ],\n});\n```\n\n### Complete Documentation\n\nFor comprehensive guides on attachment support, storage backends, security, and best practices, see [ATTACHMENTS.md](ATTACHMENTS.md).\n\nTopics covered:\n- Available storage backend implementations\n- Creating custom AttachmentManagers for any storage service\n- Security best practices and performance optimization\n- Adapter support for sending attachments\n- Usage examples and patterns\n\n## One-Off Notifications\n\nOne-off notifications allow you to send notifications directly to an email address or phone number without requiring a user account in your system. This is particularly useful for:\n\n- **Prospects**: Send welcome emails or marketing materials to potential customers\n- **Guests**: Invite external participants to events or meetings\n- **External Contacts**: Share information with partners or vendors\n- **Temporary Recipients**: Send one-time notifications without creating user accounts\n\n### Key Differences from Regular Notifications\n\n| Feature | Regular Notification | One-Off Notification |\n|---------|---------------------|----------------------|\n| **Recipient** | User ID (requires account) | Email/phone directly |\n| **User Data** | Fetched from user table | Provided inline (firstName, lastName) |\n| **Use Case** | Registered users | Prospects, guests, external contacts |\n| **Storage** | Same table with `userId` | Same table with `emailOrPhone` |\n\n### Creating One-Off Notifications\n\n```typescript\n// Send an immediate one-off notification\nconst notification = await vintaSend.createOneOffNotification({\n  emailOrPhone: 'prospect@example.com',\n  firstName: 'John',\n  lastName: 'Doe',\n  notificationType: 'EMAIL',\n  title: 'Welcome!',\n  bodyTemplate: './templates/welcome.html',\n  subjectTemplate: 'Welcome to {{companyName}}!',\n  contextName: 'welcomeContext',\n  contextParameters: { companyName: 'Acme Corp' },\n  sendAfter: null, // Send immediately\n  extraParams: null,\n});\n```\n\n#### Using with Phone Numbers (SMS)\n\n```typescript\n// Send SMS to a phone number (requires SMS adapter)\nconst smsNotification = await vintaSend.createOneOffNotification({\n  emailOrPhone: '+15551234567', // E.164 format recommended\n  firstName: 'John',\n  lastName: 'Doe',\n  notificationType: 'SMS',\n  title: 'Welcome SMS',\n  bodyTemplate: './templates/welcome-sms.txt',\n  subjectTemplate: null, // SMS doesn't use subjects\n  contextName: 'welcomeContext',\n  contextParameters: { companyName: 'Acme Corp' },\n  sendAfter: null,\n  extraParams: null,\n});\n```\n\n### Database Schema Considerations\n\nOne-off notifications are stored in the same table as regular notifications using a unified approach:\n\n- **Regular notifications**: Have `userId` set, `emailOrPhone` is null\n- **One-off notifications**: Have `emailOrPhone` set, `userId` is null\n\n## Git Commit SHA Tracking\n\nVintaSend can persist which source-code version executed each notification send/render flow.\n\n### Field behavior\n\n- Persisted notifications include `gitCommitSha: string | null`\n- Notification input/resend payloads do not accept this field (`gitCommitSha` is system-managed)\n- The SHA is resolved at execution time (send/render), not creation time\n\n### Configuring a provider\n\nUse `BaseGitCommitShaProvider` with `VintaSendFactory.create(...)`.\n\nPreferred factory style (object parameter):\n\n```typescript\nimport { VintaSendFactory, type BaseGitCommitShaProvider } from 'vintasend';\n\nconst gitCommitShaProvider: BaseGitCommitShaProvider = {\n  getCurrentGitCommitSha: () =\u003e process.env.GIT_COMMIT_SHA ?? null,\n};\n\nconst vintaSend = new VintaSendFactory\u003cNotificationTypeConfig\u003e().create({\n  adapters: [adapter],\n  backend,\n  logger,\n  contextGeneratorsMap,\n  gitCommitShaProvider,\n});\n```\n\n\u003e Positional `create(...)` parameters are still supported for backward compatibility, but object-style configuration is recommended.\n\n### Provider examples\n\nEnvironment variable (CI/CD injected):\n\n```typescript\nconst envProvider: BaseGitCommitShaProvider = {\n  getCurrentGitCommitSha: () =\u003e process.env.GIT_COMMIT_SHA ?? null,\n};\n```\n\nShell command (async):\n\n```typescript\nimport { execSync } from 'node:child_process';\n\nconst shellProvider: BaseGitCommitShaProvider = {\n  getCurrentGitCommitSha: async () =\u003e {\n    try {\n      return execSync('git rev-parse HEAD', { encoding: 'utf-8' }).trim() || null;\n    } catch {\n      return null;\n    }\n  },\n};\n```\n\nStatic provider (for deterministic environments):\n\n```typescript\nconst staticProvider: BaseGitCommitShaProvider = {\n  getCurrentGitCommitSha: () =\u003e '0123456789abcdef0123456789abcdef01234567',\n};\n```\n\n### Migration Guides\n\n#### Migrating to v0.7.0 (Git Commit SHA Tracking)\n\nThis migration is only needed if you're using the Prisma backend.\n\n`gitCommitSha` is now persisted on notifications (regular and one-off) as a nullable, system-managed field.\n\n1. Update your Prisma schema (`Notification` model):\n  - Add `gitCommitSha String?`\n  - Add `@@index([gitCommitSha])`\n2. Run a migration in your app:\n  ```bash\n  prisma migrate dev --name add-notification-git-commit-sha\n  ```\n3. Optionally configure a `BaseGitCommitShaProvider` in `VintaSendFactory`.\n4. Do not add `gitCommitSha` to notification create/resend input payloads (it is provider-managed).\n\n#### Migrating to v0.4.0 (Attachment Support)\n\nVersion 0.4.0 introduces file attachment support with a **breaking change** to the `VintaSendFactory.create()` method signature.\n\n**Breaking Change**: The `attachmentManager` parameter now comes **before** the `options` parameter.\n\n- **Old signature (v0.3.x)**: `create(adapters, backend, logger, contextGeneratorsMap, queueService?, options?)`\n- **New signature (v0.4.0)**: `create(adapters, backend, logger, contextGeneratorsMap, queueService?, attachmentManager?, options?)`\n\n**Migration Steps**:\n\n1. **If you're NOT passing `options`** - No changes needed:\n   ```typescript\n   // This still works in v0.4.0\n   factory.create(adapters, backend, logger, contextGeneratorsMap);\n   factory.create(adapters, backend, logger, contextGeneratorsMap, queueService);\n   ```\n\n2. **If you ARE passing `options` as the 6th argument** - Add `undefined` for `attachmentManager`:\n   ```typescript\n   // Before (v0.3.x) - THIS WILL BREAK\n   factory.create(adapters, backend, logger, contextGeneratorsMap, queueService, { \n     raiseErrorOnFailedSend: true \n   });\n   \n   // After (v0.4.0) - CORRECT\n   factory.create(adapters, backend, logger, contextGeneratorsMap, queueService, undefined, { \n     raiseErrorOnFailedSend: true \n   });\n   ```\n\n3. **If you want to use attachments** - Pass the `attachmentManager`:\n   ```typescript\n   import { LocalFileAttachmentManager } from 'vintasend';\n   \n   const attachmentManager = new LocalFileAttachmentManager({ uploadDir: './uploads' });\n   \n   factory.create(adapters, backend, logger, contextGeneratorsMap, queueService, attachmentManager, {\n     raiseErrorOnFailedSend: true\n   });\n   ```\n\n4. **For Prisma users**: Add attachment models to your schema (optional, only if you want attachment support):\n   ```bash\n   # Add AttachmentFile and NotificationAttachment models to schema.prisma\n   # See ATTACHMENTS.md for the complete schema\n   prisma migrate dev --name add-attachment-support\n   ```\n\n**Note**: Attachment methods in `BaseNotificationBackend` are now **optional**. Existing backend implementations continue to work without changes. See [ATTACHMENTS.md](ATTACHMENTS.md) for full documentation.\n\n#### Migrating to v0.3.0 (One-off Notifications)\n\nIf you're adding one-off notification support to an existing installation:\n\n1. **Update your Prisma schema** to make `userId` optional and add one-off fields:\n   ```bash\n   # Add the new fields to your schema.prisma\n   # Then run:\n   prisma migrate dev --name add-one-off-notification-support\n   ```\n\n2. **No code changes required** for existing functionality - all existing notifications continue to work as before.\n\n3. **Existing notifications are preserved** - they have `userId` set and `emailOrPhone` as null.\n\n## Glossary\n\n* **Notification Backend**: It is a class that implements the methods necessary for VintaSend services to create, update, and retrieve Notifications from the database.\n* **Notification Adapter**: It is a class that implements the methods necessary for VintaSend services to send Notifications through email, SMS or even push/in-app notifications.\n* **Template Renderer**: It is a class that implements the methods necessary for VintaSend adapter to render the notification body.\n* **Notification Context**: It's the data passed to the templates to render the notification correctly. It's generated when the notification is sent, not on creation time\n* **Context generator**: It's a class defined by the user context generator map with a context name. That class has a `generate` method that, when called, generates the data necessary to render its respective notification.\n* **Context name**: The registered name of a context generator. It's stored in the notification so the context generator is called at the moment the notification will be sent.\n* **Context generators map**: It's an object defined by the user that maps context names to their respective context generators.\n* **Queue service**: Service for enqueueing notifications so they are sent by an external service.\n* **Logger**: A class that allows the `NotificationService` to create logs following a format defined by its users.\n* **One-off Notification**: A notification sent directly to an email address or phone number without requiring a user account. Used for prospects, guests, or external contacts.\n* **Regular Notification**: A notification associated with a user account (via userId). Used for registered users in your system.\n* **AttachmentManager**: A class that handles file storage operations (upload, download, delete) for notification attachments. Supports S3, Azure, GCS, and custom storage backends.\n* **Attachment**: A file attached to a notification, either uploaded inline or referenced from previously uploaded files. Supports automatic deduplication and reuse across multiple notifications.  \n\n\n## Implementations\n\n### Community\n\nVintaSend has many backend, adapter, and template renderer implementations. If you can't find something that fulfills your needs, the package has very clear interfaces you can implement and achieve the exact behavior you expect without loosing VintaSend's friendly API.\n\n#### Officially supported packages \n\n##### Backends\n\n* **[vintasend-prisma](https://github.com/vintasoftware/vintasend-ts-prisma/)**: Uses Prisma Client to manage the notifications in the database.\n* **[vintasend-medplum](https://github.com/vintasoftware/vintasend-medplum/)**: Uses Medplum FHIR resources (Communication, Binary, Media) to manage notifications in healthcare applications.\n\n##### Adapters\n\n* **[vintasend-nodemailer](https://github.com/vintasoftware/vintasend-nodemailer/)**: Uses nodemailer to send transactional emails to users.\n* **[vintasend-sendgrid](https://github.com/vintasoftware/vintasend-ts-sendgrid/)**: Uses SendGrid's API to send transactional emails with attachment support.\n* **[vintasend-medplum](https://github.com/vintasoftware/vintasend-medplum/)**: Uses Medplum's email API to send notifications in healthcare applications.\n* **[vintasend-mailgun](https://github.com/vintasoftware/vintasend-ts-mailgun/)**: Uses Mailgun's API to send transactional emails with attachment support.\n* **[vintasend-twilio](https://github.com/vintasoftware/vintasend-ts-twilio/)**: Uses Twilio's API to send transactional SMS.\n\n##### Attachment Managers\n\n* **[vintasend-aws-s3-attachments](https://github.com/vintasoftware/vintasend-aws-s3-attachments/)**: AWS S3 storage backend with presigned URLs and streaming support. Also works with S3-compatible services (MinIO, DigitalOcean Spaces, Cloudflare R2, etc.).\n* **[vintasend-medplum](https://github.com/vintasoftware/vintasend-medplum/)**: FHIR-compliant file storage using Binary and Media resources for healthcare applications.\n\n##### Template Renderers\n* **[vintasend-pug](https://github.com/vintasoftware/vintasend-pug/)**: Renders emails using Pug.\n* **[vintasend-react-email](https://github.com/vintasoftware/vintasend-react-email/)**: Renders emails using React Email, including uncompiled TS/TSX template support.\n\n##### Loggers\n* **[vintasend-winston](https://github.com/vintasoftware/vintasend-winston/)**: Uses Winston to allow `NotificationService` to create log entries.\n* **[vintasend-medplum](https://github.com/vintasoftware/vintasend-medplum/)**: Simple console-based logger for Medplum applications.\n\n## Examples\n\nExamples of how to use VintaSend in different context are available on the [vintasend-ts-examples repository](https://github.com/vintasoftware/vintasend-ts-examples).\n\n## Development\n\n1. Clone the repository\n2. Install dependencies:\n```bash\nnpm install\n# or\nyarn\n```\n3. Run tests:\n```bash\nnpm test\n# or\nyarn test\n```\n\n## Contributing\n\nFeel free to open issues and submit pull requests.\n\n### Release Process\n\nThis project uses a two-step automated release process:\n\n1. **Bump versions**: `npm run release:bump`\n2. **Update CHANGELOG.md**: Edit manually\n3. **Publish**: `npm run release:publish`\n\nOptional publish skips:\n- `npm run release:publish -- --skip=vintasend-medplum,vintasend-ts-twilio`\n- `npm run release:publish -- --skip-root`\n- `npm run release:publish -- --skip=root,vintasend-medplum`\n\nFor detailed instructions, see:\n- [Release Guide](RELEASE_GUIDE.md) - Complete walkthrough with examples\n- [Quick Reference](RELEASE_QUICK_REF.md) - Command cheatsheet\n- [Technical Docs](scripts/README.md) - Script architecture details\n\n### Creating new implementations\n\nThere's a template project for creating new implementations that already includes basic dependencies, configuration for tests, and Github Actions hooks. You can find it in [vintasend-ts/src/implementations/vintasend-implementation-template](https://github.com/vintasoftware/vintasend-ts/tree/main/src/implementations/vintasend-implementation-template)\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## Commercial Support\n\n[![alt text](https://avatars2.githubusercontent.com/u/5529080?s=80\u0026v=4 'Vinta Logo')](https://www.vinta.com.br/)\n\nThis project is maintained by [Vinta Software](https://www.vinta.com.br/) and is used in products of Vinta's clients. We are always looking for exciting work! If you need any commercial support, feel free to get in touch: contact@vinta.com.br\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvintasoftware%2Fvintasend-ts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvintasoftware%2Fvintasend-ts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvintasoftware%2Fvintasend-ts/lists"}