{"_id":"@arthurfiddich/notification-service","_rev":"3-c262f52d517b0cb10af3c7254df6445d","name":"@arthurfiddich/notification-service","dist-tags":{"latest":"1.1.1"},"versions":{"1.0.0":{"name":"@arthurfiddich/notification-service","version":"1.0.0","keywords":["notification","push","firebase","in-app","email","sms","whatsapp"],"author":{"name":"arthurfiddich"},"license":"MIT","_id":"@arthurfiddich/notification-service@1.0.0","maintainers":[{"name":"arthurfiddich","email":"arthurfiddich@gmail.com"}],"dist":{"shasum":"10db092bc926692d91ab11d432ba943b97e79d44","tarball":"https://registry.npmjs.org/@arthurfiddich/notification-service/-/notification-service-1.0.0.tgz","fileCount":74,"integrity":"sha512-GVu1qcby8L6f2kSwQGHkfyctVAA1oqr5EwsQxdaIu+oiQSpI127XezVK+hKIUGdaJqpp8KXydQtWtPjznoUV4Q==","signatures":[{"sig":"MEUCIHQHaCkhQZ/KLtRP/IYlVcDCQy3GPrlMrsO24N435awEAiEA2AajZrU4eGUoHJkt+NCFNQq5DEYM8VI8wUnvGOkSLe8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":107522},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"dev":"tsc --watch","lint":"eslint src/","test":"vitest run","build":"tsc","clean":"rm -rf dist","test:watch":"vitest","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"arthurfiddich","email":"arthurfiddich@gmail.com"},"repository":{"url":"","type":"git"},"_npmVersion":"10.9.3","description":"A pluggable, channel-agnostic notification service with support for in-app, push, email, SMS, and WhatsApp notifications","directories":{},"_nodeVersion":"22.19.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.1.0","typescript":"^5.7.0","@types/node":"^22.0.0","firebase-admin":"^13.7.0"},"peerDependencies":{"firebase-admin":">=12.0.0"},"peerDependenciesMeta":{"firebase-admin":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/notification-service_1.0.0_1776147532342_0.3561069609052483","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@arthurfiddich/notification-service","version":"1.1.0","keywords":["notification","push","firebase","in-app","email","sms","whatsapp"],"author":{"name":"arthurfiddich"},"license":"MIT","_id":"@arthurfiddich/notification-service@1.1.0","maintainers":[{"name":"arthurfiddich","email":"arthurfiddich@gmail.com"}],"dist":{"shasum":"b814137f01be98384e1cfdfc1719e6c7a877c827","tarball":"https://registry.npmjs.org/@arthurfiddich/notification-service/-/notification-service-1.1.0.tgz","fileCount":74,"integrity":"sha512-d7mVM2p2Jy7mYtfyVCt2odWnYs92Z4aVI8/9Lk0TQ30oPRqi2XSvy3OK8dseWEbACbQubh63+FZNgWpZF11Jhw==","signatures":[{"sig":"MEUCIQD9elBpaAJjcSLhb3XOn1gLRj2+qni7jK62bKeCPdxQMAIgHpVefRZaYez/VRnAw2XPGDsQaZiNdSqoBUVgx+mfU7o=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":108319},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"dev":"tsc --watch","lint":"eslint src/","test":"vitest run","build":"tsc","clean":"rm -rf dist","test:watch":"vitest","prepublishOnly":"npm run clean && npm run build"},"_npmUser":{"name":"arthurfiddich","email":"arthurfiddich@gmail.com"},"repository":{"url":"","type":"git"},"_npmVersion":"10.9.3","description":"A pluggable, channel-agnostic notification service with support for in-app, push, email, SMS, and WhatsApp notifications","directories":{},"_nodeVersion":"22.19.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.1.0","typescript":"^5.7.0","@types/node":"^22.0.0","firebase-admin":"^13.7.0"},"peerDependencies":{"firebase-admin":">=12.0.0"},"peerDependenciesMeta":{"firebase-admin":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/notification-service_1.1.0_1776159922975_0.33631218240195504","host":"s3://npm-registry-packages-npm-production"}},"1.1.1":{"name":"@arthurfiddich/notification-service","version":"1.1.1","description":"A pluggable, channel-agnostic notification service with support for in-app, push, email, SMS, and WhatsApp notifications","main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"build":"tsc","dev":"tsc --watch","clean":"rm -rf dist","test":"vitest run","test:watch":"vitest","lint":"eslint src/","prepublishOnly":"npm run clean && npm run build"},"keywords":["notification","push","firebase","in-app","email","sms","whatsapp"],"author":{"name":"arthurfiddich"},"license":"MIT","publishConfig":{"access":"public"},"repository":{"type":"git","url":""},"peerDependencies":{"firebase-admin":">=12.0.0"},"peerDependenciesMeta":{"firebase-admin":{"optional":true}},"devDependencies":{"@types/node":"^22.0.0","typescript":"^5.7.0","vitest":"^3.1.0","firebase-admin":"^13.7.0"},"_id":"@arthurfiddich/notification-service@1.1.1","_nodeVersion":"22.19.0","_npmVersion":"10.9.3","dist":{"integrity":"sha512-0Eu9WcK0PTsEekaOVnbSPT6sl0cGZowOeR9an+zCt03RpraEuLRn3MDg5udzTmlxPZU+hhpkkGIwAwfJJJNwwA==","shasum":"97122851c62f2c48550f55b0f07f801bdf464207","tarball":"https://registry.npmjs.org/@arthurfiddich/notification-service/-/notification-service-1.1.1.tgz","fileCount":74,"unpackedSize":108472,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDyqJUAZlYsolo2d1o1PXuWXqQj7yD++dliRwlu1qcxiAIhAOnPaCg3k51t9zJRt/zVlzDVQvq7cr0hBKKaW3Zd9VjB"}]},"_npmUser":{"name":"arthurfiddich","email":"arthurfiddich@gmail.com"},"directories":{},"maintainers":[{"name":"arthurfiddich","email":"arthurfiddich@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/notification-service_1.1.1_1776160873149_0.870910502573323"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-14T06:18:52.237Z","modified":"2026-04-14T10:01:13.452Z","1.0.0":"2026-04-14T06:18:52.494Z","1.1.0":"2026-04-14T09:45:23.173Z","1.1.1":"2026-04-14T10:01:13.345Z"},"author":{"name":"arthurfiddich"},"license":"MIT","keywords":["notification","push","firebase","in-app","email","sms","whatsapp"],"repository":{"type":"git","url":""},"description":"A pluggable, channel-agnostic notification service with support for in-app, push, email, SMS, and WhatsApp notifications","maintainers":[{"name":"arthurfiddich","email":"arthurfiddich@gmail.com"}],"readme":"# @natupicks/notification-service\n\nA pluggable, channel-agnostic notification service for Node.js applications. Supports in-app notifications, Firebase push notifications, and is designed for easy extension to email, SMS, and WhatsApp.\n\n## Features\n\n- **Multi-channel delivery** — Send to in-app, push, email, SMS, and WhatsApp from a single API\n- **Provider-based architecture** — Register/swap providers at runtime\n- **Template engine** — `{{variable}}` interpolation with built-in e-commerce templates\n- **Pluggable persistence** — Bring your own store (MongoDB, Postgres, etc.)\n- **User preference checking** — Respect opt-in/opt-out before sending\n- **Retry with exponential backoff** — Automatic retries for failed deliveries\n- **Idempotency** — Prevent duplicate notifications\n- **Batch sending** — Send to multiple recipients efficiently\n- **Priority levels** — Low, Normal, High, Urgent\n- **Zero required dependencies** — `firebase-admin` is an optional peer dependency\n\n## Installation\n\n```bash\nnpm install @natupicks/notification-service\n\n# If using Firebase push notifications:\nnpm install firebase-admin\n```\n\nOr use as a local dependency:\n\n```json\n{\n  \"dependencies\": {\n    \"@natupicks/notification-service\": \"file:../notification-service\"\n  }\n}\n```\n\n## Quick Start\n\n```typescript\nimport {\n  NotificationService,\n  InAppProvider,\n  FirebasePushProvider,\n  MemoryStore,\n  NotificationChannel,\n} from \"@natupicks/notification-service\";\n\n// 1. Create a store (use MemoryStore for dev, implement NotificationStore for production)\nconst store = new MemoryStore();\n\n// 2. Initialize providers\nconst inAppProvider = new InAppProvider();\nawait inAppProvider.initialize({ store });\n\nconst pushProvider = new FirebasePushProvider();\nawait pushProvider.initialize({\n  messagingInstance: firebaseAdmin.messaging(), // your firebase-admin instance\n  recipientResolver: {\n    resolvePushTokens: async (userId) => {\n      const user = await User.findById(userId).select(\"fcmTokens\");\n      return user?.fcmTokens ?? [];\n    },\n    onInvalidPushTokens: async (userId, tokens) => {\n      await User.findByIdAndUpdate(userId, {\n        $pull: { fcmTokens: { $in: tokens } },\n      });\n    },\n  },\n});\n\n// 3. Create the service\nconst notificationService = new NotificationService({\n  providers: {\n    [NotificationChannel.IN_APP]: inAppProvider,\n    [NotificationChannel.PUSH]: pushProvider,\n  },\n  store,\n});\n\n// 4. Send notifications\nawait notificationService.send({\n  recipientId: userId,\n  channels: [NotificationChannel.IN_APP, NotificationChannel.PUSH],\n  templateName: \"order.confirmed\",\n  templateVars: { orderNumber: \"ORD-12345\" },\n});\n```\n\n## Architecture\n\n```\n┌─────────────────────────────────────────────────────┐\n│                NotificationService                  │\n│  (orchestrator, templates, retry, preferences)      │\n├──────────┬──────────┬──────────┬─────────┬──────────┤\n│  In-App  │  Push    │  Email   │  SMS    │ WhatsApp │\n│ Provider │ Provider │ Provider │Provider │ Provider │\n├──────────┴──────────┴──────────┴─────────┴──────────┤\n│              NotificationStore (pluggable)           │\n│         Memory │ MongoDB │ Postgres │ etc.          │\n└─────────────────────────────────────────────────────┘\n```\n\n## Providers\n\n### InAppProvider\n\nStores notifications in the configured `NotificationStore` for in-app display.\n\n```typescript\nconst provider = new InAppProvider();\nawait provider.initialize({ store: myStore });\n```\n\n### FirebasePushProvider\n\nSends push notifications via Firebase Cloud Messaging.\n\n```typescript\nconst provider = new FirebasePushProvider();\nawait provider.initialize({\n  messagingInstance: firebaseAdmin.messaging(),\n  recipientResolver: myResolver,\n});\n```\n\n### Custom Provider\n\nImplement the `NotificationProvider` interface or extend `BaseProvider`:\n\n```typescript\nimport { BaseProvider, NotificationChannel, DeliveryStatus } from \"@natupicks/notification-service\";\n\nclass EmailProvider extends BaseProvider {\n  readonly name = \"email\";\n\n  async send(recipientId, payload, priority, metadata) {\n    // Your email sending logic (nodemailer, SES, SendGrid, etc.)\n    const email = await this.resolveEmail(recipientId);\n    await sendEmail(email, payload.title, payload.body);\n\n    return this.result(recipientId, NotificationChannel.EMAIL, DeliveryStatus.SENT);\n  }\n}\n```\n\n## Templates\n\nBuilt-in templates for common scenarios:\n\n| Template | Variables |\n|----------|-----------|\n| `order.confirmed` | `orderNumber` |\n| `order.shipped` | `orderNumber`, `trackingNumber` |\n| `order.delivered` | `orderNumber` |\n| `order.cancelled` | `orderNumber` |\n| `order.refunded` | `orderNumber`, `amount` |\n| `promo.discount` | `discount`, `code` |\n| `account.welcome` | `appName`, `name` |\n| `cart.abandoned` | `itemCount` |\n| `wishlist.price_drop` | `productName`, `newPrice`, `oldPrice` |\n\nRegister custom templates:\n\n```typescript\nservice.registerTemplates({\n  name: \"custom.alert\",\n  title: \"Alert: {{type}}\",\n  body: \"{{message}}\",\n});\n```\n\n## Notification Store\n\nImplement `NotificationStore` for your database:\n\n```typescript\nimport type { NotificationStore } from \"@natupicks/notification-service\";\n\nclass MongoNotificationStore implements NotificationStore {\n  async save(record) { /* MongoDB insert */ }\n  async findById(id) { /* MongoDB findOne */ }\n  async findByRecipient(recipientId, options) { /* MongoDB find with pagination */ }\n  async getUnreadCount(recipientId) { /* MongoDB countDocuments */ }\n  async markAsRead(id) { /* MongoDB updateOne */ }\n  async markAllAsRead(recipientId) { /* MongoDB updateMany */ }\n  async delete(id) { /* MongoDB deleteOne */ }\n  async deleteAllForRecipient(recipientId) { /* MongoDB deleteMany */ }\n  async updateStatus(id, status) { /* MongoDB updateOne */ }\n  async findByIdempotencyKey(key) { /* MongoDB findOne */ }\n}\n```\n\n## User Preferences\n\nRespect user notification settings:\n\n```typescript\nconst service = new NotificationService({\n  preferenceChecker: {\n    isAllowed: async (recipientId, channel, category) => {\n      const settings = await NotificationSetting.findOne({ user: recipientId });\n      if (!settings) return true;\n      const channelSettings = settings[channel];\n      if (!channelSettings?.enabled) return false;\n      if (category && channelSettings[category] !== undefined) {\n        return channelSettings[category];\n      }\n      return true;\n    },\n  },\n});\n```\n\n## In-App Notification Management\n\n```typescript\n// Get notifications for a user\nconst { data, total, hasMore } = await service.getNotifications(userId, {\n  page: 1,\n  limit: 20,\n  unreadOnly: true,\n});\n\n// Get unread count\nconst count = await service.getUnreadCount(userId);\n\n// Mark as read\nawait service.markAsRead(notificationId);\nawait service.markAllAsRead(userId);\n\n// Delete\nawait service.deleteNotification(notificationId);\nawait service.deleteAllNotifications(userId);\n```\n\n## Configuration\n\n```typescript\nconst service = new NotificationService({\n  providers: { /* channel -> provider mapping */ },\n  store: myStore,\n  preferenceChecker: myChecker,\n  retry: {\n    maxAttempts: 3,     // default: 3\n    baseDelay: 1000,    // default: 1000ms\n    maxDelay: 30000,    // default: 30000ms\n  },\n  logger: myLogger,     // must implement Logger interface\n});\n```\n\n## Roadmap\n\n- [ ] Email provider (Nodemailer / SES / SendGrid)\n- [ ] SMS provider (Twilio / SNS)\n- [ ] WhatsApp provider (WhatsApp Business API)\n- [ ] Scheduling support (delayed notifications)\n- [ ] Notification grouping / digest\n- [ ] Webhook delivery channel\n- [ ] Rate limiting per user/channel\n- [ ] Analytics and delivery tracking dashboard\n\n## License\n\nMIT\n","readmeFilename":"README.md"}