@ewyn/client 0.1.0 → 0.3.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.
@@ -0,0 +1,428 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { Ewyn, EwynApiError } from '../index.js';
3
+ // Mock fetch globally
4
+ global.fetch = vi.fn();
5
+ describe('Ewyn SDK', () => {
6
+ let client;
7
+ beforeEach(() => {
8
+ vi.clearAllMocks();
9
+ global.fetch.mockReset();
10
+ client = new Ewyn({
11
+ workspaceId: 'test-workspace-id',
12
+ apiKey: 'test-api-key',
13
+ maxRetries: 2, // Allow 1 retry (attempt < maxRetries)
14
+ timeout: 5000,
15
+ });
16
+ });
17
+ describe('constructor', () => {
18
+ it('should create client with required options', () => {
19
+ expect(client).toBeInstanceOf(Ewyn);
20
+ });
21
+ it('should use default base URL if not provided', () => {
22
+ const defaultClient = new Ewyn({
23
+ workspaceId: 'test-workspace-id',
24
+ apiKey: 'test-api-key',
25
+ });
26
+ expect(defaultClient).toBeInstanceOf(Ewyn);
27
+ });
28
+ it('should accept template config', () => {
29
+ const config = {
30
+ welcome: {
31
+ id: 'version-uuid',
32
+ name: 'Welcome',
33
+ version: 1,
34
+ vars: {
35
+ firstName: { required: true },
36
+ },
37
+ },
38
+ };
39
+ const configClient = new Ewyn({
40
+ workspaceId: 'test-workspace-id',
41
+ apiKey: 'test-api-key',
42
+ templates: config,
43
+ });
44
+ expect(configClient).toBeInstanceOf(Ewyn);
45
+ });
46
+ });
47
+ describe('send', () => {
48
+ it('should send email with template ID', async () => {
49
+ const mockResponse = {
50
+ messageId: 'msg-123',
51
+ status: 'queued',
52
+ queuedAt: '2024-01-01T00:00:00Z',
53
+ };
54
+ global.fetch.mockResolvedValueOnce({
55
+ ok: true,
56
+ status: 200,
57
+ json: async () => mockResponse,
58
+ });
59
+ const result = await client.send({
60
+ to: 'test@example.com',
61
+ templateId: 'template-uuid',
62
+ variables: {
63
+ firstName: 'John',
64
+ },
65
+ });
66
+ expect(result).toEqual(mockResponse);
67
+ expect(global.fetch).toHaveBeenCalledWith('https://www.ewyn.ai/api/v1/workspaces/test-workspace-id/send', expect.objectContaining({
68
+ method: 'POST',
69
+ headers: expect.objectContaining({
70
+ 'Content-Type': 'application/json',
71
+ 'Authorization': 'Bearer test-api-key',
72
+ }),
73
+ body: JSON.stringify({
74
+ to: 'test@example.com',
75
+ templateId: 'template-uuid',
76
+ variables: {
77
+ firstName: 'John',
78
+ },
79
+ }),
80
+ }));
81
+ });
82
+ it('should send email with template name when config provided', async () => {
83
+ const config = {
84
+ welcome: {
85
+ id: 'version-uuid',
86
+ name: 'Welcome',
87
+ version: 1,
88
+ vars: {
89
+ firstName: { required: true },
90
+ },
91
+ },
92
+ };
93
+ const configClient = new Ewyn({
94
+ workspaceId: 'test-workspace-id',
95
+ apiKey: 'test-api-key',
96
+ templates: config,
97
+ });
98
+ const mockResponse = {
99
+ messageId: 'msg-123',
100
+ status: 'queued',
101
+ queuedAt: '2024-01-01T00:00:00Z',
102
+ };
103
+ global.fetch.mockResolvedValueOnce({
104
+ ok: true,
105
+ status: 200,
106
+ json: async () => mockResponse,
107
+ });
108
+ const result = await configClient.send({
109
+ to: 'test@example.com',
110
+ template: 'welcome',
111
+ variables: {
112
+ firstName: 'John',
113
+ },
114
+ });
115
+ expect(result).toEqual(mockResponse);
116
+ });
117
+ it('should include version parameter if specified', async () => {
118
+ const mockResponse = {
119
+ messageId: 'msg-123',
120
+ status: 'queued',
121
+ queuedAt: '2024-01-01T00:00:00Z',
122
+ };
123
+ global.fetch.mockResolvedValueOnce({
124
+ ok: true,
125
+ status: 200,
126
+ json: async () => mockResponse,
127
+ });
128
+ await client.send({
129
+ to: 'test@example.com',
130
+ templateId: 'template-uuid',
131
+ version: 2,
132
+ variables: {
133
+ firstName: 'John',
134
+ },
135
+ });
136
+ expect(global.fetch).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
137
+ body: expect.stringContaining('"version":2'),
138
+ }));
139
+ });
140
+ it('should include metadata if provided', async () => {
141
+ const mockResponse = {
142
+ messageId: 'msg-123',
143
+ status: 'queued',
144
+ queuedAt: '2024-01-01T00:00:00Z',
145
+ };
146
+ global.fetch.mockResolvedValueOnce({
147
+ ok: true,
148
+ status: 200,
149
+ json: async () => mockResponse,
150
+ });
151
+ await client.send({
152
+ to: 'test@example.com',
153
+ templateId: 'template-uuid',
154
+ metadata: {
155
+ userId: 'user-123',
156
+ },
157
+ });
158
+ expect(global.fetch).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
159
+ body: expect.stringContaining('"metadata"'),
160
+ }));
161
+ });
162
+ it('should include idempotency key if provided', async () => {
163
+ const mockResponse = {
164
+ messageId: 'msg-123',
165
+ status: 'queued',
166
+ queuedAt: '2024-01-01T00:00:00Z',
167
+ };
168
+ global.fetch.mockResolvedValueOnce({
169
+ ok: true,
170
+ status: 200,
171
+ json: async () => mockResponse,
172
+ });
173
+ await client.send({
174
+ to: 'test@example.com',
175
+ templateId: 'template-uuid',
176
+ idempotencyKey: 'idempotency-key-123',
177
+ });
178
+ expect(global.fetch).toHaveBeenCalledWith(expect.any(String), expect.objectContaining({
179
+ body: expect.stringContaining('"idempotencyKey":"idempotency-key-123"'),
180
+ }));
181
+ });
182
+ it('should throw error if neither templateId nor template provided', async () => {
183
+ await expect(client.send({
184
+ to: 'test@example.com',
185
+ variables: {},
186
+ })).rejects.toThrow('Either templateId or template must be provided');
187
+ });
188
+ it('should throw error if template name provided without config', async () => {
189
+ await expect(client.send({
190
+ to: 'test@example.com',
191
+ template: 'welcome',
192
+ variables: {},
193
+ })).rejects.toThrow('Template name provided but no template config was provided');
194
+ });
195
+ it('should throw error if template not found in config', async () => {
196
+ const config = {
197
+ welcome: {
198
+ id: 'version-uuid',
199
+ name: 'Welcome',
200
+ version: 1,
201
+ vars: {},
202
+ },
203
+ };
204
+ const configClient = new Ewyn({
205
+ workspaceId: 'test-workspace-id',
206
+ apiKey: 'test-api-key',
207
+ templates: config,
208
+ });
209
+ await expect(configClient.send({
210
+ to: 'test@example.com',
211
+ template: 'nonexistent',
212
+ variables: {},
213
+ })).rejects.toThrow('Template "nonexistent" not found in template config');
214
+ });
215
+ });
216
+ describe('variable validation', () => {
217
+ it('should validate required variables', async () => {
218
+ const config = {
219
+ welcome: {
220
+ id: 'version-uuid',
221
+ name: 'Welcome',
222
+ version: 1,
223
+ vars: {
224
+ firstName: { required: true },
225
+ lastName: { required: true },
226
+ },
227
+ },
228
+ };
229
+ const configClient = new Ewyn({
230
+ workspaceId: 'test-workspace-id',
231
+ apiKey: 'test-api-key',
232
+ templates: config,
233
+ });
234
+ await expect(configClient.send({
235
+ to: 'test@example.com',
236
+ template: 'welcome',
237
+ variables: {
238
+ firstName: 'John',
239
+ // lastName missing
240
+ },
241
+ })).rejects.toThrow('Missing required variables for template "welcome": lastName');
242
+ });
243
+ it('should allow optional variables to be omitted', async () => {
244
+ const config = {
245
+ welcome: {
246
+ id: 'version-uuid',
247
+ name: 'Welcome',
248
+ version: 1,
249
+ vars: {
250
+ firstName: { required: true },
251
+ lastName: { required: false },
252
+ },
253
+ },
254
+ };
255
+ const configClient = new Ewyn({
256
+ workspaceId: 'test-workspace-id',
257
+ apiKey: 'test-api-key',
258
+ templates: config,
259
+ });
260
+ const mockResponse = {
261
+ messageId: 'msg-123',
262
+ status: 'queued',
263
+ queuedAt: '2024-01-01T00:00:00Z',
264
+ };
265
+ global.fetch.mockResolvedValueOnce({
266
+ ok: true,
267
+ status: 200,
268
+ json: async () => mockResponse,
269
+ });
270
+ const result = await configClient.send({
271
+ to: 'test@example.com',
272
+ template: 'welcome',
273
+ variables: {
274
+ firstName: 'John',
275
+ // lastName omitted (optional)
276
+ },
277
+ });
278
+ expect(result).toEqual(mockResponse);
279
+ });
280
+ });
281
+ describe('error handling', () => {
282
+ it('should throw EwynApiError on 400 error', async () => {
283
+ const mock400Response = {
284
+ ok: false,
285
+ status: 400,
286
+ json: async () => ({
287
+ error: 'Bad request',
288
+ code: 'INVALID_REQUEST',
289
+ details: { field: 'to' },
290
+ }),
291
+ };
292
+ global.fetch
293
+ .mockResolvedValueOnce(mock400Response)
294
+ .mockResolvedValueOnce(mock400Response);
295
+ await expect(client.send({
296
+ to: 'test@example.com',
297
+ templateId: 'template-uuid',
298
+ })).rejects.toThrow(EwynApiError);
299
+ try {
300
+ await client.send({
301
+ to: 'test@example.com',
302
+ templateId: 'template-uuid',
303
+ });
304
+ }
305
+ catch (error) {
306
+ expect(error).toBeInstanceOf(EwynApiError);
307
+ expect(error.status).toBe(400);
308
+ expect(error.code).toBe('INVALID_REQUEST');
309
+ expect(error.isClientError()).toBe(true);
310
+ expect(error.isServerError()).toBe(false);
311
+ expect(error.isRetryable()).toBe(false);
312
+ }
313
+ });
314
+ it('should throw EwynApiError on 429 rate limit', async () => {
315
+ global.fetch.mockResolvedValue({
316
+ ok: false,
317
+ status: 429,
318
+ json: async () => ({
319
+ error: 'Too many requests',
320
+ code: 'RATE_LIMITED',
321
+ }),
322
+ });
323
+ try {
324
+ await client.send({
325
+ to: 'test@example.com',
326
+ templateId: 'template-uuid',
327
+ });
328
+ }
329
+ catch (error) {
330
+ expect(error).toBeInstanceOf(EwynApiError);
331
+ expect(error.status).toBe(429);
332
+ expect(error.isRetryable()).toBe(true);
333
+ }
334
+ // Should have retried once (maxRetries: 1)
335
+ expect(global.fetch).toHaveBeenCalledTimes(2);
336
+ });
337
+ it('should throw EwynApiError on 500 server error', async () => {
338
+ global.fetch.mockResolvedValue({
339
+ ok: false,
340
+ status: 500,
341
+ json: async () => ({
342
+ error: 'Internal server error',
343
+ }),
344
+ });
345
+ try {
346
+ await client.send({
347
+ to: 'test@example.com',
348
+ templateId: 'template-uuid',
349
+ });
350
+ }
351
+ catch (error) {
352
+ expect(error).toBeInstanceOf(EwynApiError);
353
+ expect(error.status).toBe(500);
354
+ expect(error.isServerError()).toBe(true);
355
+ expect(error.isRetryable()).toBe(true);
356
+ }
357
+ // Should have retried once
358
+ expect(global.fetch).toHaveBeenCalledTimes(2);
359
+ });
360
+ it('should handle network errors', async () => {
361
+ global.fetch.mockRejectedValue(new Error('Network error'));
362
+ await expect(client.send({
363
+ to: 'test@example.com',
364
+ templateId: 'template-uuid',
365
+ })).rejects.toThrow('Network error');
366
+ // Should have retried once
367
+ expect(global.fetch).toHaveBeenCalledTimes(2);
368
+ });
369
+ it('should handle timeout errors', async () => {
370
+ const abortError = new Error('The operation was aborted');
371
+ abortError.name = 'AbortError';
372
+ global.fetch.mockRejectedValueOnce(abortError);
373
+ try {
374
+ await client.send({
375
+ to: 'test@example.com',
376
+ templateId: 'template-uuid',
377
+ });
378
+ }
379
+ catch (error) {
380
+ expect(error).toBeInstanceOf(EwynApiError);
381
+ expect(error.status).toBe(408);
382
+ expect(error.code).toBe('TIMEOUT');
383
+ }
384
+ });
385
+ });
386
+ describe('retry logic', () => {
387
+ it('should retry on 5xx errors with exponential backoff', async () => {
388
+ const startTime = Date.now();
389
+ global.fetch
390
+ .mockResolvedValueOnce({
391
+ ok: false,
392
+ status: 500,
393
+ json: async () => ({ error: 'Server error' }),
394
+ })
395
+ .mockResolvedValueOnce({
396
+ ok: true,
397
+ status: 200,
398
+ json: async () => ({
399
+ messageId: 'msg-123',
400
+ status: 'queued',
401
+ queuedAt: '2024-01-01T00:00:00Z',
402
+ }),
403
+ });
404
+ const result = await client.send({
405
+ to: 'test@example.com',
406
+ templateId: 'template-uuid',
407
+ });
408
+ const duration = Date.now() - startTime;
409
+ expect(result.messageId).toBe('msg-123');
410
+ expect(global.fetch).toHaveBeenCalledTimes(2);
411
+ // Should have waited ~1 second between attempts
412
+ expect(duration).toBeGreaterThanOrEqual(950);
413
+ });
414
+ it('should not retry on 4xx client errors', async () => {
415
+ global.fetch.mockResolvedValueOnce({
416
+ ok: false,
417
+ status: 404,
418
+ json: async () => ({ error: 'Not found' }),
419
+ });
420
+ await expect(client.send({
421
+ to: 'test@example.com',
422
+ templateId: 'template-uuid',
423
+ })).rejects.toThrow(EwynApiError);
424
+ // Should not retry
425
+ expect(global.fetch).toHaveBeenCalledTimes(1);
426
+ });
427
+ });
428
+ });
package/dist/cli.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ /** Exported for testing. */
3
+ export declare function runFetchConfig(cwd: string): Promise<void>;
4
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AA+GA,4BAA4B;AAC5B,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqB/D"}
package/dist/cli.js ADDED
@@ -0,0 +1,140 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import { createRequire } from 'node:module';
4
+ import path from 'node:path';
5
+ import { pathToFileURL } from 'node:url';
6
+ const EWYN_API_BASE_URL = 'https://www.ewyn.ai/api/v1';
7
+ const CONFIG_NAMES = ['ewyn.config.ts', 'ewyn.config.mjs', 'ewyn.config.js'];
8
+ const DEFAULT_OUTPUT_FILE = 'ewynTemplates.ts';
9
+ function printUsage() {
10
+ console.error(`
11
+ Usage: ewyn <command>
12
+
13
+ Commands:
14
+ fetch-config Fetch template config from the API and write ewynTemplates.ts (or configurationPath from ewyn.config)
15
+
16
+ Config file (in current working directory):
17
+ Look for ewyn.config.ts, ewyn.config.mjs, or ewyn.config.js with default export:
18
+ { workspaceId: string, apiKey: string, configurationPath?: string }
19
+
20
+ Example ewyn.config.ts:
21
+ export default {
22
+ workspaceId: process.env.EWYN_WORKSPACE_ID!,
23
+ apiKey: process.env.EWYN_API_KEY!,
24
+ configurationPath: './src/ewynTemplates.ts', // optional
25
+ };
26
+ `);
27
+ }
28
+ function findConfigPath(cwd) {
29
+ for (const name of CONFIG_NAMES) {
30
+ const p = path.join(cwd, name);
31
+ if (fs.existsSync(p))
32
+ return p;
33
+ }
34
+ return null;
35
+ }
36
+ async function loadConfig(configPath) {
37
+ const ext = path.extname(configPath);
38
+ if (ext === '.ts') {
39
+ const require = createRequire(import.meta.url);
40
+ const { register } = require('tsx/cjs');
41
+ register();
42
+ const mod = require(configPath);
43
+ const config = mod?.default;
44
+ if (!config || typeof config !== 'object') {
45
+ throw new Error(`${configPath}: expected default export to be a config object`);
46
+ }
47
+ return config;
48
+ }
49
+ const mod = await import(pathToFileURL(configPath).href);
50
+ const config = mod?.default;
51
+ if (!config || typeof config !== 'object') {
52
+ throw new Error(`${configPath}: expected default export to be a config object`);
53
+ }
54
+ return config;
55
+ }
56
+ function validateConfig(config, configPath) {
57
+ if (!config.workspaceId || typeof config.workspaceId !== 'string') {
58
+ console.error(`Error: workspaceId is missing or invalid in ${configPath}. You can use process.env.EWYN_WORKSPACE_ID.`);
59
+ process.exit(1);
60
+ }
61
+ if (!config.apiKey || typeof config.apiKey !== 'string') {
62
+ console.error(`Error: apiKey is missing or invalid in ${configPath}. You can use process.env.EWYN_API_KEY.`);
63
+ process.exit(1);
64
+ }
65
+ }
66
+ async function fetchTemplates(workspaceId, apiKey) {
67
+ const url = `${EWYN_API_BASE_URL}/workspaces/${workspaceId}/templates/config`;
68
+ const response = await fetch(url, {
69
+ method: 'GET',
70
+ headers: {
71
+ Authorization: `Bearer ${apiKey}`,
72
+ },
73
+ });
74
+ if (!response.ok) {
75
+ const body = await response.text();
76
+ let details = body;
77
+ try {
78
+ const json = JSON.parse(body);
79
+ details = json.error ?? json.message ?? body;
80
+ }
81
+ catch {
82
+ // use raw body
83
+ }
84
+ console.error(`Error: API request failed (${response.status}): ${details}`);
85
+ process.exit(1);
86
+ }
87
+ const data = (await response.json());
88
+ const templates = data.templates ?? {};
89
+ return templates;
90
+ }
91
+ function serializeTemplates(templates) {
92
+ const lines = ['// Generated by @ewyn/client fetch-config. Do not edit by hand.', '', 'export const ewynTemplates = '];
93
+ const json = JSON.stringify(templates, null, 2);
94
+ // Ensure valid TS: escape any stray characters and add "as const"
95
+ lines.push(json + ' as const;');
96
+ return lines.join('\n');
97
+ }
98
+ /** Exported for testing. */
99
+ export async function runFetchConfig(cwd) {
100
+ const configPath = findConfigPath(cwd);
101
+ if (!configPath) {
102
+ console.error('Error: ewyn.config.ts (or ewyn.config.mjs / ewyn.config.js) not found in current directory.');
103
+ process.exit(1);
104
+ }
105
+ const config = await loadConfig(configPath);
106
+ validateConfig(config, configPath);
107
+ const outputPath = config.configurationPath
108
+ ? path.resolve(cwd, config.configurationPath)
109
+ : path.join(cwd, DEFAULT_OUTPUT_FILE);
110
+ const templates = await fetchTemplates(config.workspaceId, config.apiKey);
111
+ const dir = path.dirname(outputPath);
112
+ fs.mkdirSync(dir, { recursive: true });
113
+ fs.writeFileSync(outputPath, serializeTemplates(templates), 'utf-8');
114
+ console.log(`Wrote ${outputPath}`);
115
+ }
116
+ async function main() {
117
+ const args = process.argv.slice(2);
118
+ const command = args[0];
119
+ if (!command || command === '--help' || command === '-h') {
120
+ printUsage();
121
+ process.exit(0);
122
+ }
123
+ if (command === 'fetch-config') {
124
+ const cwd = process.cwd();
125
+ await runFetchConfig(cwd);
126
+ return;
127
+ }
128
+ console.error(`Error: unknown command "${command}".`);
129
+ printUsage();
130
+ process.exit(1);
131
+ }
132
+ const isMain = typeof process !== 'undefined' &&
133
+ process.argv[1] &&
134
+ pathToFileURL(process.argv[1]).href === import.meta.url;
135
+ if (isMain) {
136
+ main().catch((err) => {
137
+ console.error(err instanceof Error ? err.message : String(err));
138
+ process.exit(1);
139
+ });
140
+ }
package/dist/errors.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
- * API Error class for handling HTTP errors from the Mailer API
2
+ * API Error class for handling HTTP errors from the Ewyn API
3
3
  */
4
- export declare class MailerApiError extends Error {
4
+ export declare class EwynApiError extends Error {
5
5
  status: number;
6
6
  code?: string | undefined;
7
7
  details?: unknown | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,cAAe,SAAQ,KAAK;IAE9B,MAAM,EAAE,MAAM;IACd,IAAI,CAAC,EAAE,MAAM;IACb,OAAO,CAAC,EAAE,OAAO;gBAFjB,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,MAAM,YAAA,EACb,OAAO,CAAC,EAAE,OAAO,YAAA,EACxB,OAAO,CAAC,EAAE,MAAM;IAOlB;;OAEG;IACH,aAAa,IAAI,OAAO;IAIxB;;OAEG;IACH,aAAa,IAAI,OAAO;IAIxB;;OAEG;IACH,WAAW,IAAI,OAAO;CAGvB"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,YAAa,SAAQ,KAAK;IAE5B,MAAM,EAAE,MAAM;IACd,IAAI,CAAC,EAAE,MAAM;IACb,OAAO,CAAC,EAAE,OAAO;gBAFjB,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,MAAM,YAAA,EACb,OAAO,CAAC,EAAE,OAAO,YAAA,EACxB,OAAO,CAAC,EAAE,MAAM;IAOlB;;OAEG;IACH,aAAa,IAAI,OAAO;IAIxB;;OAEG;IACH,aAAa,IAAI,OAAO;IAIxB;;OAEG;IACH,WAAW,IAAI,OAAO;CAGvB"}
package/dist/errors.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
- * API Error class for handling HTTP errors from the Mailer API
2
+ * API Error class for handling HTTP errors from the Ewyn API
3
3
  */
4
- export class MailerApiError extends Error {
4
+ export class EwynApiError extends Error {
5
5
  status;
6
6
  code;
7
7
  details;
@@ -10,8 +10,8 @@ export class MailerApiError extends Error {
10
10
  this.status = status;
11
11
  this.code = code;
12
12
  this.details = details;
13
- this.name = 'MailerApiError';
14
- Object.setPrototypeOf(this, MailerApiError.prototype);
13
+ this.name = 'EwynApiError';
14
+ Object.setPrototypeOf(this, EwynApiError.prototype);
15
15
  }
16
16
  /**
17
17
  * Check if the error is a client error (4xx)
package/dist/index.d.ts CHANGED
@@ -1,65 +1,17 @@
1
- import type { MailerOptions, MailerOptionsTyped, SendEmailOptions, SendEmailOptionsTyped, SendEmailResponse, TemplateConfig } from './types.js';
2
- /**
3
- * Mailer SDK Client
4
- *
5
- * @example Basic usage without template config
6
- * ```ts
7
- * const client = new Mailer({
8
- * workspaceId: 'your-workspace-id',
9
- * apiKey: 'your-api-key',
10
- * });
11
- *
12
- * await client.send({
13
- * to: 'user@example.com',
14
- * templateId: 'version-uuid',
15
- * variables: { firstName: 'John' }
16
- * });
17
- * ```
18
- *
19
- * @example Type-safe usage with template config
20
- * ```ts
21
- * const config = {
22
- * welcome: {
23
- * id: 'version-uuid',
24
- * name: 'Welcome Email',
25
- * version: 1,
26
- * vars: {
27
- * firstName: { required: true },
28
- * plan: { required: false }
29
- * }
30
- * }
31
- * } as const;
32
- *
33
- * const client = new Mailer({
34
- * workspaceId: 'your-workspace-id',
35
- * apiKey: 'your-api-key',
36
- * templates: config
37
- * });
38
- *
39
- * // Type-safe sending
40
- * await client.send({
41
- * to: 'user@example.com',
42
- * template: 'welcome', // Autocomplete available
43
- * variables: {
44
- * firstName: 'John' // TypeScript enforces required vars
45
- * }
46
- * });
47
- * ```
48
- */
49
- export declare class Mailer<TConfig extends TemplateConfig = TemplateConfig> {
1
+ import type { EwynOptions, EwynOptionsTyped, SendEmailOptions, SendEmailOptionsTyped, SendEmailResponse, TemplateConfig } from './types.js';
2
+ export declare class Ewyn<TConfig extends TemplateConfig = TemplateConfig> {
50
3
  private readonly workspaceId;
51
4
  private readonly apiKey;
52
- private readonly baseUrl;
53
5
  private readonly templates?;
54
6
  private readonly maxRetries;
55
7
  private readonly timeout;
56
- constructor(options: MailerOptions | MailerOptionsTyped<TConfig>);
8
+ constructor(options: EwynOptions | EwynOptionsTyped<TConfig>);
57
9
  /**
58
10
  * Send an email using a template
59
11
  *
60
12
  * @param options - Email sending options
61
13
  * @returns Promise resolving to the send response
62
- * @throws {MailerApiError} If the API request fails
14
+ * @throws {EwynApiError} If the API request fails
63
15
  *
64
16
  * @example With template ID
65
17
  * ```ts
@@ -94,6 +46,6 @@ export declare class Mailer<TConfig extends TemplateConfig = TemplateConfig> {
94
46
  */
95
47
  private requestWithRetry;
96
48
  }
97
- export { MailerApiError } from './errors.js';
98
- export type { MailerOptions, MailerOptionsTyped, SendEmailOptions, SendEmailOptionsBase, SendEmailOptionsTyped, SendEmailResponse, TemplateConfig, TemplateConfigEntry, TemplateVariable } from './types.js';
49
+ export { EwynApiError } from './errors.js';
50
+ export type { EwynFetchConfig, EwynOptions, EwynOptionsTyped, SendEmailOptions, SendEmailOptionsBase, SendEmailOptionsTyped, SendEmailResponse, TemplateConfig, TemplateConfigEntry, TemplateVariable } from './types.js';
99
51
  //# sourceMappingURL=index.d.ts.map