@ewyn/client 0.2.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
@@ -27,7 +27,6 @@ import { Ewyn } from '@ewyn/client';
27
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
@@ -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
@@ -119,8 +169,6 @@ 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
174
  ### `Ewyn` Class
@@ -135,7 +183,6 @@ new Ewyn(options: EwynOptions)
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)
@@ -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
+ });
@@ -48,7 +48,6 @@ describe('Dashboard config (runtime)', () => {
48
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
  }));
@@ -84,7 +83,6 @@ describe('Dashboard config (runtime)', () => {
84
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,
@@ -120,7 +118,6 @@ describe('Dashboard config (runtime)', () => {
120
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,
@@ -150,7 +147,6 @@ describe('Dashboard config (runtime)', () => {
150
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,
@@ -168,7 +164,6 @@ describe('Dashboard config (runtime)', () => {
168
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,4 +1,4 @@
1
- import { describe, it, expect } from 'vitest';
1
+ import { describe, expect, it } from 'vitest';
2
2
  import { EwynApiError } from '../errors.js';
3
3
  describe('EwynApiError', () => {
4
4
  describe('constructor', () => {
@@ -10,7 +10,6 @@ describe('Ewyn SDK', () => {
10
10
  client = new Ewyn({
11
11
  workspaceId: 'test-workspace-id',
12
12
  apiKey: 'test-api-key',
13
- baseUrl: 'https://api.test.com/api/v1',
14
13
  maxRetries: 2, // Allow 1 retry (attempt < maxRetries)
15
14
  timeout: 5000,
16
15
  });
@@ -65,7 +64,7 @@ describe('Ewyn SDK', () => {
65
64
  },
66
65
  });
67
66
  expect(result).toEqual(mockResponse);
68
- expect(global.fetch).toHaveBeenCalledWith('https://api.test.com/api/v1/workspaces/test-workspace-id/send', expect.objectContaining({
67
+ expect(global.fetch).toHaveBeenCalledWith('https://www.ewyn.ai/api/v1/workspaces/test-workspace-id/send', expect.objectContaining({
69
68
  method: 'POST',
70
69
  headers: expect.objectContaining({
71
70
  'Content-Type': 'application/json',
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/index.d.ts CHANGED
@@ -1,55 +1,7 @@
1
1
  import type { EwynOptions, EwynOptionsTyped, SendEmailOptions, SendEmailOptionsTyped, SendEmailResponse, TemplateConfig } from './types.js';
2
- /**
3
- * Ewyn SDK Client
4
- *
5
- * @example Basic usage without template config
6
- * ```ts
7
- * const client = new Ewyn({
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 Ewyn({
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
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;
@@ -95,5 +47,5 @@ export declare class Ewyn<TConfig extends TemplateConfig = TemplateConfig> {
95
47
  private requestWithRetry;
96
48
  }
97
49
  export { EwynApiError } from './errors.js';
98
- export type { EwynOptions, EwynOptionsTyped, SendEmailOptions, SendEmailOptionsBase, SendEmailOptionsTyped, SendEmailResponse, TemplateConfig, TemplateConfigEntry, TemplateVariable, } from './types.js';
50
+ export type { EwynFetchConfig, EwynOptions, EwynOptionsTyped, SendEmailOptions, SendEmailOptionsBase, SendEmailOptionsTyped, SendEmailResponse, TemplateConfig, TemplateConfigEntry, TemplateVariable } from './types.js';
99
51
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,WAAW,EACX,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,qBAAa,IAAI,CAAC,OAAO,SAAS,cAAc,GAAG,cAAc;IAC/D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAU;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,OAAO,EAAE,WAAW,GAAG,gBAAgB,CAAC,OAAO,CAAC;IAS5D;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACG,IAAI,CACR,OAAO,EAAE,OAAO,SAAS,cAAc,GACnC,qBAAqB,CAAC,OAAO,CAAC,GAAG,gBAAgB,GACjD,gBAAgB,GACnB,OAAO,CAAC,iBAAiB,CAAC;IA8C7B;;OAEG;YACW,iBAAiB;IAyB/B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA2BzB;;OAEG;YACW,gBAAgB;CAuE/B;AAED,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,YAAY,EACV,WAAW,EACX,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,EACd,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,WAAW,EACX,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,EACf,MAAM,YAAY,CAAC;AAmDpB,qBAAa,IAAI,CAAC,OAAO,SAAS,cAAc,GAAG,cAAc;IAC/D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAU;IACrC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,OAAO,EAAE,WAAW,GAAG,gBAAgB,CAAC,OAAO,CAAC;IAQ5D;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACG,IAAI,CACR,OAAO,EAAE,OAAO,SAAS,cAAc,GACnC,qBAAqB,CAAC,OAAO,CAAC,GAAG,gBAAgB,GACjD,gBAAgB,GACnB,OAAO,CAAC,iBAAiB,CAAC;IA8C7B;;OAEG;YACW,iBAAiB;IAyB/B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA2BzB;;OAEG;YACW,gBAAgB;CAuE/B;AAED,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,YAAY,EACV,eAAe,EACf,WAAW,EACX,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,cAAc,EACd,mBAAmB,EACnB,gBAAgB,EACjB,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -46,17 +46,16 @@ import { EwynApiError } from './errors.js';
46
46
  * });
47
47
  * ```
48
48
  */
49
+ const EWYN_API_BASE_URL = 'https://www.ewyn.ai/api/v1';
49
50
  export class Ewyn {
50
51
  workspaceId;
51
52
  apiKey;
52
- baseUrl;
53
53
  templates;
54
54
  maxRetries;
55
55
  timeout;
56
56
  constructor(options) {
57
57
  this.workspaceId = options.workspaceId;
58
58
  this.apiKey = options.apiKey;
59
- this.baseUrl = options.baseUrl || 'https://www.ewyn.ai/api/v1';
60
59
  this.templates = options.templates;
61
60
  this.maxRetries = options.maxRetries ?? 3;
62
61
  this.timeout = options.timeout ?? 30000;
@@ -166,7 +165,7 @@ export class Ewyn {
166
165
  * Make HTTP request with retry logic
167
166
  */
168
167
  async requestWithRetry(path, init, attempt = 1) {
169
- const url = `${this.baseUrl}${path}`;
168
+ const url = `${EWYN_API_BASE_URL}${path}`;
170
169
  const controller = new AbortController();
171
170
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
172
171
  try {
package/dist/types.d.ts CHANGED
@@ -104,8 +104,6 @@ export interface EwynOptionsBase {
104
104
  workspaceId: string;
105
105
  /** API key secret */
106
106
  apiKey: string;
107
- /** Base URL for the API (defaults to production) */
108
- baseUrl?: string;
109
107
  /** Maximum number of retries for retryable errors (default: 3) */
110
108
  maxRetries?: number;
111
109
  /** Request timeout in milliseconds (default: 30000) */
@@ -125,5 +123,17 @@ export type EwynOptions = EwynOptionsBase & {
125
123
  /** Template configuration for name-based sending and validation */
126
124
  templates?: TemplateConfig;
127
125
  };
126
+ /**
127
+ * Configuration for the fetch-config CLI (ewyn.config.ts).
128
+ * Used only by the CLI to know which workspace/API key to use and where to write the generated file.
129
+ */
130
+ export interface EwynFetchConfig {
131
+ /** Workspace ID (UUID) */
132
+ workspaceId: string;
133
+ /** API key secret (e.g. process.env.EWYN_API_KEY) */
134
+ apiKey: string;
135
+ /** Output path for the generated ewynTemplates file (e.g. ./src/ewynTemplates.ts). If omitted, defaults to ./ewynTemplates.ts in cwd. */
136
+ configurationPath?: string;
137
+ }
128
138
  export {};
129
139
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ewyn/client",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Official TypeScript SDK for Ewyn email service with full type safety",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -11,6 +11,9 @@
11
11
  "types": "./dist/index.d.ts"
12
12
  }
13
13
  },
14
+ "bin": {
15
+ "ewyn": "./dist/cli.js"
16
+ },
14
17
  "files": [
15
18
  "dist"
16
19
  ],
@@ -35,7 +38,9 @@
35
38
  "engines": {
36
39
  "node": ">=18.0.0"
37
40
  },
38
- "dependencies": {},
41
+ "dependencies": {
42
+ "tsx": "^4.19.0"
43
+ },
39
44
  "devDependencies": {
40
45
  "@types/node": "^20.11.0",
41
46
  "@workspace/eslint-config": "workspace:*",