@solytude/listmonk 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 +542 -0
- package/dist/chunk-saezhne8.js +14 -0
- package/dist/chunk-saezhne8.js.map +9 -0
- package/dist/client.d.ts +430 -0
- package/dist/errors/api.d.ts +178 -0
- package/dist/errors/base.d.ts +47 -0
- package/dist/errors/configuration.d.ts +49 -0
- package/dist/errors/index.d.ts +13 -0
- package/dist/errors/network.d.ts +69 -0
- package/dist/errors/validation.d.ts +57 -0
- package/dist/http/auth.d.ts +39 -0
- package/dist/http/client.d.ts +149 -0
- package/dist/http/hooks.d.ts +90 -0
- package/dist/http/index.d.ts +10 -0
- package/dist/http/resource.d.ts +100 -0
- package/dist/http/sse.d.ts +36 -0
- package/dist/http/url.d.ts +51 -0
- package/dist/index.d.ts +64 -0
- package/dist/index.js +16039 -0
- package/dist/index.js.map +120 -0
- package/dist/resources/admin/admin.d.ts +92 -0
- package/dist/resources/admin/index.d.ts +10 -0
- package/dist/resources/admin/schemas.d.ts +23 -0
- package/dist/resources/admin/types.d.ts +29 -0
- package/dist/resources/bounces/bounces.d.ts +262 -0
- package/dist/resources/bounces/index.d.ts +10 -0
- package/dist/resources/bounces/schemas.d.ts +86 -0
- package/dist/resources/bounces/types.d.ts +37 -0
- package/dist/resources/campaigns/campaigns.d.ts +339 -0
- package/dist/resources/campaigns/index.d.ts +10 -0
- package/dist/resources/campaigns/schemas.d.ts +374 -0
- package/dist/resources/campaigns/types.d.ts +111 -0
- package/dist/resources/dashboard/dashboard.d.ts +73 -0
- package/dist/resources/dashboard/index.d.ts +10 -0
- package/dist/resources/dashboard/schemas.d.ts +55 -0
- package/dist/resources/dashboard/types.d.ts +22 -0
- package/dist/resources/import/import.d.ts +215 -0
- package/dist/resources/import/index.d.ts +10 -0
- package/dist/resources/import/schemas.d.ts +109 -0
- package/dist/resources/import/types.d.ts +72 -0
- package/dist/resources/lists/index.d.ts +10 -0
- package/dist/resources/lists/lists.d.ts +180 -0
- package/dist/resources/lists/schemas.d.ts +176 -0
- package/dist/resources/lists/types.d.ts +56 -0
- package/dist/resources/maintenance/index.d.ts +10 -0
- package/dist/resources/maintenance/maintenance.d.ts +92 -0
- package/dist/resources/maintenance/schemas.d.ts +36 -0
- package/dist/resources/maintenance/types.d.ts +31 -0
- package/dist/resources/media/index.d.ts +10 -0
- package/dist/resources/media/media.d.ts +198 -0
- package/dist/resources/media/schemas.d.ts +48 -0
- package/dist/resources/media/types.d.ts +48 -0
- package/dist/resources/public/index.d.ts +10 -0
- package/dist/resources/public/public.d.ts +111 -0
- package/dist/resources/public/schemas.d.ts +52 -0
- package/dist/resources/public/types.d.ts +30 -0
- package/dist/resources/settings/index.d.ts +10 -0
- package/dist/resources/settings/schemas.d.ts +374 -0
- package/dist/resources/settings/settings.d.ts +106 -0
- package/dist/resources/settings/types.d.ts +78 -0
- package/dist/resources/subscribers/index.d.ts +10 -0
- package/dist/resources/subscribers/schemas.d.ts +360 -0
- package/dist/resources/subscribers/subscribers.d.ts +308 -0
- package/dist/resources/subscribers/types.d.ts +113 -0
- package/dist/resources/templates/index.d.ts +10 -0
- package/dist/resources/templates/schemas.d.ts +110 -0
- package/dist/resources/templates/templates.d.ts +225 -0
- package/dist/resources/templates/types.d.ts +45 -0
- package/dist/resources/tx/index.d.ts +10 -0
- package/dist/resources/tx/schemas.d.ts +67 -0
- package/dist/resources/tx/tx.d.ts +167 -0
- package/dist/resources/tx/types.d.ts +88 -0
- package/dist/schemas/common.d.ts +48 -0
- package/dist/schemas/index.d.ts +6 -0
- package/dist/testing/errors.d.ts +25 -0
- package/dist/testing/factories/bounce.d.ts +43 -0
- package/dist/testing/factories/campaign.d.ts +43 -0
- package/dist/testing/factories/common.d.ts +54 -0
- package/dist/testing/factories/index.d.ts +12 -0
- package/dist/testing/factories/list.d.ts +43 -0
- package/dist/testing/factories/media.d.ts +40 -0
- package/dist/testing/factories/subscriber.d.ts +43 -0
- package/dist/testing/factories/template.d.ts +40 -0
- package/dist/testing/index.d.ts +39 -0
- package/dist/testing/index.js +573 -0
- package/dist/testing/index.js.map +32 -0
- package/dist/testing/mock-client.d.ts +119 -0
- package/dist/testing/mock-function.d.ts +28 -0
- package/dist/testing/resources/admin.d.ts +20 -0
- package/dist/testing/resources/bounces.d.ts +22 -0
- package/dist/testing/resources/campaigns.d.ts +31 -0
- package/dist/testing/resources/dashboard.d.ts +17 -0
- package/dist/testing/resources/import.d.ts +19 -0
- package/dist/testing/resources/index.d.ts +18 -0
- package/dist/testing/resources/lists.d.ts +24 -0
- package/dist/testing/resources/maintenance.d.ts +18 -0
- package/dist/testing/resources/media.d.ts +21 -0
- package/dist/testing/resources/public.d.ts +18 -0
- package/dist/testing/resources/settings.d.ts +19 -0
- package/dist/testing/resources/subscribers.d.ts +33 -0
- package/dist/testing/resources/templates.d.ts +24 -0
- package/dist/testing/resources/tx.d.ts +16 -0
- package/dist/testing/types.d.ts +138 -0
- package/dist/types/config.d.ts +59 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/pagination.d.ts +37 -0
- package/package.json +97 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 solytude
|
|
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,542 @@
|
|
|
1
|
+
# @solytude/listmonk
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@solytude/listmonk)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](https://www.typescriptlang.org/)
|
|
6
|
+
|
|
7
|
+
Production-grade TypeScript SDK for the [listmonk](https://listmonk.app/) email marketing platform API.
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
- [Features](#features)
|
|
12
|
+
- [Requirements](#requirements)
|
|
13
|
+
- [Installation](#installation)
|
|
14
|
+
- [Quick Start](#quick-start)
|
|
15
|
+
- [Authentication](#authentication)
|
|
16
|
+
- [Resources](#resources)
|
|
17
|
+
- [Usage Examples](#usage-examples)
|
|
18
|
+
- [Configuration](#configuration)
|
|
19
|
+
- [Error Handling](#error-handling)
|
|
20
|
+
- [Pagination](#pagination)
|
|
21
|
+
- [TypeScript](#typescript)
|
|
22
|
+
- [Testing](#testing)
|
|
23
|
+
- [Contributing](#contributing)
|
|
24
|
+
- [License](#license)
|
|
25
|
+
|
|
26
|
+
## Features
|
|
27
|
+
|
|
28
|
+
- **Complete API Coverage** — Full listmonk API coverage across 13 resource modules
|
|
29
|
+
- **Type-Safe** — Full TypeScript support with strict typing and Zod runtime validation
|
|
30
|
+
- **Zero Dependencies** — Only [Zod](https://zod.dev/) as a runtime dependency
|
|
31
|
+
- **Modern Patterns** — Async iterators for pagination, AbortController for timeouts
|
|
32
|
+
- **Stripe-Style API** — Intuitive resource-based interface (`client.subscribers.create()`)
|
|
33
|
+
- **Resilient** — Automatic retries with exponential backoff and jitter
|
|
34
|
+
- **Flexible** — Custom fetch support for edge runtimes and testing
|
|
35
|
+
- **Secure** — HTTPS enforcement, credential redaction in debug mode
|
|
36
|
+
|
|
37
|
+
## Requirements
|
|
38
|
+
|
|
39
|
+
- **Node.js** 22+ or **Bun** 1.3+
|
|
40
|
+
- **TypeScript** 5.7+ (optional, but recommended)
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# npm
|
|
46
|
+
npm install @solytude/listmonk
|
|
47
|
+
|
|
48
|
+
# yarn
|
|
49
|
+
yarn add @solytude/listmonk
|
|
50
|
+
|
|
51
|
+
# pnpm
|
|
52
|
+
pnpm add @solytude/listmonk
|
|
53
|
+
|
|
54
|
+
# bun
|
|
55
|
+
bun add @solytude/listmonk
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Quick Start
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { Listmonk } from '@solytude/listmonk';
|
|
62
|
+
|
|
63
|
+
const client = new Listmonk({
|
|
64
|
+
url: 'https://listmonk.example.com',
|
|
65
|
+
auth: {
|
|
66
|
+
username: process.env.LISTMONK_USERNAME!,
|
|
67
|
+
password: process.env.LISTMONK_PASSWORD!,
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Create a subscriber
|
|
72
|
+
const subscriber = await client.subscribers.create({
|
|
73
|
+
email: 'user@example.com',
|
|
74
|
+
name: 'John Doe',
|
|
75
|
+
lists: [1, 2],
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
console.log(`Created subscriber: ${subscriber.id}`);
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Authentication
|
|
82
|
+
|
|
83
|
+
The SDK uses Basic Authentication with username and password credentials. Create API credentials in your listmonk admin panel under **Settings → API**.
|
|
84
|
+
|
|
85
|
+
### Using Environment Variables (Recommended)
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
export LISTMONK_URL=https://listmonk.example.com
|
|
89
|
+
export LISTMONK_USERNAME=your_username
|
|
90
|
+
export LISTMONK_PASSWORD=your_password
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
const client = new Listmonk({
|
|
95
|
+
url: process.env.LISTMONK_URL!,
|
|
96
|
+
auth: {
|
|
97
|
+
username: process.env.LISTMONK_USERNAME!,
|
|
98
|
+
password: process.env.LISTMONK_PASSWORD!,
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
> **Security Warning**: Never commit credentials to version control. Always use environment variables or a secrets manager.
|
|
104
|
+
|
|
105
|
+
## Resources
|
|
106
|
+
|
|
107
|
+
The SDK provides access to all listmonk API resources:
|
|
108
|
+
|
|
109
|
+
| Resource | Description | Methods |
|
|
110
|
+
|----------|-------------|---------|
|
|
111
|
+
| `subscribers` | Subscriber management, blocklisting, bounces | 17 |
|
|
112
|
+
| `lists` | Mailing list CRUD operations | 5 |
|
|
113
|
+
| `campaigns` | Campaign lifecycle, analytics, previews | 14 |
|
|
114
|
+
| `templates` | Email template management | 8 |
|
|
115
|
+
| `tx` | Transactional email sending | 1 |
|
|
116
|
+
| `media` | Media file uploads and management | 4 |
|
|
117
|
+
| `import` | Bulk subscriber imports (CSV/ZIP) | 4 |
|
|
118
|
+
| `bounces` | Bounce record management | 4 |
|
|
119
|
+
| `settings` | Server settings and SMTP configuration | 4 |
|
|
120
|
+
| `dashboard` | Dashboard statistics and charts | 2 |
|
|
121
|
+
| `admin` | Server administration and logs | 3 |
|
|
122
|
+
| `maintenance` | Garbage collection and cleanup | 3 |
|
|
123
|
+
| `public` | Public subscription endpoints | 3 |
|
|
124
|
+
|
|
125
|
+
## Usage Examples
|
|
126
|
+
|
|
127
|
+
### Subscribers
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
// List subscribers with pagination
|
|
131
|
+
const subscribers = await client.subscribers.list({
|
|
132
|
+
page: 1,
|
|
133
|
+
per_page: 100,
|
|
134
|
+
query: 'subscribers.status = \'enabled\'',
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Get a single subscriber
|
|
138
|
+
const subscriber = await client.subscribers.get(123);
|
|
139
|
+
|
|
140
|
+
// Update a subscriber
|
|
141
|
+
await client.subscribers.update(123, {
|
|
142
|
+
name: 'Jane Doe',
|
|
143
|
+
attribs: { company: 'Acme Inc' },
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Delete a subscriber
|
|
147
|
+
await client.subscribers.delete(123);
|
|
148
|
+
|
|
149
|
+
// Bulk operations
|
|
150
|
+
await client.subscribers.addToLists([1, 2, 3], { lists: [1, 2] });
|
|
151
|
+
await client.subscribers.blocklist([4, 5, 6]);
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Campaigns
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
// Create a campaign
|
|
158
|
+
const campaign = await client.campaigns.create({
|
|
159
|
+
name: 'Weekly Newsletter',
|
|
160
|
+
subject: 'This Week in Tech',
|
|
161
|
+
lists: [1],
|
|
162
|
+
type: 'regular',
|
|
163
|
+
content_type: 'richtext',
|
|
164
|
+
body: '<h1>Hello {{.Subscriber.Name}}</h1>',
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Preview a campaign
|
|
168
|
+
const preview = await client.campaigns.preview(campaign.id);
|
|
169
|
+
|
|
170
|
+
// Send a test email
|
|
171
|
+
await client.campaigns.test(campaign.id, {
|
|
172
|
+
subscribers: ['test@example.com'],
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// Start the campaign
|
|
176
|
+
await client.campaigns.updateStatus(campaign.id, 'running');
|
|
177
|
+
|
|
178
|
+
// Get campaign analytics
|
|
179
|
+
const analytics = await client.campaigns.analytics('clicks', {
|
|
180
|
+
campaign_id: campaign.id,
|
|
181
|
+
});
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Transactional Email
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
await client.tx.send({
|
|
188
|
+
subscriber_email: 'user@example.com',
|
|
189
|
+
template_id: 1,
|
|
190
|
+
data: {
|
|
191
|
+
order_id: '12345',
|
|
192
|
+
items: ['Widget A', 'Widget B'],
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Lists
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
// Create a list
|
|
201
|
+
const list = await client.lists.create({
|
|
202
|
+
name: 'Newsletter Subscribers',
|
|
203
|
+
type: 'public',
|
|
204
|
+
optin: 'double',
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// Get all lists
|
|
208
|
+
const lists = await client.lists.list();
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Media
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
// Upload a file
|
|
215
|
+
const media = await client.media.upload({
|
|
216
|
+
file: new File([buffer], 'image.png', { type: 'image/png' }),
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// List all media
|
|
220
|
+
const files = await client.media.list();
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Configuration
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
const client = new Listmonk({
|
|
227
|
+
// Required
|
|
228
|
+
url: 'https://listmonk.example.com',
|
|
229
|
+
auth: {
|
|
230
|
+
username: 'your_username',
|
|
231
|
+
password: 'your_password',
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
// Optional
|
|
235
|
+
timeout: 30000, // Request timeout in ms (default: 30000)
|
|
236
|
+
maxRetries: 2, // Max retry attempts (default: 2)
|
|
237
|
+
debug: false, // Enable debug logging (default: false)
|
|
238
|
+
fetch: customFetch, // Custom fetch implementation
|
|
239
|
+
});
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Configuration Options
|
|
243
|
+
|
|
244
|
+
| Option | Type | Default | Description |
|
|
245
|
+
|--------|------|---------|-------------|
|
|
246
|
+
| `url` | `string` | — | listmonk server URL (required) |
|
|
247
|
+
| `auth.username` | `string` | — | API username (required) |
|
|
248
|
+
| `auth.password` | `string` | — | API password (required) |
|
|
249
|
+
| `timeout` | `number` | `30000` | Request timeout in milliseconds |
|
|
250
|
+
| `maxRetries` | `number` | `2` | Maximum retry attempts for failed requests |
|
|
251
|
+
| `debug` | `boolean` | `false` | Enable debug logging (credentials redacted) |
|
|
252
|
+
| `fetch` | `typeof fetch` | `globalThis.fetch` | Custom fetch implementation |
|
|
253
|
+
|
|
254
|
+
## Error Handling
|
|
255
|
+
|
|
256
|
+
The SDK provides a typed error hierarchy for granular error handling:
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
import {
|
|
260
|
+
Listmonk,
|
|
261
|
+
ListmonkError,
|
|
262
|
+
ListmonkApiError,
|
|
263
|
+
ListmonkAuthenticationError,
|
|
264
|
+
ListmonkNotFoundError,
|
|
265
|
+
ListmonkRateLimitError,
|
|
266
|
+
ListmonkValidationError,
|
|
267
|
+
ListmonkNetworkError,
|
|
268
|
+
} from '@solytude/listmonk';
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
await client.subscribers.get(999999);
|
|
272
|
+
} catch (error) {
|
|
273
|
+
if (error instanceof ListmonkNotFoundError) {
|
|
274
|
+
console.error('Subscriber not found:', error.message);
|
|
275
|
+
} else if (error instanceof ListmonkAuthenticationError) {
|
|
276
|
+
console.error('Invalid credentials:', error.message);
|
|
277
|
+
} else if (error instanceof ListmonkRateLimitError) {
|
|
278
|
+
console.error('Rate limited. Retry after:', error.retryAfter);
|
|
279
|
+
} else if (error instanceof ListmonkValidationError) {
|
|
280
|
+
console.error('Validation failed:', error.issues);
|
|
281
|
+
} else if (error instanceof ListmonkApiError) {
|
|
282
|
+
console.error('API error:', error.status, error.message);
|
|
283
|
+
} else if (error instanceof ListmonkNetworkError) {
|
|
284
|
+
console.error('Network error:', error.message);
|
|
285
|
+
} else {
|
|
286
|
+
throw error;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Error Types
|
|
292
|
+
|
|
293
|
+
| Error Class | HTTP Status | Description |
|
|
294
|
+
|-------------|-------------|-------------|
|
|
295
|
+
| `ListmonkBadRequestError` | 400 | Invalid request parameters |
|
|
296
|
+
| `ListmonkAuthenticationError` | 401 | Invalid or missing credentials |
|
|
297
|
+
| `ListmonkPermissionDeniedError` | 403 | Insufficient permissions |
|
|
298
|
+
| `ListmonkNotFoundError` | 404 | Resource not found |
|
|
299
|
+
| `ListmonkRateLimitError` | 429 | Too many requests |
|
|
300
|
+
| `ListmonkInternalServerError` | 5xx | Server-side error |
|
|
301
|
+
| `ListmonkValidationError` | — | Zod schema validation failed |
|
|
302
|
+
| `ListmonkNetworkError` | — | Connection or timeout error |
|
|
303
|
+
| `ListmonkConfigurationError` | — | Invalid client configuration |
|
|
304
|
+
|
|
305
|
+
## Pagination
|
|
306
|
+
|
|
307
|
+
### Async Iteration (Recommended)
|
|
308
|
+
|
|
309
|
+
Iterate through all results automatically:
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
for await (const subscriber of client.subscribers.listAll()) {
|
|
313
|
+
console.log(subscriber.email);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// With filtering
|
|
317
|
+
for await (const subscriber of client.subscribers.listAll({
|
|
318
|
+
query: 'subscribers.status = \'enabled\'',
|
|
319
|
+
})) {
|
|
320
|
+
console.log(subscriber.email);
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Manual Pagination
|
|
325
|
+
|
|
326
|
+
Control pagination manually when needed:
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
let page = 1;
|
|
330
|
+
let hasMore = true;
|
|
331
|
+
|
|
332
|
+
while (hasMore) {
|
|
333
|
+
const response = await client.subscribers.list({
|
|
334
|
+
page,
|
|
335
|
+
per_page: 100,
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
for (const subscriber of response.data.results) {
|
|
339
|
+
console.log(subscriber.email);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
hasMore = page * 100 < response.data.total;
|
|
343
|
+
page++;
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
## TypeScript
|
|
348
|
+
|
|
349
|
+
The SDK is written in TypeScript and provides comprehensive type definitions. Types are inferred from Zod schemas, ensuring runtime validation matches compile-time types.
|
|
350
|
+
|
|
351
|
+
### Importing Types
|
|
352
|
+
|
|
353
|
+
```typescript
|
|
354
|
+
import type {
|
|
355
|
+
Subscriber,
|
|
356
|
+
List,
|
|
357
|
+
Campaign,
|
|
358
|
+
Template,
|
|
359
|
+
CreateSubscriberParams,
|
|
360
|
+
UpdateCampaignParams,
|
|
361
|
+
} from '@solytude/listmonk';
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### Using Schemas
|
|
365
|
+
|
|
366
|
+
Zod schemas are exported for custom validation or extension:
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
import { SubscriberSchema, CreateSubscriberSchema } from '@solytude/listmonk';
|
|
370
|
+
|
|
371
|
+
// Validate external data
|
|
372
|
+
const subscriber = SubscriberSchema.parse(externalData);
|
|
373
|
+
|
|
374
|
+
// Extend schemas
|
|
375
|
+
const CustomSubscriberSchema = SubscriberSchema.extend({
|
|
376
|
+
customField: z.string(),
|
|
377
|
+
});
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
## Testing
|
|
381
|
+
|
|
382
|
+
The SDK provides test utilities for unit testing applications without real API calls:
|
|
383
|
+
|
|
384
|
+
```bash
|
|
385
|
+
# The testing module is included in the main package
|
|
386
|
+
import { MockListmonkClient } from '@solytude/listmonk/testing';
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### MockListmonkClient
|
|
390
|
+
|
|
391
|
+
The `MockListmonkClient` provides a test-runner-agnostic mock implementation:
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
import { MockListmonkClient, createMockSubscriber } from '@solytude/listmonk/testing';
|
|
395
|
+
|
|
396
|
+
describe('MyApp', () => {
|
|
397
|
+
let mockClient: MockListmonkClient;
|
|
398
|
+
|
|
399
|
+
beforeEach(() => {
|
|
400
|
+
mockClient = new MockListmonkClient();
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
afterEach(() => {
|
|
404
|
+
mockClient.reset(); // Clears all mock configurations and call history
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
it('fetches subscribers', async () => {
|
|
408
|
+
// Configure mock response
|
|
409
|
+
mockClient.subscribers.list.mockResolvedValue({
|
|
410
|
+
data: {
|
|
411
|
+
results: [createMockSubscriber({ email: 'test@example.com' })],
|
|
412
|
+
total: 1,
|
|
413
|
+
per_page: 100,
|
|
414
|
+
page: 1,
|
|
415
|
+
},
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// Use in your application code
|
|
419
|
+
const result = await mockClient.subscribers.list();
|
|
420
|
+
|
|
421
|
+
// Verify behavior
|
|
422
|
+
expect(result.data.results[0].email).toBe('test@example.com');
|
|
423
|
+
expect(mockClient.subscribers.list.callCount).toBe(1);
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
it('handles errors', async () => {
|
|
427
|
+
mockClient.subscribers.get.mockRejectedValue(new Error('Not found'));
|
|
428
|
+
|
|
429
|
+
await expect(mockClient.subscribers.get(999)).rejects.toThrow('Not found');
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### Mock Call Tracking
|
|
435
|
+
|
|
436
|
+
All mock methods track calls for assertions:
|
|
437
|
+
|
|
438
|
+
```typescript
|
|
439
|
+
await mockClient.subscribers.create({ email: 'user@example.com', lists: [1] });
|
|
440
|
+
|
|
441
|
+
expect(mockClient.subscribers.create.callCount).toBe(1);
|
|
442
|
+
expect(mockClient.subscribers.create.lastCall).toEqual([
|
|
443
|
+
{ email: 'user@example.com', lists: [1] }
|
|
444
|
+
]);
|
|
445
|
+
expect(mockClient.subscribers.create.calls).toHaveLength(1);
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### Async Iterator Mocking
|
|
449
|
+
|
|
450
|
+
Mock `listAll()` methods that return async iterators:
|
|
451
|
+
|
|
452
|
+
```typescript
|
|
453
|
+
mockClient.subscribers.listAll.mockAsyncIterator([
|
|
454
|
+
createMockSubscriber({ id: 1 }),
|
|
455
|
+
createMockSubscriber({ id: 2 }),
|
|
456
|
+
createMockSubscriber({ id: 3 }),
|
|
457
|
+
]);
|
|
458
|
+
|
|
459
|
+
const subscribers = [];
|
|
460
|
+
for await (const sub of mockClient.subscribers.listAll()) {
|
|
461
|
+
subscribers.push(sub);
|
|
462
|
+
}
|
|
463
|
+
expect(subscribers).toHaveLength(3);
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
### Factory Functions
|
|
467
|
+
|
|
468
|
+
Create realistic mock data with factory functions:
|
|
469
|
+
|
|
470
|
+
```typescript
|
|
471
|
+
import {
|
|
472
|
+
createMockSubscriber,
|
|
473
|
+
createMockSubscribers,
|
|
474
|
+
createMockList,
|
|
475
|
+
createMockCampaign,
|
|
476
|
+
createMockTemplate,
|
|
477
|
+
createMockMedia,
|
|
478
|
+
createMockBounce,
|
|
479
|
+
resetFactories,
|
|
480
|
+
} from '@solytude/listmonk/testing';
|
|
481
|
+
|
|
482
|
+
// Create single mock entities
|
|
483
|
+
const subscriber = createMockSubscriber({ email: 'custom@example.com' });
|
|
484
|
+
const list = createMockList({ name: 'Newsletter', type: 'private' });
|
|
485
|
+
const campaign = createMockCampaign({ status: 'running' });
|
|
486
|
+
|
|
487
|
+
// Create multiple mock entities
|
|
488
|
+
const subscribers = createMockSubscribers(5); // 5 unique subscribers
|
|
489
|
+
const lists = createMockLists(3, { type: 'private' }); // 3 private lists
|
|
490
|
+
|
|
491
|
+
// Reset factory counters (call in beforeEach for predictable IDs)
|
|
492
|
+
beforeEach(() => {
|
|
493
|
+
resetFactories();
|
|
494
|
+
});
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
### Custom Fetch for Testing
|
|
498
|
+
|
|
499
|
+
Inject a custom fetch function for fine-grained control:
|
|
500
|
+
|
|
501
|
+
```typescript
|
|
502
|
+
const mockFetch = vi.fn().mockResolvedValue(
|
|
503
|
+
new Response(JSON.stringify({ data: { id: 1 } }), {
|
|
504
|
+
status: 200,
|
|
505
|
+
headers: { 'Content-Type': 'application/json' },
|
|
506
|
+
})
|
|
507
|
+
);
|
|
508
|
+
|
|
509
|
+
const client = new Listmonk({
|
|
510
|
+
url: 'https://test.listmonk.app',
|
|
511
|
+
auth: { username: 'test', password: 'test' },
|
|
512
|
+
fetch: mockFetch,
|
|
513
|
+
});
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
## Contributing
|
|
517
|
+
|
|
518
|
+
Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.
|
|
519
|
+
|
|
520
|
+
### Development
|
|
521
|
+
|
|
522
|
+
```bash
|
|
523
|
+
# Install dependencies
|
|
524
|
+
bun install
|
|
525
|
+
|
|
526
|
+
# Run tests
|
|
527
|
+
bun test
|
|
528
|
+
|
|
529
|
+
# Type check
|
|
530
|
+
bun run typecheck
|
|
531
|
+
|
|
532
|
+
# Lint
|
|
533
|
+
bun run check
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
## License
|
|
537
|
+
|
|
538
|
+
This project is licensed under the MIT License — see the [LICENSE](LICENSE) file for details.
|
|
539
|
+
|
|
540
|
+
---
|
|
541
|
+
|
|
542
|
+
**[listmonk](https://listmonk.app/)** is a free and open source, high performance, self-hosted newsletter and mailing list manager.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, {
|
|
5
|
+
get: all[name],
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
set: (newValue) => all[name] = () => newValue
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export { __export };
|
|
13
|
+
|
|
14
|
+
//# debugId=934851CB66A4F95F64756E2164756E21
|