@zan-shop/push-notifications 1.0.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/LICENSE +21 -0
- package/README.md +255 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/models/device-token.d.ts +30 -0
- package/dist/models/device-token.d.ts.map +1 -0
- package/dist/models/device-token.js +7 -0
- package/dist/models/device-token.js.map +1 -0
- package/dist/services/device-service.d.ts +51 -0
- package/dist/services/device-service.d.ts.map +1 -0
- package/dist/services/device-service.js +14 -0
- package/dist/services/device-service.js.map +1 -0
- package/dist/services/fcm-service.d.ts +48 -0
- package/dist/services/fcm-service.d.ts.map +1 -0
- package/dist/services/fcm-service.js +208 -0
- package/dist/services/fcm-service.js.map +1 -0
- package/dist/services/index.d.ts +8 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +12 -0
- package/dist/services/index.js.map +1 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +22 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/notification.d.ts +48 -0
- package/dist/types/notification.d.ts.map +1 -0
- package/dist/types/notification.js +7 -0
- package/dist/types/notification.js.map +1 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +23 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/notification-formatter.d.ts +18 -0
- package/dist/utils/notification-formatter.d.ts.map +1 -0
- package/dist/utils/notification-formatter.js +92 -0
- package/dist/utils/notification-formatter.js.map +1 -0
- package/dist/utils/validators.d.ts +18 -0
- package/dist/utils/validators.d.ts.map +1 -0
- package/dist/utils/validators.js +43 -0
- package/dist/utils/validators.js.map +1 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ZAN
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# @zan-shop/push-notifications
|
|
2
|
+
|
|
3
|
+
Push notification module for Medusa.js using Firebase Cloud Messaging (FCM). Provides type-safe utilities for sending mobile push notifications with built-in formatters for common e-commerce events.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ๐ฅ Firebase Cloud Messaging integration
|
|
8
|
+
- ๐ฑ iOS & Android support
|
|
9
|
+
- ๐ฆ Pre-built formatters (orders, shipments, returns, payouts)
|
|
10
|
+
- ๐ Batch sending (up to 500 devices)
|
|
11
|
+
- โ
Full TypeScript support
|
|
12
|
+
- ๐งช Dry-run mode for testing
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @zan-shop/push-notifications
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Prerequisites:**
|
|
21
|
+
- Firebase project with Cloud Messaging enabled
|
|
22
|
+
- Firebase Admin SDK already initialized in your project
|
|
23
|
+
- Node.js >= 18.0.0
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
### 1. Create FCM Service
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { FCMService } from '@zan-shop/push-notifications';
|
|
31
|
+
|
|
32
|
+
const fcmService = new FCMService({
|
|
33
|
+
firebaseApp,
|
|
34
|
+
dryRun: false, // Set to true for testing
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 2. Send Notifications
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { formatShipmentNotification } from '@zan-shop/push-notifications';
|
|
42
|
+
|
|
43
|
+
const notification = formatShipmentNotification({
|
|
44
|
+
type: 'shipment.created',
|
|
45
|
+
orderId: 'order_123',
|
|
46
|
+
trackingNumber: 'TRACK123',
|
|
47
|
+
carrier: 'FedEx',
|
|
48
|
+
estimatedDelivery: '2024-01-15',
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const result = await fcmService.sendToDevice(deviceToken, notification);
|
|
52
|
+
|
|
53
|
+
if (result.success) {
|
|
54
|
+
console.log('Notification sent:', result.messageId);
|
|
55
|
+
} else {
|
|
56
|
+
console.error('Failed to send:', result.error);
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Event Formatters
|
|
61
|
+
|
|
62
|
+
Pre-built formatters for common e-commerce events:
|
|
63
|
+
|
|
64
|
+
### Shipment Notifications
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { formatShipmentNotification } from '@zan-shop/push-notifications';
|
|
68
|
+
|
|
69
|
+
const notification = formatShipmentNotification({
|
|
70
|
+
type: 'shipment.created',
|
|
71
|
+
orderId: 'order_123',
|
|
72
|
+
trackingNumber: 'TRACK123',
|
|
73
|
+
carrier: 'FedEx',
|
|
74
|
+
});
|
|
75
|
+
// Output: "๐ฆ Order Shipped - Your order #order_123 has been shipped! Track your package..."
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Order Notifications
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { formatOrderNotification } from '@zan-shop/push-notifications';
|
|
82
|
+
|
|
83
|
+
const notification = formatOrderNotification({
|
|
84
|
+
type: 'order.placed',
|
|
85
|
+
orderId: 'order_123',
|
|
86
|
+
orderNumber: 'ORD-2024-001',
|
|
87
|
+
});
|
|
88
|
+
// Output: "โ
Order Confirmed - Your order ORD-2024-001 has been confirmed!"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Return Notifications
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import { formatReturnNotification } from '@zan-shop/push-notifications';
|
|
95
|
+
|
|
96
|
+
const notification = formatReturnNotification({
|
|
97
|
+
type: 'return.approved',
|
|
98
|
+
returnId: 'return_123',
|
|
99
|
+
orderId: 'order_123',
|
|
100
|
+
});
|
|
101
|
+
// Output: "โ
Return Approved - Your return request for order #order_123 has been approved!"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Custom Notifications
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import type { NotificationPayload } from '@zan-shop/push-notifications';
|
|
108
|
+
|
|
109
|
+
const customNotification: NotificationPayload = {
|
|
110
|
+
title: '๐ Special Offer',
|
|
111
|
+
body: 'Get 20% off your next purchase!',
|
|
112
|
+
data: {
|
|
113
|
+
type: 'promotion',
|
|
114
|
+
promoCode: 'SAVE20',
|
|
115
|
+
},
|
|
116
|
+
imageUrl: 'https://example.com/promo-banner.jpg',
|
|
117
|
+
clickAction: '/promotions/save20',
|
|
118
|
+
priority: 'high',
|
|
119
|
+
sound: 'default',
|
|
120
|
+
badge: 1,
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
await fcmService.sendToDevice(deviceToken, customNotification);
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Batch Sending
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
const tokens = ['token1', 'token2', 'token3'];
|
|
130
|
+
const result = await fcmService.sendToMultipleDevices(tokens, notification);
|
|
131
|
+
|
|
132
|
+
console.log(`Success: ${result.successCount}, Failed: ${result.failureCount}`);
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Medusa Integration
|
|
136
|
+
|
|
137
|
+
Example subscriber for shipment notifications:
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import type { SubscriberArgs } from '@medusajs/framework';
|
|
141
|
+
import { FCMService, formatShipmentNotification } from '@zan-shop/push-notifications';
|
|
142
|
+
|
|
143
|
+
export default async function handleShipmentCreated({
|
|
144
|
+
event,
|
|
145
|
+
container,
|
|
146
|
+
}: SubscriberArgs<{ id: string }>) {
|
|
147
|
+
const fcmService = container.resolve<FCMService>('fcmService');
|
|
148
|
+
const deviceService = container.resolve('deviceService');
|
|
149
|
+
|
|
150
|
+
const shipment = await fetchShipmentDetails(event.data.id);
|
|
151
|
+
|
|
152
|
+
// Get customer's devices
|
|
153
|
+
const devices = await deviceService.getCustomerDevices(shipment.order.customer_id);
|
|
154
|
+
|
|
155
|
+
if (devices.length === 0) return;
|
|
156
|
+
|
|
157
|
+
// Format notification
|
|
158
|
+
const notification = formatShipmentNotification({
|
|
159
|
+
type: 'shipment.created',
|
|
160
|
+
orderId: shipment.order.id,
|
|
161
|
+
trackingNumber: shipment.tracking_number,
|
|
162
|
+
carrier: shipment.carrier,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Send to all customer devices
|
|
166
|
+
const tokens = devices.map(d => d.token);
|
|
167
|
+
const result = await fcmService.sendToMultipleDevices(tokens, notification);
|
|
168
|
+
|
|
169
|
+
console.log(`Sent: ${result.successCount} succeeded, ${result.failureCount} failed`);
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## API Reference
|
|
174
|
+
|
|
175
|
+
### FCMService
|
|
176
|
+
|
|
177
|
+
- `sendToDevice(token, payload)` - Send to single device
|
|
178
|
+
- `sendToMultipleDevices(tokens, payload)` - Batch send (auto-splits into batches of 500)
|
|
179
|
+
- `sendToCustomer(customerId, payload)` - Send to all customer devices
|
|
180
|
+
- `sendToTopic(topic, payload)` - Broadcast to topic subscribers
|
|
181
|
+
- `validateToken(token)` - Check if token is valid
|
|
182
|
+
|
|
183
|
+
### Formatters
|
|
184
|
+
|
|
185
|
+
- `formatShipmentNotification(data)` - Shipment events
|
|
186
|
+
- `formatOrderNotification(data)` - Order events
|
|
187
|
+
- `formatReturnNotification(data)` - Return events
|
|
188
|
+
- `formatPayoutNotification(data)` - Payout events
|
|
189
|
+
|
|
190
|
+
### Types
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
import type {
|
|
194
|
+
NotificationPayload,
|
|
195
|
+
SendResult,
|
|
196
|
+
BatchResult,
|
|
197
|
+
DeviceToken,
|
|
198
|
+
DeviceRegistration,
|
|
199
|
+
} from '@zan-shop/push-notifications';
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Device Management
|
|
203
|
+
|
|
204
|
+
The package provides an abstract `DeviceService` class. Extend it with your database implementation:
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
import { DeviceService } from '@zan-shop/push-notifications';
|
|
208
|
+
|
|
209
|
+
class MyDeviceService extends DeviceService {
|
|
210
|
+
async registerDevice(customerId, deviceData) { /* ... */ }
|
|
211
|
+
async getCustomerDevices(customerId) { /* ... */ }
|
|
212
|
+
async deactivateDevice(token) { /* ... */ }
|
|
213
|
+
// ... other methods
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Testing
|
|
218
|
+
|
|
219
|
+
Enable dry-run mode for testing without sending actual notifications:
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
const fcmService = new FCMService({ firebaseApp, dryRun: true });
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Versioning
|
|
226
|
+
|
|
227
|
+
This package uses [semantic-release](https://github.com/semantic-release/semantic-release) for automated versioning. Use conventional commits:
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
# For features (bumps minor version 1.0.0 -> 1.1.0)
|
|
231
|
+
git commit -m "feat: add notification scheduling"
|
|
232
|
+
|
|
233
|
+
# For fixes (bumps patch version 1.0.0 -> 1.0.1)
|
|
234
|
+
git commit -m "fix: handle expired tokens correctly"
|
|
235
|
+
|
|
236
|
+
# For breaking changes (bumps major version 1.0.0 -> 2.0.0)
|
|
237
|
+
git commit -m "feat!: change FCM service API
|
|
238
|
+
|
|
239
|
+
BREAKING CHANGE: sendToDevice now requires configuration object"
|
|
240
|
+
|
|
241
|
+
# No version bump
|
|
242
|
+
git commit -m "chore: update dependencies"
|
|
243
|
+
git commit -m "docs: update README"
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## License
|
|
247
|
+
|
|
248
|
+
MIT
|
|
249
|
+
|
|
250
|
+
## Links
|
|
251
|
+
|
|
252
|
+
- [GitHub Repository](https://github.com/zan-shop/push-notifications)
|
|
253
|
+
- [Issues](https://github.com/zan-shop/push-notifications/issues)
|
|
254
|
+
- [Firebase Cloud Messaging Docs](https://firebase.google.com/docs/cloud-messaging)
|
|
255
|
+
- [Medusa.js Docs](https://docs.medusajs.com)
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zan-shop/push-notifications
|
|
3
|
+
*
|
|
4
|
+
* Reusable push notification module for Medusa.js using Firebase Cloud Messaging
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import { FCMService, formatShipmentNotification } from '@zan-shop/push-notifications';
|
|
9
|
+
*
|
|
10
|
+
* const fcmService = new FCMService({ firebaseApp });
|
|
11
|
+
*
|
|
12
|
+
* const notification = formatShipmentNotification({
|
|
13
|
+
* type: 'shipment.created',
|
|
14
|
+
* orderId: '123',
|
|
15
|
+
* trackingNumber: 'ABC123',
|
|
16
|
+
* carrier: 'FedEx',
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* await fcmService.sendToDevice(deviceToken, notification);
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export type { DeviceToken, DeviceInfo, DeviceRegistration, Platform, } from './models/device-token';
|
|
23
|
+
export { FCMService, DeviceService } from './services';
|
|
24
|
+
export type { FCMServiceOptions } from './services';
|
|
25
|
+
export type { NotificationPayload, NotificationPriority, SendResult, BatchResult, NotificationError, ShipmentNotification, OrderNotification, ReturnNotification, } from './types';
|
|
26
|
+
export { validateFCMToken, validatePlatform, validateAppVersion, formatShipmentNotification, formatOrderNotification, formatReturnNotification, } from './utils';
|
|
27
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,YAAY,EACV,WAAW,EACX,UAAU,EACV,kBAAkB,EAClB,QAAQ,GACT,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACvD,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAGpD,YAAY,EACV,mBAAmB,EACnB,oBAAoB,EACpB,UAAU,EACV,WAAW,EACX,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,0BAA0B,EAC1B,uBAAuB,EACvB,wBAAwB,GACzB,MAAM,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @zan-shop/push-notifications
|
|
4
|
+
*
|
|
5
|
+
* Reusable push notification module for Medusa.js using Firebase Cloud Messaging
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { FCMService, formatShipmentNotification } from '@zan-shop/push-notifications';
|
|
10
|
+
*
|
|
11
|
+
* const fcmService = new FCMService({ firebaseApp });
|
|
12
|
+
*
|
|
13
|
+
* const notification = formatShipmentNotification({
|
|
14
|
+
* type: 'shipment.created',
|
|
15
|
+
* orderId: '123',
|
|
16
|
+
* trackingNumber: 'ABC123',
|
|
17
|
+
* carrier: 'FedEx',
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* await fcmService.sendToDevice(deviceToken, notification);
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.formatReturnNotification = exports.formatOrderNotification = exports.formatShipmentNotification = exports.validateAppVersion = exports.validatePlatform = exports.validateFCMToken = exports.DeviceService = exports.FCMService = void 0;
|
|
25
|
+
// Services
|
|
26
|
+
var services_1 = require("./services");
|
|
27
|
+
Object.defineProperty(exports, "FCMService", { enumerable: true, get: function () { return services_1.FCMService; } });
|
|
28
|
+
Object.defineProperty(exports, "DeviceService", { enumerable: true, get: function () { return services_1.DeviceService; } });
|
|
29
|
+
// Utils
|
|
30
|
+
var utils_1 = require("./utils");
|
|
31
|
+
Object.defineProperty(exports, "validateFCMToken", { enumerable: true, get: function () { return utils_1.validateFCMToken; } });
|
|
32
|
+
Object.defineProperty(exports, "validatePlatform", { enumerable: true, get: function () { return utils_1.validatePlatform; } });
|
|
33
|
+
Object.defineProperty(exports, "validateAppVersion", { enumerable: true, get: function () { return utils_1.validateAppVersion; } });
|
|
34
|
+
Object.defineProperty(exports, "formatShipmentNotification", { enumerable: true, get: function () { return utils_1.formatShipmentNotification; } });
|
|
35
|
+
Object.defineProperty(exports, "formatOrderNotification", { enumerable: true, get: function () { return utils_1.formatOrderNotification; } });
|
|
36
|
+
Object.defineProperty(exports, "formatReturnNotification", { enumerable: true, get: function () { return utils_1.formatReturnNotification; } });
|
|
37
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;;;AAUH,WAAW;AACX,uCAAuD;AAA9C,sGAAA,UAAU,OAAA;AAAE,yGAAA,aAAa,OAAA;AAelC,QAAQ;AACR,iCAOiB;AANf,yGAAA,gBAAgB,OAAA;AAChB,yGAAA,gBAAgB,OAAA;AAChB,2GAAA,kBAAkB,OAAA;AAClB,mHAAA,0BAA0B,OAAA;AAC1B,gHAAA,uBAAuB,OAAA;AACvB,iHAAA,wBAAwB,OAAA"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device Token Model
|
|
3
|
+
* Represents a registered mobile device for push notifications
|
|
4
|
+
*/
|
|
5
|
+
export interface DeviceToken {
|
|
6
|
+
id: string;
|
|
7
|
+
customer_id: string;
|
|
8
|
+
token: string;
|
|
9
|
+
platform: 'ios' | 'android';
|
|
10
|
+
app_version?: string;
|
|
11
|
+
device_info?: DeviceInfo;
|
|
12
|
+
is_active: boolean;
|
|
13
|
+
last_used_at: Date;
|
|
14
|
+
created_at: Date;
|
|
15
|
+
updated_at: Date;
|
|
16
|
+
deleted_at?: Date | null;
|
|
17
|
+
}
|
|
18
|
+
export interface DeviceInfo {
|
|
19
|
+
model?: string;
|
|
20
|
+
os_version?: string;
|
|
21
|
+
manufacturer?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface DeviceRegistration {
|
|
24
|
+
token: string;
|
|
25
|
+
platform: 'ios' | 'android';
|
|
26
|
+
app_version?: string;
|
|
27
|
+
device_info?: DeviceInfo;
|
|
28
|
+
}
|
|
29
|
+
export type Platform = 'ios' | 'android';
|
|
30
|
+
//# sourceMappingURL=device-token.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-token.d.ts","sourceRoot":"","sources":["../../src/models/device-token.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,KAAK,GAAG,SAAS,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,IAAI,CAAC;IACnB,UAAU,EAAE,IAAI,CAAC;IACjB,UAAU,EAAE,IAAI,CAAC;IACjB,UAAU,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,KAAK,GAAG,SAAS,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,UAAU,CAAC;CAC1B;AAED,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,SAAS,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-token.js","sourceRoot":"","sources":["../../src/models/device-token.ts"],"names":[],"mappings":";AAAA;;;GAGG"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device Service Interface
|
|
3
|
+
* Abstract interface for managing device tokens
|
|
4
|
+
*
|
|
5
|
+
* This is an abstract class that should be implemented with database-specific logic
|
|
6
|
+
* The implementing class in zan-backend will use Medusa's data layer
|
|
7
|
+
*/
|
|
8
|
+
import { DeviceToken, DeviceRegistration } from '../models/device-token';
|
|
9
|
+
export declare abstract class DeviceService {
|
|
10
|
+
/**
|
|
11
|
+
* Register a new device token for a customer
|
|
12
|
+
* Should handle deduplication (same customer + token)
|
|
13
|
+
*/
|
|
14
|
+
abstract registerDevice(customerId: string, deviceData: DeviceRegistration): Promise<DeviceToken>;
|
|
15
|
+
/**
|
|
16
|
+
* Update an existing device
|
|
17
|
+
*/
|
|
18
|
+
abstract updateDevice(tokenId: string, updates: Partial<DeviceToken>): Promise<DeviceToken>;
|
|
19
|
+
/**
|
|
20
|
+
* Deactivate a device (e.g., on logout)
|
|
21
|
+
* Marks device as inactive rather than deleting
|
|
22
|
+
*/
|
|
23
|
+
abstract deactivateDevice(token: string): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Deactivate all devices for a customer (e.g., on account deletion)
|
|
26
|
+
*/
|
|
27
|
+
abstract deactivateCustomerDevices(customerId: string): Promise<number>;
|
|
28
|
+
/**
|
|
29
|
+
* Get all active devices for a customer
|
|
30
|
+
*/
|
|
31
|
+
abstract getCustomerDevices(customerId: string): Promise<DeviceToken[]>;
|
|
32
|
+
/**
|
|
33
|
+
* Get a specific device by token
|
|
34
|
+
*/
|
|
35
|
+
abstract getDeviceByToken(token: string): Promise<DeviceToken | null>;
|
|
36
|
+
/**
|
|
37
|
+
* Update last_used_at timestamp for a device
|
|
38
|
+
*/
|
|
39
|
+
abstract touchDevice(token: string): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Clean up inactive tokens older than specified days
|
|
42
|
+
* Returns number of devices cleaned up
|
|
43
|
+
*/
|
|
44
|
+
abstract cleanupInactiveTokens(daysThreshold: number): Promise<number>;
|
|
45
|
+
/**
|
|
46
|
+
* Handle invalid tokens (called when FCM reports token as invalid)
|
|
47
|
+
* Marks tokens as inactive
|
|
48
|
+
*/
|
|
49
|
+
abstract markTokensAsInvalid(tokens: string[]): Promise<void>;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=device-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-service.d.ts","sourceRoot":"","sources":["../../src/services/device-service.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEzE,8BAAsB,aAAa;IACjC;;;OAGG;IACH,QAAQ,CAAC,cAAc,CACrB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,kBAAkB,GAC7B,OAAO,CAAC,WAAW,CAAC;IAEvB;;OAEG;IACH,QAAQ,CAAC,YAAY,CACnB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,GAC5B,OAAO,CAAC,WAAW,CAAC;IAEvB;;;OAGG;IACH,QAAQ,CAAC,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAEvD;;OAEG;IACH,QAAQ,CAAC,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAEvE;;OAEG;IACH,QAAQ,CAAC,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAEvE;;OAEG;IACH,QAAQ,CAAC,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAErE;;OAEG;IACH,QAAQ,CAAC,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAElD;;;OAGG;IACH,QAAQ,CAAC,qBAAqB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAEtE;;;OAGG;IACH,QAAQ,CAAC,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAC9D"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Device Service Interface
|
|
4
|
+
* Abstract interface for managing device tokens
|
|
5
|
+
*
|
|
6
|
+
* This is an abstract class that should be implemented with database-specific logic
|
|
7
|
+
* The implementing class in zan-backend will use Medusa's data layer
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.DeviceService = void 0;
|
|
11
|
+
class DeviceService {
|
|
12
|
+
}
|
|
13
|
+
exports.DeviceService = DeviceService;
|
|
14
|
+
//# sourceMappingURL=device-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-service.js","sourceRoot":"","sources":["../../src/services/device-service.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;AAIH,MAAsB,aAAa;CAuDlC;AAvDD,sCAuDC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FCM Service
|
|
3
|
+
* Handles sending push notifications via Firebase Cloud Messaging
|
|
4
|
+
*/
|
|
5
|
+
import * as admin from 'firebase-admin';
|
|
6
|
+
import { NotificationPayload, SendResult, BatchResult } from '../types/notification';
|
|
7
|
+
export interface FCMServiceOptions {
|
|
8
|
+
firebaseApp: admin.app.App;
|
|
9
|
+
dryRun?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare class FCMService {
|
|
12
|
+
private messaging;
|
|
13
|
+
private dryRun;
|
|
14
|
+
constructor(options: FCMServiceOptions);
|
|
15
|
+
/**
|
|
16
|
+
* Send notification to a single device
|
|
17
|
+
*/
|
|
18
|
+
sendToDevice(token: string, payload: NotificationPayload): Promise<SendResult>;
|
|
19
|
+
/**
|
|
20
|
+
* Send notification to multiple devices
|
|
21
|
+
*/
|
|
22
|
+
sendToMultipleDevices(tokens: string[], payload: NotificationPayload): Promise<BatchResult>;
|
|
23
|
+
/**
|
|
24
|
+
* Send notification to a topic (broadcast)
|
|
25
|
+
*/
|
|
26
|
+
sendToTopic(topic: string, payload: NotificationPayload): Promise<SendResult>;
|
|
27
|
+
/**
|
|
28
|
+
* Validate a device token
|
|
29
|
+
*/
|
|
30
|
+
validateToken(token: string): Promise<boolean>;
|
|
31
|
+
/**
|
|
32
|
+
* Send batch of notifications
|
|
33
|
+
*/
|
|
34
|
+
private sendBatch;
|
|
35
|
+
/**
|
|
36
|
+
* Build FCM message for a single device
|
|
37
|
+
*/
|
|
38
|
+
private buildMessage;
|
|
39
|
+
/**
|
|
40
|
+
* Build FCM message for a topic
|
|
41
|
+
*/
|
|
42
|
+
private buildTopicMessage;
|
|
43
|
+
/**
|
|
44
|
+
* Parse Firebase error into standardized format
|
|
45
|
+
*/
|
|
46
|
+
private parseError;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=fcm-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fcm-service.d.ts","sourceRoot":"","sources":["../../src/services/fcm-service.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAC;AACxC,OAAO,EACL,mBAAmB,EACnB,UAAU,EACV,WAAW,EAEZ,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,SAAS,CAA4B;IAC7C,OAAO,CAAC,MAAM,CAAU;gBAEZ,OAAO,EAAE,iBAAiB;IAKtC;;OAEG;IACG,YAAY,CAChB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,UAAU,CAAC;IAkBtB;;OAEG;IACG,qBAAqB,CACzB,MAAM,EAAE,MAAM,EAAE,EAChB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,WAAW,CAAC;IA6BvB;;OAEG;IACG,WAAW,CACf,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,UAAU,CAAC;IAkBtB;;OAEG;IACG,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAwBpD;;OAEG;YACW,SAAS;IA+BvB;;OAEG;IACH,OAAO,CAAC,YAAY;IAiCpB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA8BzB;;OAEG;IACH,OAAO,CAAC,UAAU;CAUnB"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* FCM Service
|
|
4
|
+
* Handles sending push notifications via Firebase Cloud Messaging
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.FCMService = void 0;
|
|
8
|
+
class FCMService {
|
|
9
|
+
constructor(options) {
|
|
10
|
+
this.messaging = options.firebaseApp.messaging();
|
|
11
|
+
this.dryRun = options.dryRun || false;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Send notification to a single device
|
|
15
|
+
*/
|
|
16
|
+
async sendToDevice(token, payload) {
|
|
17
|
+
try {
|
|
18
|
+
const message = this.buildMessage(token, payload);
|
|
19
|
+
const messageId = await this.messaging.send(message, this.dryRun);
|
|
20
|
+
return {
|
|
21
|
+
success: true,
|
|
22
|
+
messageId,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
return {
|
|
27
|
+
success: false,
|
|
28
|
+
error: this.parseError(error, token),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Send notification to multiple devices
|
|
34
|
+
*/
|
|
35
|
+
async sendToMultipleDevices(tokens, payload) {
|
|
36
|
+
if (tokens.length === 0) {
|
|
37
|
+
return {
|
|
38
|
+
successCount: 0,
|
|
39
|
+
failureCount: 0,
|
|
40
|
+
results: [],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// FCM allows max 500 tokens per batch
|
|
44
|
+
const batchSize = 500;
|
|
45
|
+
const results = [];
|
|
46
|
+
for (let i = 0; i < tokens.length; i += batchSize) {
|
|
47
|
+
const batch = tokens.slice(i, i + batchSize);
|
|
48
|
+
const batchResults = await this.sendBatch(batch, payload);
|
|
49
|
+
results.push(...batchResults);
|
|
50
|
+
}
|
|
51
|
+
const successCount = results.filter((r) => r.success).length;
|
|
52
|
+
const failureCount = results.length - successCount;
|
|
53
|
+
return {
|
|
54
|
+
successCount,
|
|
55
|
+
failureCount,
|
|
56
|
+
results,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Send notification to a topic (broadcast)
|
|
61
|
+
*/
|
|
62
|
+
async sendToTopic(topic, payload) {
|
|
63
|
+
try {
|
|
64
|
+
const message = this.buildTopicMessage(topic, payload);
|
|
65
|
+
const messageId = await this.messaging.send(message, this.dryRun);
|
|
66
|
+
return {
|
|
67
|
+
success: true,
|
|
68
|
+
messageId,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
return {
|
|
73
|
+
success: false,
|
|
74
|
+
error: this.parseError(error),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Validate a device token
|
|
80
|
+
*/
|
|
81
|
+
async validateToken(token) {
|
|
82
|
+
try {
|
|
83
|
+
// Try to send a dry-run message
|
|
84
|
+
await this.messaging.send({
|
|
85
|
+
token,
|
|
86
|
+
notification: {
|
|
87
|
+
title: 'Test',
|
|
88
|
+
body: 'Test',
|
|
89
|
+
},
|
|
90
|
+
}, true // Always dry run for validation
|
|
91
|
+
);
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
// Token is invalid if we get specific error codes
|
|
96
|
+
const invalidTokenCodes = [
|
|
97
|
+
'messaging/invalid-registration-token',
|
|
98
|
+
'messaging/registration-token-not-registered',
|
|
99
|
+
];
|
|
100
|
+
return !invalidTokenCodes.includes(error?.code);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Send batch of notifications
|
|
105
|
+
*/
|
|
106
|
+
async sendBatch(tokens, payload) {
|
|
107
|
+
try {
|
|
108
|
+
const messages = tokens.map((token) => this.buildMessage(token, payload));
|
|
109
|
+
const response = await this.messaging.sendEach(messages, this.dryRun);
|
|
110
|
+
return response.responses.map((resp, index) => {
|
|
111
|
+
if (resp.success) {
|
|
112
|
+
return {
|
|
113
|
+
success: true,
|
|
114
|
+
messageId: resp.messageId,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
return {
|
|
119
|
+
success: false,
|
|
120
|
+
error: this.parseError(resp.error, tokens[index]),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
// If entire batch fails, return error for all tokens
|
|
127
|
+
return tokens.map((token) => ({
|
|
128
|
+
success: false,
|
|
129
|
+
error: this.parseError(error, token),
|
|
130
|
+
}));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Build FCM message for a single device
|
|
135
|
+
*/
|
|
136
|
+
buildMessage(token, payload) {
|
|
137
|
+
return {
|
|
138
|
+
token,
|
|
139
|
+
notification: {
|
|
140
|
+
title: payload.title,
|
|
141
|
+
body: payload.body,
|
|
142
|
+
...(payload.imageUrl && { imageUrl: payload.imageUrl }),
|
|
143
|
+
},
|
|
144
|
+
data: payload.data,
|
|
145
|
+
android: {
|
|
146
|
+
priority: payload.priority === 'high' ? 'high' : 'normal',
|
|
147
|
+
notification: {
|
|
148
|
+
sound: payload.sound || 'default',
|
|
149
|
+
clickAction: payload.clickAction,
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
apns: {
|
|
153
|
+
payload: {
|
|
154
|
+
aps: {
|
|
155
|
+
badge: payload.badge,
|
|
156
|
+
sound: payload.sound || 'default',
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
fcmOptions: {
|
|
160
|
+
...(payload.imageUrl && { imageUrl: payload.imageUrl }),
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Build FCM message for a topic
|
|
167
|
+
*/
|
|
168
|
+
buildTopicMessage(topic, payload) {
|
|
169
|
+
return {
|
|
170
|
+
topic,
|
|
171
|
+
notification: {
|
|
172
|
+
title: payload.title,
|
|
173
|
+
body: payload.body,
|
|
174
|
+
...(payload.imageUrl && { imageUrl: payload.imageUrl }),
|
|
175
|
+
},
|
|
176
|
+
data: payload.data,
|
|
177
|
+
android: {
|
|
178
|
+
priority: payload.priority === 'high' ? 'high' : 'normal',
|
|
179
|
+
notification: {
|
|
180
|
+
sound: payload.sound || 'default',
|
|
181
|
+
clickAction: payload.clickAction,
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
apns: {
|
|
185
|
+
payload: {
|
|
186
|
+
aps: {
|
|
187
|
+
badge: payload.badge,
|
|
188
|
+
sound: payload.sound || 'default',
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Parse Firebase error into standardized format
|
|
196
|
+
*/
|
|
197
|
+
parseError(error, token) {
|
|
198
|
+
const code = error?.code || 'unknown';
|
|
199
|
+
const message = error?.message || 'Unknown error occurred';
|
|
200
|
+
return {
|
|
201
|
+
code,
|
|
202
|
+
message,
|
|
203
|
+
...(token && { token }),
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
exports.FCMService = FCMService;
|
|
208
|
+
//# sourceMappingURL=fcm-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fcm-service.js","sourceRoot":"","sources":["../../src/services/fcm-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Services Index
|
|
4
|
+
* Exports all service classes
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.DeviceService = exports.FCMService = void 0;
|
|
8
|
+
var fcm_service_1 = require("./fcm-service");
|
|
9
|
+
Object.defineProperty(exports, "FCMService", { enumerable: true, get: function () { return fcm_service_1.FCMService; } });
|
|
10
|
+
var device_service_1 = require("./device-service");
|
|
11
|
+
Object.defineProperty(exports, "DeviceService", { enumerable: true, get: function () { return device_service_1.DeviceService; } });
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,6CAA2C;AAAlC,yGAAA,UAAU,OAAA;AAEnB,mDAAiD;AAAxC,+GAAA,aAAa,OAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,gBAAgB,CAAC;AAC/B,YAAY,EACV,WAAW,EACX,UAAU,EACV,kBAAkB,EAClB,QAAQ,GACT,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Type Definitions Index
|
|
4
|
+
* Exports all type definitions for external use
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
18
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
19
|
+
};
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
__exportStar(require("./notification"), exports);
|
|
22
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;AAEH,iDAA+B"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notification Types
|
|
3
|
+
* Type definitions for push notification payloads and results
|
|
4
|
+
*/
|
|
5
|
+
export interface NotificationPayload {
|
|
6
|
+
title: string;
|
|
7
|
+
body: string;
|
|
8
|
+
data?: Record<string, string>;
|
|
9
|
+
imageUrl?: string;
|
|
10
|
+
sound?: string;
|
|
11
|
+
badge?: number;
|
|
12
|
+
clickAction?: string;
|
|
13
|
+
priority?: NotificationPriority;
|
|
14
|
+
}
|
|
15
|
+
export type NotificationPriority = 'high' | 'normal';
|
|
16
|
+
export interface SendResult {
|
|
17
|
+
success: boolean;
|
|
18
|
+
messageId?: string;
|
|
19
|
+
error?: NotificationError;
|
|
20
|
+
}
|
|
21
|
+
export interface BatchResult {
|
|
22
|
+
successCount: number;
|
|
23
|
+
failureCount: number;
|
|
24
|
+
results: SendResult[];
|
|
25
|
+
}
|
|
26
|
+
export interface NotificationError {
|
|
27
|
+
code: string;
|
|
28
|
+
message: string;
|
|
29
|
+
token?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface ShipmentNotification {
|
|
32
|
+
type: 'shipment.created';
|
|
33
|
+
orderId: string;
|
|
34
|
+
trackingNumber?: string;
|
|
35
|
+
carrier?: string;
|
|
36
|
+
estimatedDelivery?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface OrderNotification {
|
|
39
|
+
type: 'order.placed' | 'order.delivered' | 'order.canceled';
|
|
40
|
+
orderId: string;
|
|
41
|
+
orderNumber?: string;
|
|
42
|
+
}
|
|
43
|
+
export interface ReturnNotification {
|
|
44
|
+
type: 'return.created' | 'return.approved' | 'return.rejected';
|
|
45
|
+
returnId: string;
|
|
46
|
+
orderId: string;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=notification.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification.d.ts","sourceRoot":"","sources":["../../src/types/notification.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,oBAAoB,CAAC;CACjC;AAED,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,QAAQ,CAAC;AAErD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,iBAAiB,CAAC;CAC3B;AAED,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,UAAU,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAGD,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,kBAAkB,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,cAAc,GAAG,iBAAiB,GAAG,gBAAgB,CAAC;IAC5D,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,gBAAgB,GAAG,iBAAiB,GAAG,iBAAiB,CAAC;IAC/D,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification.js","sourceRoot":"","sources":["../../src/types/notification.ts"],"names":[],"mappings":";AAAA;;;GAGG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,cAAc,CAAC;AAC7B,cAAc,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Utils Index
|
|
4
|
+
* Exports all utility functions
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
18
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
19
|
+
};
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
__exportStar(require("./validators"), exports);
|
|
22
|
+
__exportStar(require("./notification-formatter"), exports);
|
|
23
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;AAEH,+CAA6B;AAC7B,2DAAyC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notification Formatters
|
|
3
|
+
* Helper functions to format notifications for different event types
|
|
4
|
+
*/
|
|
5
|
+
import { NotificationPayload, ShipmentNotification, OrderNotification, ReturnNotification } from '../types/notification';
|
|
6
|
+
/**
|
|
7
|
+
* Format shipment notification
|
|
8
|
+
*/
|
|
9
|
+
export declare function formatShipmentNotification(data: ShipmentNotification): NotificationPayload;
|
|
10
|
+
/**
|
|
11
|
+
* Format order notification
|
|
12
|
+
*/
|
|
13
|
+
export declare function formatOrderNotification(data: OrderNotification): NotificationPayload;
|
|
14
|
+
/**
|
|
15
|
+
* Format return notification
|
|
16
|
+
*/
|
|
17
|
+
export declare function formatReturnNotification(data: ReturnNotification): NotificationPayload;
|
|
18
|
+
//# sourceMappingURL=notification-formatter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-formatter.d.ts","sourceRoot":"","sources":["../../src/utils/notification-formatter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,uBAAuB,CAAC;AAE/B;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,oBAAoB,GACzB,mBAAmB,CA2BrB;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,iBAAiB,GACtB,mBAAmB,CA0BrB;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,kBAAkB,GACvB,mBAAmB,CA0BrB"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Notification Formatters
|
|
4
|
+
* Helper functions to format notifications for different event types
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.formatShipmentNotification = formatShipmentNotification;
|
|
8
|
+
exports.formatOrderNotification = formatOrderNotification;
|
|
9
|
+
exports.formatReturnNotification = formatReturnNotification;
|
|
10
|
+
/**
|
|
11
|
+
* Format shipment notification
|
|
12
|
+
*/
|
|
13
|
+
function formatShipmentNotification(data) {
|
|
14
|
+
const { orderId, trackingNumber, carrier, estimatedDelivery } = data;
|
|
15
|
+
let body = `Your order #${orderId} has been shipped!`;
|
|
16
|
+
if (carrier && trackingNumber) {
|
|
17
|
+
body += ` Track your package with ${carrier}: ${trackingNumber}`;
|
|
18
|
+
}
|
|
19
|
+
else if (trackingNumber) {
|
|
20
|
+
body += ` Tracking: ${trackingNumber}`;
|
|
21
|
+
}
|
|
22
|
+
if (estimatedDelivery) {
|
|
23
|
+
body += ` Estimated delivery: ${estimatedDelivery}`;
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
title: '๐ฆ Order Shipped',
|
|
27
|
+
body,
|
|
28
|
+
data: {
|
|
29
|
+
type: 'shipment.created',
|
|
30
|
+
orderId,
|
|
31
|
+
...(trackingNumber && { trackingNumber }),
|
|
32
|
+
...(carrier && { carrier }),
|
|
33
|
+
},
|
|
34
|
+
clickAction: `/orders/${orderId}`,
|
|
35
|
+
priority: 'high',
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Format order notification
|
|
40
|
+
*/
|
|
41
|
+
function formatOrderNotification(data) {
|
|
42
|
+
const { type, orderId, orderNumber } = data;
|
|
43
|
+
const titles = {
|
|
44
|
+
'order.placed': 'โ
Order Confirmed',
|
|
45
|
+
'order.delivered': '๐ Order Delivered',
|
|
46
|
+
'order.canceled': 'โ Order Canceled',
|
|
47
|
+
};
|
|
48
|
+
const bodies = {
|
|
49
|
+
'order.placed': `Your order ${orderNumber || `#${orderId}`} has been confirmed!`,
|
|
50
|
+
'order.delivered': `Your order ${orderNumber || `#${orderId}`} has been delivered!`,
|
|
51
|
+
'order.canceled': `Your order ${orderNumber || `#${orderId}`} has been canceled.`,
|
|
52
|
+
};
|
|
53
|
+
return {
|
|
54
|
+
title: titles[type],
|
|
55
|
+
body: bodies[type],
|
|
56
|
+
data: {
|
|
57
|
+
type,
|
|
58
|
+
orderId,
|
|
59
|
+
...(orderNumber && { orderNumber }),
|
|
60
|
+
},
|
|
61
|
+
clickAction: `/orders/${orderId}`,
|
|
62
|
+
priority: type === 'order.placed' ? 'high' : 'normal',
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Format return notification
|
|
67
|
+
*/
|
|
68
|
+
function formatReturnNotification(data) {
|
|
69
|
+
const { type, returnId, orderId } = data;
|
|
70
|
+
const titles = {
|
|
71
|
+
'return.created': '๐ Return Initiated',
|
|
72
|
+
'return.approved': 'โ
Return Approved',
|
|
73
|
+
'return.rejected': 'โ Return Rejected',
|
|
74
|
+
};
|
|
75
|
+
const bodies = {
|
|
76
|
+
'return.created': `Your return request for order #${orderId} has been submitted.`,
|
|
77
|
+
'return.approved': `Your return request for order #${orderId} has been approved!`,
|
|
78
|
+
'return.rejected': `Your return request for order #${orderId} was rejected.`,
|
|
79
|
+
};
|
|
80
|
+
return {
|
|
81
|
+
title: titles[type],
|
|
82
|
+
body: bodies[type],
|
|
83
|
+
data: {
|
|
84
|
+
type,
|
|
85
|
+
returnId,
|
|
86
|
+
orderId,
|
|
87
|
+
},
|
|
88
|
+
clickAction: `/returns/${returnId}`,
|
|
89
|
+
priority: 'normal',
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=notification-formatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-formatter.js","sourceRoot":"","sources":["../../src/utils/notification-formatter.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAYH,gEA6BC;AAKD,0DA4BC;AAKD,4DA4BC;AAlGD;;GAEG;AACH,SAAgB,0BAA0B,CACxC,IAA0B;IAE1B,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,iBAAiB,EAAE,GAAG,IAAI,CAAC;IAErE,IAAI,IAAI,GAAG,eAAe,OAAO,oBAAoB,CAAC;IAEtD,IAAI,OAAO,IAAI,cAAc,EAAE,CAAC;QAC9B,IAAI,IAAI,4BAA4B,OAAO,KAAK,cAAc,EAAE,CAAC;IACnE,CAAC;SAAM,IAAI,cAAc,EAAE,CAAC;QAC1B,IAAI,IAAI,cAAc,cAAc,EAAE,CAAC;IACzC,CAAC;IAED,IAAI,iBAAiB,EAAE,CAAC;QACtB,IAAI,IAAI,wBAAwB,iBAAiB,EAAE,CAAC;IACtD,CAAC;IAED,OAAO;QACL,KAAK,EAAE,kBAAkB;QACzB,IAAI;QACJ,IAAI,EAAE;YACJ,IAAI,EAAE,kBAAkB;YACxB,OAAO;YACP,GAAG,CAAC,cAAc,IAAI,EAAE,cAAc,EAAE,CAAC;YACzC,GAAG,CAAC,OAAO,IAAI,EAAE,OAAO,EAAE,CAAC;SAC5B;QACD,WAAW,EAAE,WAAW,OAAO,EAAE;QACjC,QAAQ,EAAE,MAAM;KACjB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAgB,uBAAuB,CACrC,IAAuB;IAEvB,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC;IAE5C,MAAM,MAAM,GAA8C;QACxD,cAAc,EAAE,mBAAmB;QACnC,iBAAiB,EAAE,oBAAoB;QACvC,gBAAgB,EAAE,kBAAkB;KACrC,CAAC;IAEF,MAAM,MAAM,GAA8C;QACxD,cAAc,EAAE,cAAc,WAAW,IAAI,IAAI,OAAO,EAAE,sBAAsB;QAChF,iBAAiB,EAAE,cAAc,WAAW,IAAI,IAAI,OAAO,EAAE,sBAAsB;QACnF,gBAAgB,EAAE,cAAc,WAAW,IAAI,IAAI,OAAO,EAAE,qBAAqB;KAClF,CAAC;IAEF,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC;QAClB,IAAI,EAAE;YACJ,IAAI;YACJ,OAAO;YACP,GAAG,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,CAAC;SACpC;QACD,WAAW,EAAE,WAAW,OAAO,EAAE;QACjC,QAAQ,EAAE,IAAI,KAAK,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;KACtD,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAgB,wBAAwB,CACtC,IAAwB;IAExB,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAEzC,MAAM,MAAM,GAA+C;QACzD,gBAAgB,EAAE,qBAAqB;QACvC,iBAAiB,EAAE,mBAAmB;QACtC,iBAAiB,EAAE,mBAAmB;KACvC,CAAC;IAEF,MAAM,MAAM,GAA+C;QACzD,gBAAgB,EAAE,kCAAkC,OAAO,sBAAsB;QACjF,iBAAiB,EAAE,kCAAkC,OAAO,qBAAqB;QACjF,iBAAiB,EAAE,kCAAkC,OAAO,gBAAgB;KAC7E,CAAC;IAEF,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC;QAClB,IAAI,EAAE;YACJ,IAAI;YACJ,QAAQ;YACR,OAAO;SACR;QACD,WAAW,EAAE,YAAY,QAAQ,EAAE;QACnC,QAAQ,EAAE,QAAQ;KACnB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FCM Token Validator
|
|
3
|
+
* Validates Firebase Cloud Messaging device tokens
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Validate FCM token format
|
|
7
|
+
* FCM tokens are typically 152+ characters, alphanumeric with special chars
|
|
8
|
+
*/
|
|
9
|
+
export declare function validateFCMToken(token: string): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Validate platform value
|
|
12
|
+
*/
|
|
13
|
+
export declare function validatePlatform(platform: string): platform is 'ios' | 'android';
|
|
14
|
+
/**
|
|
15
|
+
* Validate app version format (semver-like)
|
|
16
|
+
*/
|
|
17
|
+
export declare function validateAppVersion(version: string): boolean;
|
|
18
|
+
//# sourceMappingURL=validators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../src/utils/validators.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAavD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,IAAI,KAAK,GAAG,SAAS,CAEhF;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAQ3D"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* FCM Token Validator
|
|
4
|
+
* Validates Firebase Cloud Messaging device tokens
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.validateFCMToken = validateFCMToken;
|
|
8
|
+
exports.validatePlatform = validatePlatform;
|
|
9
|
+
exports.validateAppVersion = validateAppVersion;
|
|
10
|
+
/**
|
|
11
|
+
* Validate FCM token format
|
|
12
|
+
* FCM tokens are typically 152+ characters, alphanumeric with special chars
|
|
13
|
+
*/
|
|
14
|
+
function validateFCMToken(token) {
|
|
15
|
+
if (!token || typeof token !== 'string') {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
// FCM tokens are typically 152-163 characters long
|
|
19
|
+
if (token.length < 140 || token.length > 200) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
// Should contain only alphanumeric, hyphens, underscores, and colons
|
|
23
|
+
const validTokenRegex = /^[a-zA-Z0-9_:-]+$/;
|
|
24
|
+
return validTokenRegex.test(token);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Validate platform value
|
|
28
|
+
*/
|
|
29
|
+
function validatePlatform(platform) {
|
|
30
|
+
return platform === 'ios' || platform === 'android';
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Validate app version format (semver-like)
|
|
34
|
+
*/
|
|
35
|
+
function validateAppVersion(version) {
|
|
36
|
+
if (!version || typeof version !== 'string') {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
// Accept formats like: 1.0.0, 1.0, 1.0.0-beta, etc.
|
|
40
|
+
const versionRegex = /^\d+\.\d+(\.\d+)?(-[a-zA-Z0-9.-]+)?$/;
|
|
41
|
+
return versionRegex.test(version);
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=validators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validators.js","sourceRoot":"","sources":["../../src/utils/validators.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAMH,4CAaC;AAKD,4CAEC;AAKD,gDAQC;AArCD;;;GAGG;AACH,SAAgB,gBAAgB,CAAC,KAAa;IAC5C,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,mDAAmD;IACnD,IAAI,KAAK,CAAC,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QAC7C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,qEAAqE;IACrE,MAAM,eAAe,GAAG,mBAAmB,CAAC;IAC5C,OAAO,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACrC,CAAC;AAED;;GAEG;AACH,SAAgB,gBAAgB,CAAC,QAAgB;IAC/C,OAAO,QAAQ,KAAK,KAAK,IAAI,QAAQ,KAAK,SAAS,CAAC;AACtD,CAAC;AAED;;GAEG;AACH,SAAgB,kBAAkB,CAAC,OAAe;IAChD,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC5C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,oDAAoD;IACpD,MAAM,YAAY,GAAG,sCAAsC,CAAC;IAC5D,OAAO,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AACpC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zan-shop/push-notifications",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Push notification module for Medusa.js using Firebase Cloud Messaging",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"watch": "tsc --watch",
|
|
13
|
+
"test": "jest",
|
|
14
|
+
"prepublishOnly": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"medusa",
|
|
18
|
+
"medusa-plugin",
|
|
19
|
+
"push-notifications",
|
|
20
|
+
"fcm",
|
|
21
|
+
"firebase",
|
|
22
|
+
"cloud-messaging"
|
|
23
|
+
],
|
|
24
|
+
"author": "zan shop",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@medusajs/framework": "^2.0.0",
|
|
28
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
29
|
+
"@semantic-release/git": "^10.0.1",
|
|
30
|
+
"@types/jest": "^29.5.0",
|
|
31
|
+
"@types/node": "^20.0.0",
|
|
32
|
+
"jest": "^29.5.0",
|
|
33
|
+
"semantic-release": "^24.0.0",
|
|
34
|
+
"ts-jest": "^29.1.0",
|
|
35
|
+
"typescript": "^5.0.0"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"firebase-admin": "^12.0.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"@medusajs/framework": "^2.0.0"
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "git+https://github.com/zan-shop/push-notifications.git"
|
|
49
|
+
},
|
|
50
|
+
"homepage": "https://github.com/zan-shop/push-notifications#readme",
|
|
51
|
+
"bugs": {
|
|
52
|
+
"url": "https://github.com/zan-shop/push-notifications/issues"
|
|
53
|
+
}
|
|
54
|
+
}
|