@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.
package/README.md CHANGED
@@ -22,12 +22,11 @@ yarn add @ewyn/client
22
22
  ### Basic Usage
23
23
 
24
24
  ```typescript
25
- import { Mailer } from '@ewyn/client';
25
+ import { Ewyn } from '@ewyn/client';
26
26
 
27
- const client = new Mailer({
27
+ const client = new Ewyn({
28
28
  workspaceId: 'your-workspace-id',
29
29
  apiKey: 'your-api-key',
30
- baseUrl: 'https://www.ewyn.ai/api/v1', // optional; omit to use production default
31
30
  });
32
31
 
33
32
  // Send an email using template version ID
@@ -46,7 +45,7 @@ await client.send({
46
45
  Get full TypeScript autocomplete and validation by copying your template config from the dashboard:
47
46
 
48
47
  ```typescript
49
- import { Mailer } from '@ewyn/client';
48
+ import { Ewyn } from '@ewyn/client';
50
49
 
51
50
  // 1. Copy template config from dashboard (API Keys page)
52
51
  const config = {
@@ -71,7 +70,7 @@ const config = {
71
70
  } as const; // ← Important: use 'as const'
72
71
 
73
72
  // 2. Initialize with config
74
- const client = new Mailer({
73
+ const client = new Ewyn({
75
74
  workspaceId: 'your-workspace-id',
76
75
  apiKey: 'your-api-key',
77
76
  templates: config,
@@ -91,7 +90,58 @@ await client.send({
91
90
 
92
91
  ## Getting Your Template Configuration
93
92
 
94
- ### From Dashboard (Recommended)
93
+ You can get your template config in three ways:
94
+
95
+ 1. **CLI (recommended)** – Run `npx @ewyn/client fetch-config` to download config and generate `ewynTemplates.ts` (see [Fetching template config (CLI)](#fetching-template-config-cli)).
96
+ 2. **Dashboard** – Copy JSON from the API Keys page.
97
+ 3. **API** – Call the templates config endpoint with curl or your own code.
98
+
99
+ ### Fetching template config (CLI)
100
+
101
+ The easiest way to keep your template config in sync is to use the built-in CLI. From your project root (where you have an `ewyn.config.ts` or `ewyn.config.js`), run:
102
+
103
+ ```bash
104
+ npx @ewyn/client fetch-config
105
+ ```
106
+
107
+ This fetches your workspace’s template config from the API and writes a TypeScript file (e.g. `ewynTemplates.ts`) that you can import for type-safe sending.
108
+
109
+ **Config file** (`ewyn.config.ts` in project root):
110
+
111
+ - `workspaceId` (required): Your workspace UUID (e.g. `process.env.EWYN_WORKSPACE_ID`)
112
+ - `apiKey` (required): Your API key (e.g. `process.env.EWYN_API_KEY`)
113
+ - `configurationPath` (optional): Where to write the generated file (e.g. `./src/ewynTemplates.ts`). If omitted, the file is written as `./ewynTemplates.ts` in the current directory.
114
+
115
+ Example `ewyn.config.ts`:
116
+
117
+ ```typescript
118
+ import type { EwynFetchConfig } from '@ewyn/client';
119
+
120
+ const config: EwynFetchConfig = {
121
+ workspaceId: process.env.EWYN_WORKSPACE_ID!,
122
+ apiKey: process.env.EWYN_API_KEY!,
123
+ configurationPath: './src/ewynTemplates.ts', // optional
124
+ };
125
+
126
+ export default config;
127
+ ```
128
+
129
+ After running `fetch-config`, import the generated config and pass it to the client:
130
+
131
+ ```typescript
132
+ import { Ewyn } from '@ewyn/client';
133
+ import { ewynTemplates } from './ewynTemplates'; // or from your configurationPath
134
+
135
+ const client = new Ewyn({
136
+ workspaceId: process.env.EWYN_WORKSPACE_ID!,
137
+ apiKey: process.env.EWYN_API_KEY!,
138
+ templates: ewynTemplates,
139
+ });
140
+ ```
141
+
142
+ **Note:** Regenerate the config after creating or updating templates (re-run `npx @ewyn/client fetch-config`).
143
+
144
+ ### From Dashboard
95
145
 
96
146
  1. Go to your dashboard → **API Keys** page
97
147
  2. Scroll to **Template Configuration** section
@@ -103,7 +153,7 @@ const config = {
103
153
  // ... paste here
104
154
  } as const;
105
155
 
106
- const client = new Mailer({
156
+ const client = new Ewyn({
107
157
  workspaceId: 'your-workspace-id',
108
158
  apiKey: 'your-api-key',
109
159
  templates: config, // Now fully type-safe!
@@ -119,23 +169,20 @@ curl -X GET https://www.ewyn.ai/api/v1/workspaces/YOUR_WORKSPACE_ID/templates/co
119
169
  -H "Authorization: Bearer YOUR_API_KEY"
120
170
  ```
121
171
 
122
- **Note:** Regenerate the config after creating or updating templates to keep it in sync.
123
-
124
172
  ## API Reference
125
173
 
126
- ### `Mailer` Class
174
+ ### `Ewyn` Class
127
175
 
128
176
  #### Constructor
129
177
 
130
178
  ```typescript
131
- new Mailer(options: MailerOptions)
179
+ new Ewyn(options: EwynOptions)
132
180
  ```
133
181
 
134
182
  **Options:**
135
183
 
136
184
  - `workspaceId` (string, required): Your workspace UUID
137
185
  - `apiKey` (string, required): Your API key secret
138
- - `baseUrl` (string, optional): Base URL for the API (defaults to `https://www.ewyn.ai/api/v1`)
139
186
  - `templates` (TemplateConfig, optional): Template configuration for name-based sending
140
187
  - `maxRetries` (number, optional): Maximum retries for retryable errors (default: 3)
141
188
  - `timeout` (number, optional): Request timeout in milliseconds (default: 30000)
@@ -168,20 +215,20 @@ Send an email using a template.
168
215
 
169
216
  **Throws:**
170
217
 
171
- - `MailerApiError`: If the API request fails
218
+ - `EwynApiError`: If the API request fails
172
219
  - `Error`: If validation fails (missing template, missing variables, etc.)
173
220
 
174
221
  ### Error Handling
175
222
 
176
- The SDK throws `MailerApiError` for API errors:
223
+ The SDK throws `EwynApiError` for API errors:
177
224
 
178
225
  ```typescript
179
- import { Mailer, MailerApiError } from '@ewyn/client';
226
+ import { Ewyn, EwynApiError } from '@ewyn/client';
180
227
 
181
228
  try {
182
229
  await client.send({ /* ... */ });
183
230
  } catch (error) {
184
- if (error instanceof MailerApiError) {
231
+ if (error instanceof EwynApiError) {
185
232
  console.error('API Error:', error.status, error.message);
186
233
  console.error('Details:', error.details);
187
234
 
@@ -235,6 +282,7 @@ Check out the [examples directory](./examples/) for comprehensive examples:
235
282
 
236
283
  - [Basic send](./examples/basic-send.ts) - Simple sending with template ID
237
284
  - [Type-safe usage](./examples/with-template-config.ts) - Full type safety with template config
285
+ - [ewyn.config example](./examples/ewyn.config.example.ts) - Example config for `fetch-config` CLI
238
286
  - [Error handling](./examples/error-handling.ts) - Comprehensive error handling patterns
239
287
  - [Idempotency](./examples/idempotency.ts) - Preventing duplicate sends
240
288
  - [Advanced patterns](./examples/advanced-usage.ts) - Batch sending, retries, and more
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cli-fetch-config.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-fetch-config.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/cli-fetch-config.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,70 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
5
+ import { runFetchConfig } from '../cli.js';
6
+ const MOCK_TEMPLATES = {
7
+ welcome: {
8
+ id: 'version-uuid-1',
9
+ name: 'Welcome Email',
10
+ version: 1,
11
+ vars: {
12
+ firstName: { required: true },
13
+ lastName: { required: false },
14
+ },
15
+ },
16
+ };
17
+ describe('fetch-config CLI', () => {
18
+ let tempDir;
19
+ let originalFetch;
20
+ beforeEach(() => {
21
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ewyn-cli-test-'));
22
+ originalFetch = globalThis.fetch;
23
+ globalThis.fetch = vi.fn().mockResolvedValue({
24
+ ok: true,
25
+ json: async () => ({ templates: MOCK_TEMPLATES }),
26
+ text: async () => '',
27
+ });
28
+ });
29
+ afterEach(() => {
30
+ globalThis.fetch = originalFetch;
31
+ vi.restoreAllMocks();
32
+ try {
33
+ fs.rmSync(tempDir, { recursive: true });
34
+ }
35
+ catch {
36
+ // ignore
37
+ }
38
+ });
39
+ it('writes ewynTemplates.ts with fetched config when using default path', async () => {
40
+ const configPath = path.join(tempDir, 'ewyn.config.js');
41
+ fs.writeFileSync(configPath, `export default {
42
+ workspaceId: 'test-workspace-id',
43
+ apiKey: 'test-api-key',
44
+ };
45
+ `, 'utf-8');
46
+ await runFetchConfig(tempDir);
47
+ const outputPath = path.join(tempDir, 'ewynTemplates.ts');
48
+ expect(fs.existsSync(outputPath)).toBe(true);
49
+ const content = fs.readFileSync(outputPath, 'utf-8');
50
+ expect(content).toContain('// Generated by @ewyn/client fetch-config');
51
+ expect(content).toContain('export const ewynTemplates = ');
52
+ expect(content).toContain('"welcome"');
53
+ expect(content).toContain('"version-uuid-1"');
54
+ expect(content).toContain(' as const;');
55
+ });
56
+ it('writes to configurationPath when set in config', async () => {
57
+ const configPath = path.join(tempDir, 'ewyn.config.js');
58
+ fs.writeFileSync(configPath, `export default {
59
+ workspaceId: 'ws-1',
60
+ apiKey: 'key-1',
61
+ configurationPath: './src/ewynTemplates.ts',
62
+ };
63
+ `, 'utf-8');
64
+ await runFetchConfig(tempDir);
65
+ const outputPath = path.join(tempDir, 'src', 'ewynTemplates.ts');
66
+ expect(fs.existsSync(outputPath)).toBe(true);
67
+ const content = fs.readFileSync(outputPath, 'utf-8');
68
+ expect(content).toContain('"welcome"');
69
+ });
70
+ });
@@ -7,7 +7,7 @@
7
7
  * variables at compile time.
8
8
  */
9
9
  import { expectTypeOf, test } from 'vitest';
10
- import { Mailer } from '../index.js';
10
+ import { Ewyn } from '../index.js';
11
11
  // Dashboard-shaped config: same structure as getConfig response (id, name, version, vars)
12
12
  const dashboardConfig = {
13
13
  welcome: {
@@ -29,7 +29,7 @@ const dashboardConfig = {
29
29
  },
30
30
  };
31
31
  test('client with dashboard config has typed send', () => {
32
- const client = new Mailer({
32
+ const client = new Ewyn({
33
33
  workspaceId: 'ws',
34
34
  apiKey: 'key',
35
35
  templates: dashboardConfig,
@@ -38,7 +38,7 @@ test('client with dashboard config has typed send', () => {
38
38
  expectTypeOf(client.send).toBeFunction();
39
39
  });
40
40
  test('valid send: required var present, optional omitted', () => {
41
- const client = new Mailer({
41
+ const client = new Ewyn({
42
42
  workspaceId: 'ws',
43
43
  apiKey: 'key',
44
44
  templates: dashboardConfig,
@@ -50,7 +50,7 @@ test('valid send: required var present, optional omitted', () => {
50
50
  }).toMatchTypeOf();
51
51
  });
52
52
  test('valid send: required and optional vars', () => {
53
- const client = new Mailer({
53
+ const client = new Ewyn({
54
54
  workspaceId: 'ws',
55
55
  apiKey: 'key',
56
56
  templates: dashboardConfig,
@@ -62,7 +62,7 @@ test('valid send: required and optional vars', () => {
62
62
  }).toMatchTypeOf();
63
63
  });
64
64
  test('valid send: hyphenated template name', () => {
65
- const client = new Mailer({
65
+ const client = new Ewyn({
66
66
  workspaceId: 'ws',
67
67
  apiKey: 'key',
68
68
  templates: dashboardConfig,
@@ -74,7 +74,7 @@ test('valid send: hyphenated template name', () => {
74
74
  }).toMatchTypeOf();
75
75
  });
76
76
  test('send return type is Promise<SendEmailResponse>', () => {
77
- const client = new Mailer({
77
+ const client = new Ewyn({
78
78
  workspaceId: 'ws',
79
79
  apiKey: 'key',
80
80
  templates: dashboardConfig,
@@ -86,7 +86,7 @@ test('template name is constrained to config keys at type level', () => {
86
86
  expectTypeOf().toMatchTypeOf();
87
87
  });
88
88
  test('missing required variable is rejected by types', () => {
89
- const client = new Mailer({
89
+ const client = new Ewyn({
90
90
  workspaceId: 'ws',
91
91
  apiKey: 'key',
92
92
  templates: dashboardConfig,
@@ -7,7 +7,7 @@
7
7
  * the dashboard" work with the SDK and that runtime validation behaves correctly.
8
8
  */
9
9
  import { beforeEach, describe, expect, it, vi } from 'vitest';
10
- import { Mailer } from '../index.js';
10
+ import { Ewyn } from '../index.js';
11
11
  global.fetch = vi.fn();
12
12
  describe('Dashboard config (runtime)', () => {
13
13
  beforeEach(() => {
@@ -45,10 +45,9 @@ describe('Dashboard config (runtime)', () => {
45
45
  },
46
46
  };
47
47
  it('sends with dashboard-shaped config (only required vars)', async () => {
48
- const client = new Mailer({
48
+ const client = new Ewyn({
49
49
  workspaceId: 'ws-1',
50
50
  apiKey: 'key-1',
51
- baseUrl: 'https://api.test.com/api/v1',
52
51
  templates: configOnlyRequired,
53
52
  maxRetries: 1,
54
53
  timeout: 5000,
@@ -72,7 +71,7 @@ describe('Dashboard config (runtime)', () => {
72
71
  },
73
72
  });
74
73
  expect(result).toEqual(mockResponse);
75
- expect(global.fetch).toHaveBeenCalledWith('https://api.test.com/api/v1/workspaces/ws-1/send', expect.objectContaining({
74
+ expect(global.fetch).toHaveBeenCalledWith('https://www.ewyn.ai/api/v1/workspaces/ws-1/send', expect.objectContaining({
76
75
  method: 'POST',
77
76
  body: expect.stringContaining('"templateId":"version-uuid-1"'),
78
77
  }));
@@ -81,10 +80,9 @@ describe('Dashboard config (runtime)', () => {
81
80
  expect(body.variables).toEqual({ firstName: 'Jane', lastName: 'Doe' });
82
81
  });
83
82
  it('sends with dashboard-shaped config (mixed required/optional, hyphenated key)', async () => {
84
- const client = new Mailer({
83
+ const client = new Ewyn({
85
84
  workspaceId: 'ws-2',
86
85
  apiKey: 'key-2',
87
- baseUrl: 'https://api.test.com/api/v1',
88
86
  templates: configMixedVars,
89
87
  maxRetries: 1,
90
88
  timeout: 5000,
@@ -117,10 +115,9 @@ describe('Dashboard config (runtime)', () => {
117
115
  });
118
116
  });
119
117
  it('sends with template that has empty vars (dashboard shape)', async () => {
120
- const client = new Mailer({
118
+ const client = new Ewyn({
121
119
  workspaceId: 'ws-3',
122
120
  apiKey: 'key-3',
123
- baseUrl: 'https://api.test.com/api/v1',
124
121
  templates: configEmptyVars,
125
122
  maxRetries: 1,
126
123
  timeout: 5000,
@@ -147,10 +144,9 @@ describe('Dashboard config (runtime)', () => {
147
144
  expect(body.variables).toEqual({});
148
145
  });
149
146
  it('throws when required variable is missing (dashboard config)', async () => {
150
- const client = new Mailer({
147
+ const client = new Ewyn({
151
148
  workspaceId: 'ws-1',
152
149
  apiKey: 'key-1',
153
- baseUrl: 'https://api.test.com/api/v1',
154
150
  templates: configOnlyRequired,
155
151
  maxRetries: 1,
156
152
  timeout: 5000,
@@ -165,10 +161,9 @@ describe('Dashboard config (runtime)', () => {
165
161
  })).rejects.toThrow('Missing required variables for template "welcome": lastName');
166
162
  });
167
163
  it('throws when template name is not in config (dashboard config)', async () => {
168
- const client = new Mailer({
164
+ const client = new Ewyn({
169
165
  workspaceId: 'ws-1',
170
166
  apiKey: 'key-1',
171
- baseUrl: 'https://api.test.com/api/v1',
172
167
  templates: configOnlyRequired,
173
168
  maxRetries: 1,
174
169
  timeout: 5000,
@@ -1,24 +1,24 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { MailerApiError } from '../errors.js';
3
- describe('MailerApiError', () => {
1
+ import { describe, expect, it } from 'vitest';
2
+ import { EwynApiError } from '../errors.js';
3
+ describe('EwynApiError', () => {
4
4
  describe('constructor', () => {
5
5
  it('should create error with status code', () => {
6
- const error = new MailerApiError(400);
6
+ const error = new EwynApiError(400);
7
7
  expect(error).toBeInstanceOf(Error);
8
- expect(error).toBeInstanceOf(MailerApiError);
8
+ expect(error).toBeInstanceOf(EwynApiError);
9
9
  expect(error.status).toBe(400);
10
10
  expect(error.message).toBe('API request failed with status 400');
11
- expect(error.name).toBe('MailerApiError');
11
+ expect(error.name).toBe('EwynApiError');
12
12
  });
13
13
  it('should create error with custom message', () => {
14
- const error = new MailerApiError(400, 'INVALID_REQUEST', undefined, 'Invalid email address');
14
+ const error = new EwynApiError(400, 'INVALID_REQUEST', undefined, 'Invalid email address');
15
15
  expect(error.status).toBe(400);
16
16
  expect(error.code).toBe('INVALID_REQUEST');
17
17
  expect(error.message).toBe('Invalid email address');
18
18
  });
19
19
  it('should create error with details', () => {
20
20
  const details = { field: 'to', reason: 'Invalid email format' };
21
- const error = new MailerApiError(400, 'VALIDATION_ERROR', details);
21
+ const error = new EwynApiError(400, 'VALIDATION_ERROR', details);
22
22
  expect(error.status).toBe(400);
23
23
  expect(error.code).toBe('VALIDATION_ERROR');
24
24
  expect(error.details).toEqual(details);
@@ -26,62 +26,62 @@ describe('MailerApiError', () => {
26
26
  });
27
27
  describe('isClientError', () => {
28
28
  it('should return true for 4xx errors', () => {
29
- expect(new MailerApiError(400).isClientError()).toBe(true);
30
- expect(new MailerApiError(401).isClientError()).toBe(true);
31
- expect(new MailerApiError(404).isClientError()).toBe(true);
32
- expect(new MailerApiError(429).isClientError()).toBe(true);
33
- expect(new MailerApiError(499).isClientError()).toBe(true);
29
+ expect(new EwynApiError(400).isClientError()).toBe(true);
30
+ expect(new EwynApiError(401).isClientError()).toBe(true);
31
+ expect(new EwynApiError(404).isClientError()).toBe(true);
32
+ expect(new EwynApiError(429).isClientError()).toBe(true);
33
+ expect(new EwynApiError(499).isClientError()).toBe(true);
34
34
  });
35
35
  it('should return false for non-4xx errors', () => {
36
- expect(new MailerApiError(200).isClientError()).toBe(false);
37
- expect(new MailerApiError(399).isClientError()).toBe(false);
38
- expect(new MailerApiError(500).isClientError()).toBe(false);
36
+ expect(new EwynApiError(200).isClientError()).toBe(false);
37
+ expect(new EwynApiError(399).isClientError()).toBe(false);
38
+ expect(new EwynApiError(500).isClientError()).toBe(false);
39
39
  });
40
40
  });
41
41
  describe('isServerError', () => {
42
42
  it('should return true for 5xx errors', () => {
43
- expect(new MailerApiError(500).isServerError()).toBe(true);
44
- expect(new MailerApiError(502).isServerError()).toBe(true);
45
- expect(new MailerApiError(503).isServerError()).toBe(true);
46
- expect(new MailerApiError(599).isServerError()).toBe(true);
43
+ expect(new EwynApiError(500).isServerError()).toBe(true);
44
+ expect(new EwynApiError(502).isServerError()).toBe(true);
45
+ expect(new EwynApiError(503).isServerError()).toBe(true);
46
+ expect(new EwynApiError(599).isServerError()).toBe(true);
47
47
  });
48
48
  it('should return false for non-5xx errors', () => {
49
- expect(new MailerApiError(200).isServerError()).toBe(false);
50
- expect(new MailerApiError(400).isServerError()).toBe(false);
51
- expect(new MailerApiError(499).isServerError()).toBe(false);
49
+ expect(new EwynApiError(200).isServerError()).toBe(false);
50
+ expect(new EwynApiError(400).isServerError()).toBe(false);
51
+ expect(new EwynApiError(499).isServerError()).toBe(false);
52
52
  });
53
53
  });
54
54
  describe('isRetryable', () => {
55
55
  it('should return true for 429 rate limit', () => {
56
- expect(new MailerApiError(429).isRetryable()).toBe(true);
56
+ expect(new EwynApiError(429).isRetryable()).toBe(true);
57
57
  });
58
58
  it('should return true for 5xx errors', () => {
59
- expect(new MailerApiError(500).isRetryable()).toBe(true);
60
- expect(new MailerApiError(502).isRetryable()).toBe(true);
61
- expect(new MailerApiError(503).isRetryable()).toBe(true);
59
+ expect(new EwynApiError(500).isRetryable()).toBe(true);
60
+ expect(new EwynApiError(502).isRetryable()).toBe(true);
61
+ expect(new EwynApiError(503).isRetryable()).toBe(true);
62
62
  });
63
63
  it('should return false for other errors', () => {
64
- expect(new MailerApiError(200).isRetryable()).toBe(false);
65
- expect(new MailerApiError(400).isRetryable()).toBe(false);
66
- expect(new MailerApiError(401).isRetryable()).toBe(false);
67
- expect(new MailerApiError(404).isRetryable()).toBe(false);
64
+ expect(new EwynApiError(200).isRetryable()).toBe(false);
65
+ expect(new EwynApiError(400).isRetryable()).toBe(false);
66
+ expect(new EwynApiError(401).isRetryable()).toBe(false);
67
+ expect(new EwynApiError(404).isRetryable()).toBe(false);
68
68
  });
69
69
  });
70
70
  describe('error prototype chain', () => {
71
71
  it('should maintain correct prototype chain', () => {
72
- const error = new MailerApiError(400);
72
+ const error = new EwynApiError(400);
73
73
  expect(error instanceof Error).toBe(true);
74
- expect(error instanceof MailerApiError).toBe(true);
75
- expect(Object.getPrototypeOf(error)).toBe(MailerApiError.prototype);
74
+ expect(error instanceof EwynApiError).toBe(true);
75
+ expect(Object.getPrototypeOf(error)).toBe(EwynApiError.prototype);
76
76
  });
77
77
  it('should work with try-catch instanceof checks', () => {
78
78
  try {
79
- throw new MailerApiError(400, 'TEST_ERROR');
79
+ throw new EwynApiError(400, 'TEST_ERROR');
80
80
  }
81
81
  catch (error) {
82
- expect(error instanceof MailerApiError).toBe(true);
82
+ expect(error instanceof EwynApiError).toBe(true);
83
83
  expect(error instanceof Error).toBe(true);
84
- if (error instanceof MailerApiError) {
84
+ if (error instanceof EwynApiError) {
85
85
  expect(error.code).toBe('TEST_ERROR');
86
86
  }
87
87
  }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ewyn.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ewyn.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/ewyn.test.ts"],"names":[],"mappings":""}