@astralibx/email-ui 2.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/README.md +181 -0
- package/dist/api.js +9 -0
- package/dist/api.js.map +1 -0
- package/dist/index-DSTzwYDP.js +255 -0
- package/dist/index-DSTzwYDP.js.map +1 -0
- package/dist/index.js +5850 -0
- package/dist/index.js.map +1 -0
- package/package.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# @astralibx/email-ui
|
|
2
|
+
|
|
3
|
+
Lit Web Components for email infrastructure admin UIs. Drop-in components for managing email accounts, automation rules, templates, and analytics -- built on native Custom Elements that work in any framework.
|
|
4
|
+
|
|
5
|
+
Part of the [@astralibx](https://github.com/astralibx) email ecosystem:
|
|
6
|
+
- `@astralibx/email-account-manager` -- account CRUD, health, warmup, SMTP, approval queues
|
|
7
|
+
- `@astralibx/email-rule-engine` -- templates, rules, throttling, runner
|
|
8
|
+
- `@astralibx/email-analytics` -- event recording, aggregation, time-series
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @astralibx/email-ui
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Peer dependency: `lit ^3.0.0` (included automatically).
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import { AlxConfig } from '@astralibx/email-ui';
|
|
22
|
+
|
|
23
|
+
// One-time setup at app bootstrap
|
|
24
|
+
AlxConfig.setup({
|
|
25
|
+
accountManagerApi: '/api/email-accounts',
|
|
26
|
+
ruleEngineApi: '/api/email-rules',
|
|
27
|
+
analyticsApi: '/api/analytics',
|
|
28
|
+
authToken: 'Bearer your-token',
|
|
29
|
+
theme: 'dark',
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
```html
|
|
34
|
+
<!-- Drop components anywhere in your HTML -->
|
|
35
|
+
<alx-account-list></alx-account-list>
|
|
36
|
+
<alx-analytics-overview date-from="2025-01-01" date-to="2025-01-31"></alx-analytics-overview>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Framework Integration
|
|
40
|
+
|
|
41
|
+
### Angular
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { CUSTOM_ELEMENTS_SCHEMA, Component } from '@angular/core';
|
|
45
|
+
import { AlxConfig } from '@astralibx/email-ui';
|
|
46
|
+
|
|
47
|
+
AlxConfig.setup({ accountManagerApi: '/api/email-accounts', authToken: token, theme: 'dark' });
|
|
48
|
+
|
|
49
|
+
@Component({
|
|
50
|
+
standalone: true,
|
|
51
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
52
|
+
template: `<alx-account-list (alx-account-selected)="onSelect($event)"></alx-account-list>`,
|
|
53
|
+
})
|
|
54
|
+
export class EmailAccountsComponent {
|
|
55
|
+
onSelect(event: CustomEvent) {
|
|
56
|
+
console.log('Selected account:', event.detail);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### React / Next.js
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
import { AlxConfig } from '@astralibx/email-ui';
|
|
65
|
+
import { useEffect, useRef } from 'react';
|
|
66
|
+
|
|
67
|
+
AlxConfig.setup({ accountManagerApi: '/api/email-accounts', authToken: token, theme: 'dark' });
|
|
68
|
+
|
|
69
|
+
export default function EmailAccounts() {
|
|
70
|
+
const listRef = useRef<HTMLElement>(null);
|
|
71
|
+
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
listRef.current?.addEventListener('alx-account-selected', (e: Event) => {
|
|
74
|
+
console.log('Selected:', (e as CustomEvent).detail);
|
|
75
|
+
});
|
|
76
|
+
}, []);
|
|
77
|
+
|
|
78
|
+
return <alx-account-list ref={listRef} />;
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Plain HTML
|
|
83
|
+
|
|
84
|
+
```html
|
|
85
|
+
<script type="module">
|
|
86
|
+
import { AlxConfig } from 'https://cdn.example.com/@astralibx/email-ui/dist/index.js';
|
|
87
|
+
|
|
88
|
+
AlxConfig.setup({
|
|
89
|
+
accountManagerApi: 'https://api.example.com/email-accounts',
|
|
90
|
+
ruleEngineApi: 'https://api.example.com/email-rules',
|
|
91
|
+
analyticsApi: 'https://api.example.com/analytics',
|
|
92
|
+
authToken: 'Bearer your-token',
|
|
93
|
+
theme: 'dark',
|
|
94
|
+
});
|
|
95
|
+
</script>
|
|
96
|
+
|
|
97
|
+
<alx-account-list></alx-account-list>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Components
|
|
101
|
+
|
|
102
|
+
### Account Management (9)
|
|
103
|
+
|
|
104
|
+
| Tag | Description |
|
|
105
|
+
|-----|-------------|
|
|
106
|
+
| `<alx-account-list>` | Paginated table with status, health, capacity, warmup indicators |
|
|
107
|
+
| `<alx-account-form>` | Create/edit form with SMTP + IMAP config, provider selection |
|
|
108
|
+
| `<alx-account-health>` | Health dashboard with scores, bounce rates, error trends |
|
|
109
|
+
| `<alx-account-warmup>` | Warmup progress, day tracking, phase schedule |
|
|
110
|
+
| `<alx-account-capacity>` | Aggregate + per-account daily limits and usage bars |
|
|
111
|
+
| `<alx-smtp-tester>` | One-click SMTP connection test with result display |
|
|
112
|
+
| `<alx-bounce-status>` | IMAP bounce check status, single or multi-account view |
|
|
113
|
+
| `<alx-approval-queue>` | Pending drafts with approve/reject/bulk actions |
|
|
114
|
+
| `<alx-global-settings>` | Timezone, dev mode, IMAP, approval, queue tuning |
|
|
115
|
+
|
|
116
|
+
### Rule Engine (7)
|
|
117
|
+
|
|
118
|
+
| Tag | Description |
|
|
119
|
+
|-----|-------------|
|
|
120
|
+
| `<alx-template-list>` | Template table with category/audience/platform filters |
|
|
121
|
+
| `<alx-template-editor>` | MJML + Handlebars editor with live preview |
|
|
122
|
+
| `<alx-rule-list>` | Rules with active toggle, stats, dry-run button |
|
|
123
|
+
| `<alx-rule-editor>` | Condition builder, behavior config, template selection |
|
|
124
|
+
| `<alx-run-history>` | Execution logs with expandable per-rule breakdown |
|
|
125
|
+
| `<alx-throttle-settings>` | Global throttle limits (per-user daily/weekly, gap) |
|
|
126
|
+
| `<alx-guide-panel>` | Collapsible built-in documentation panel |
|
|
127
|
+
|
|
128
|
+
### Analytics (5)
|
|
129
|
+
|
|
130
|
+
| Tag | Description |
|
|
131
|
+
|-----|-------------|
|
|
132
|
+
| `<alx-analytics-overview>` | Metric cards: sent, delivered, failed, bounced, opened, clicked |
|
|
133
|
+
| `<alx-analytics-timeline>` | Bar chart of send volume (daily/weekly/monthly) |
|
|
134
|
+
| `<alx-analytics-accounts>` | Sortable per-account performance table |
|
|
135
|
+
| `<alx-analytics-rules>` | Sortable per-rule stats with error rates |
|
|
136
|
+
| `<alx-analytics-engagement>` | Open/click/unsubscribe rates (SES tracking note) |
|
|
137
|
+
|
|
138
|
+
## API-Only Usage
|
|
139
|
+
|
|
140
|
+
Use the typed API clients without any UI components:
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import { AccountAPI, RuleAPI, AnalyticsAPI } from '@astralibx/email-ui/api';
|
|
144
|
+
|
|
145
|
+
const accounts = new AccountAPI('/api/email-accounts');
|
|
146
|
+
const list = await accounts.list({ page: 1, limit: 20 });
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Theming
|
|
150
|
+
|
|
151
|
+
Override CSS custom properties on any component or a parent element:
|
|
152
|
+
|
|
153
|
+
```css
|
|
154
|
+
alx-account-list {
|
|
155
|
+
--alx-primary: #d4af37;
|
|
156
|
+
--alx-bg: #111;
|
|
157
|
+
--alx-surface: #1a1a1a;
|
|
158
|
+
--alx-border: #333;
|
|
159
|
+
--alx-text: #ccc;
|
|
160
|
+
--alx-text-muted: #888;
|
|
161
|
+
--alx-radius: 6px;
|
|
162
|
+
--alx-font-family: 'Inter', sans-serif;
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Built-in themes: `alxDarkTheme` and `alxLightTheme` are available as Lit CSS exports.
|
|
167
|
+
|
|
168
|
+
## Documentation
|
|
169
|
+
|
|
170
|
+
- [Configuration](docs/configuration.md) -- AlxConfig setup, auth tokens, all options
|
|
171
|
+
- [Theming](docs/theming.md) -- CSS custom properties, dark/light themes, customization
|
|
172
|
+
- [Account Components](docs/account-components.md) -- All 9 account management components
|
|
173
|
+
- [Rule Components](docs/rule-components.md) -- All 7 rule engine components
|
|
174
|
+
- [Analytics Components](docs/analytics-components.md) -- All 5 analytics components
|
|
175
|
+
- [API Client](docs/api-client.md) -- Using AccountAPI, RuleAPI, AnalyticsAPI directly
|
|
176
|
+
- [Framework Integration](docs/framework-integration.md) -- Angular, React, Vue, Next.js, plain HTML
|
|
177
|
+
- [Events](docs/events.md) -- All custom events with names, detail types, examples
|
|
178
|
+
|
|
179
|
+
## License
|
|
180
|
+
|
|
181
|
+
MIT
|
package/dist/api.js
ADDED
package/dist/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
const API_KEY_MAP = {
|
|
2
|
+
accountManager: "accountManagerApi",
|
|
3
|
+
ruleEngine: "ruleEngineApi",
|
|
4
|
+
analytics: "analyticsApi"
|
|
5
|
+
};
|
|
6
|
+
const _AlxConfig = class _AlxConfig {
|
|
7
|
+
static setup(options) {
|
|
8
|
+
_AlxConfig.instance = { ...options };
|
|
9
|
+
}
|
|
10
|
+
static get() {
|
|
11
|
+
return { ..._AlxConfig.instance };
|
|
12
|
+
}
|
|
13
|
+
static getApiUrl(key) {
|
|
14
|
+
const prop = API_KEY_MAP[key];
|
|
15
|
+
return _AlxConfig.instance[prop] ?? "";
|
|
16
|
+
}
|
|
17
|
+
static getHeaders() {
|
|
18
|
+
const headers = {
|
|
19
|
+
"Content-Type": "application/json"
|
|
20
|
+
};
|
|
21
|
+
if (_AlxConfig.instance.authToken) {
|
|
22
|
+
headers["Authorization"] = _AlxConfig.instance.authToken;
|
|
23
|
+
}
|
|
24
|
+
return headers;
|
|
25
|
+
}
|
|
26
|
+
static setAuthToken(token) {
|
|
27
|
+
_AlxConfig.instance = { ..._AlxConfig.instance, authToken: token };
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
_AlxConfig.instance = {};
|
|
31
|
+
let AlxConfig = _AlxConfig;
|
|
32
|
+
class HttpClientError extends Error {
|
|
33
|
+
constructor(message, status, body) {
|
|
34
|
+
super(message);
|
|
35
|
+
this.status = status;
|
|
36
|
+
this.body = body;
|
|
37
|
+
this.name = "HttpClientError";
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function buildQueryString(params) {
|
|
41
|
+
if (!params) return "";
|
|
42
|
+
const entries = Object.entries(params).filter(
|
|
43
|
+
([, v]) => v !== void 0 && v !== null
|
|
44
|
+
);
|
|
45
|
+
if (entries.length === 0) return "";
|
|
46
|
+
const qs = new URLSearchParams();
|
|
47
|
+
for (const [key, value] of entries) {
|
|
48
|
+
qs.set(key, String(value));
|
|
49
|
+
}
|
|
50
|
+
return "?" + qs.toString();
|
|
51
|
+
}
|
|
52
|
+
async function handleResponse(response) {
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
let body;
|
|
55
|
+
try {
|
|
56
|
+
body = await response.json();
|
|
57
|
+
} catch {
|
|
58
|
+
body = await response.text().catch(() => null);
|
|
59
|
+
}
|
|
60
|
+
throw new HttpClientError(
|
|
61
|
+
`HTTP ${response.status}: ${response.statusText}`,
|
|
62
|
+
response.status,
|
|
63
|
+
body
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
if (response.status === 204) return void 0;
|
|
67
|
+
return response.json();
|
|
68
|
+
}
|
|
69
|
+
class HttpClient {
|
|
70
|
+
constructor(baseUrl) {
|
|
71
|
+
this.baseUrl = baseUrl;
|
|
72
|
+
}
|
|
73
|
+
url(path, params) {
|
|
74
|
+
return this.baseUrl + path + buildQueryString(params);
|
|
75
|
+
}
|
|
76
|
+
async get(path, params) {
|
|
77
|
+
const response = await fetch(this.url(path, params), {
|
|
78
|
+
method: "GET",
|
|
79
|
+
headers: AlxConfig.getHeaders()
|
|
80
|
+
});
|
|
81
|
+
return handleResponse(response);
|
|
82
|
+
}
|
|
83
|
+
async post(path, body) {
|
|
84
|
+
const response = await fetch(this.url(path), {
|
|
85
|
+
method: "POST",
|
|
86
|
+
headers: AlxConfig.getHeaders(),
|
|
87
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
88
|
+
});
|
|
89
|
+
return handleResponse(response);
|
|
90
|
+
}
|
|
91
|
+
async put(path, body) {
|
|
92
|
+
const response = await fetch(this.url(path), {
|
|
93
|
+
method: "PUT",
|
|
94
|
+
headers: AlxConfig.getHeaders(),
|
|
95
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
96
|
+
});
|
|
97
|
+
return handleResponse(response);
|
|
98
|
+
}
|
|
99
|
+
async patch(path, body) {
|
|
100
|
+
const response = await fetch(this.url(path), {
|
|
101
|
+
method: "PATCH",
|
|
102
|
+
headers: AlxConfig.getHeaders(),
|
|
103
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
104
|
+
});
|
|
105
|
+
return handleResponse(response);
|
|
106
|
+
}
|
|
107
|
+
async delete(path) {
|
|
108
|
+
const response = await fetch(this.url(path), {
|
|
109
|
+
method: "DELETE",
|
|
110
|
+
headers: AlxConfig.getHeaders()
|
|
111
|
+
});
|
|
112
|
+
return handleResponse(response);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
class AccountAPI {
|
|
116
|
+
constructor(baseUrl) {
|
|
117
|
+
this.http = new HttpClient(baseUrl ?? AlxConfig.getApiUrl("accountManager"));
|
|
118
|
+
}
|
|
119
|
+
list(params) {
|
|
120
|
+
return this.http.get("/accounts", params);
|
|
121
|
+
}
|
|
122
|
+
getById(id) {
|
|
123
|
+
return this.http.get(`/accounts/${id}`);
|
|
124
|
+
}
|
|
125
|
+
create(data) {
|
|
126
|
+
return this.http.post("/accounts", data);
|
|
127
|
+
}
|
|
128
|
+
update(id, data) {
|
|
129
|
+
return this.http.put(`/accounts/${id}`, data);
|
|
130
|
+
}
|
|
131
|
+
remove(id) {
|
|
132
|
+
return this.http.delete(`/accounts/${id}`);
|
|
133
|
+
}
|
|
134
|
+
testConnection(id) {
|
|
135
|
+
return this.http.post(`/accounts/${id}/test`);
|
|
136
|
+
}
|
|
137
|
+
getHealth(id) {
|
|
138
|
+
return this.http.get(`/accounts/${id}/health`);
|
|
139
|
+
}
|
|
140
|
+
getAllHealth() {
|
|
141
|
+
return this.http.get("/accounts/health");
|
|
142
|
+
}
|
|
143
|
+
getCapacity() {
|
|
144
|
+
return this.http.get("/accounts/capacity");
|
|
145
|
+
}
|
|
146
|
+
getWarmupStatus(id) {
|
|
147
|
+
return this.http.get(`/accounts/${id}/warmup`);
|
|
148
|
+
}
|
|
149
|
+
startWarmup(id) {
|
|
150
|
+
return this.http.post(`/accounts/${id}/warmup/start`);
|
|
151
|
+
}
|
|
152
|
+
getSettings() {
|
|
153
|
+
return this.http.get("/settings");
|
|
154
|
+
}
|
|
155
|
+
updateSettings(data) {
|
|
156
|
+
return this.http.patch("/settings", data);
|
|
157
|
+
}
|
|
158
|
+
listIdentifiers(params) {
|
|
159
|
+
return this.http.get("/identifiers", params);
|
|
160
|
+
}
|
|
161
|
+
listDrafts(params) {
|
|
162
|
+
return this.http.get("/drafts", params);
|
|
163
|
+
}
|
|
164
|
+
approveDraft(id) {
|
|
165
|
+
return this.http.post(`/drafts/${id}/approve`);
|
|
166
|
+
}
|
|
167
|
+
rejectDraft(id) {
|
|
168
|
+
return this.http.post(`/drafts/${id}/reject`);
|
|
169
|
+
}
|
|
170
|
+
bulkApprove(ids) {
|
|
171
|
+
return this.http.post("/drafts/bulk-approve", { ids });
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
class RuleAPI {
|
|
175
|
+
constructor(baseUrl) {
|
|
176
|
+
this.http = new HttpClient(baseUrl ?? AlxConfig.getApiUrl("ruleEngine"));
|
|
177
|
+
}
|
|
178
|
+
listTemplates(params) {
|
|
179
|
+
return this.http.get("/templates", params);
|
|
180
|
+
}
|
|
181
|
+
createTemplate(data) {
|
|
182
|
+
return this.http.post("/templates", data);
|
|
183
|
+
}
|
|
184
|
+
updateTemplate(id, data) {
|
|
185
|
+
return this.http.put(`/templates/${id}`, data);
|
|
186
|
+
}
|
|
187
|
+
deleteTemplate(id) {
|
|
188
|
+
return this.http.delete(`/templates/${id}`);
|
|
189
|
+
}
|
|
190
|
+
previewTemplate(data) {
|
|
191
|
+
return this.http.post("/templates/preview", data);
|
|
192
|
+
}
|
|
193
|
+
listRules(params) {
|
|
194
|
+
return this.http.get("/rules", params);
|
|
195
|
+
}
|
|
196
|
+
createRule(data) {
|
|
197
|
+
return this.http.post("/rules", data);
|
|
198
|
+
}
|
|
199
|
+
updateRule(id, data) {
|
|
200
|
+
return this.http.put(`/rules/${id}`, data);
|
|
201
|
+
}
|
|
202
|
+
deleteRule(id) {
|
|
203
|
+
return this.http.delete(`/rules/${id}`);
|
|
204
|
+
}
|
|
205
|
+
toggleRule(id) {
|
|
206
|
+
return this.http.post(`/rules/${id}/toggle`);
|
|
207
|
+
}
|
|
208
|
+
dryRun(id) {
|
|
209
|
+
return this.http.post(`/rules/${id}/dry-run`);
|
|
210
|
+
}
|
|
211
|
+
triggerRun() {
|
|
212
|
+
return this.http.post("/runner");
|
|
213
|
+
}
|
|
214
|
+
getRunHistory(params) {
|
|
215
|
+
return this.http.get("/runner/logs", params);
|
|
216
|
+
}
|
|
217
|
+
getThrottleSettings() {
|
|
218
|
+
return this.http.get("/throttle");
|
|
219
|
+
}
|
|
220
|
+
updateThrottleSettings(data) {
|
|
221
|
+
return this.http.put("/throttle", data);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
class AnalyticsAPI {
|
|
225
|
+
constructor(baseUrl) {
|
|
226
|
+
this.http = new HttpClient(baseUrl ?? AlxConfig.getApiUrl("analytics"));
|
|
227
|
+
}
|
|
228
|
+
getOverview(params) {
|
|
229
|
+
return this.http.get("/overview", params);
|
|
230
|
+
}
|
|
231
|
+
getTimeline(params) {
|
|
232
|
+
return this.http.get("/timeline", params);
|
|
233
|
+
}
|
|
234
|
+
getAccountStats(params) {
|
|
235
|
+
return this.http.get("/accounts", params);
|
|
236
|
+
}
|
|
237
|
+
getRuleStats(params) {
|
|
238
|
+
return this.http.get("/rules", params);
|
|
239
|
+
}
|
|
240
|
+
getTemplateStats(params) {
|
|
241
|
+
return this.http.get("/templates", params);
|
|
242
|
+
}
|
|
243
|
+
triggerAggregation(data) {
|
|
244
|
+
return this.http.post("/aggregate", data);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
export {
|
|
248
|
+
AccountAPI as A,
|
|
249
|
+
HttpClient as H,
|
|
250
|
+
RuleAPI as R,
|
|
251
|
+
AnalyticsAPI as a,
|
|
252
|
+
AlxConfig as b,
|
|
253
|
+
HttpClientError as c
|
|
254
|
+
};
|
|
255
|
+
//# sourceMappingURL=index-DSTzwYDP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-DSTzwYDP.js","sources":["../src/config.ts","../src/api/http-client.ts","../src/api/account.api.ts","../src/api/rule.api.ts","../src/api/analytics.api.ts"],"sourcesContent":["export interface AlxConfigOptions {\n accountManagerApi?: string;\n ruleEngineApi?: string;\n analyticsApi?: string;\n authToken?: string;\n theme?: 'dark' | 'light';\n locale?: string;\n}\n\nconst API_KEY_MAP = {\n accountManager: 'accountManagerApi',\n ruleEngine: 'ruleEngineApi',\n analytics: 'analyticsApi',\n} as const;\n\n/**\n * Global configuration singleton for @astralibx/email-ui.\n *\n * Call `AlxConfig.setup()` once at application startup to configure\n * API base URLs, auth tokens, and theme preferences.\n *\n * @example\n * ```typescript\n * AlxConfig.setup({\n * accountManagerApi: '/api/email-accounts',\n * ruleEngineApi: '/api/email-rules',\n * analyticsApi: '/api/analytics',\n * authToken: 'Bearer xxx',\n * theme: 'dark',\n * });\n * ```\n */\nexport class AlxConfig {\n private static instance: AlxConfigOptions = {};\n\n static setup(options: AlxConfigOptions): void {\n AlxConfig.instance = { ...options };\n }\n\n static get(): AlxConfigOptions {\n return { ...AlxConfig.instance };\n }\n\n static getApiUrl(key: 'accountManager' | 'ruleEngine' | 'analytics'): string {\n const prop = API_KEY_MAP[key];\n return AlxConfig.instance[prop] ?? '';\n }\n\n static getHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (AlxConfig.instance.authToken) {\n headers['Authorization'] = AlxConfig.instance.authToken;\n }\n return headers;\n }\n\n static setAuthToken(token: string): void {\n AlxConfig.instance = { ...AlxConfig.instance, authToken: token };\n }\n}\n","import { AlxConfig } from '../config.js';\n\nexport interface PaginationParams {\n page?: number;\n limit?: number;\n}\n\nexport interface ApiResponse<T> {\n data: T;\n status: number;\n}\n\nexport interface PaginatedResponse<T> {\n data: T[];\n total: number;\n page: number;\n limit: number;\n totalPages: number;\n}\n\nexport class HttpClientError extends Error {\n constructor(\n message: string,\n public readonly status: number,\n public readonly body?: unknown,\n ) {\n super(message);\n this.name = 'HttpClientError';\n }\n}\n\nfunction buildQueryString(params?: Record<string, unknown>): string {\n if (!params) return '';\n const entries = Object.entries(params).filter(\n ([, v]) => v !== undefined && v !== null,\n );\n if (entries.length === 0) return '';\n const qs = new URLSearchParams();\n for (const [key, value] of entries) {\n qs.set(key, String(value));\n }\n return '?' + qs.toString();\n}\n\nasync function handleResponse<T>(response: Response): Promise<T> {\n if (!response.ok) {\n let body: unknown;\n try {\n body = await response.json();\n } catch {\n body = await response.text().catch(() => null);\n }\n throw new HttpClientError(\n `HTTP ${response.status}: ${response.statusText}`,\n response.status,\n body,\n );\n }\n if (response.status === 204) return undefined as T;\n return response.json() as Promise<T>;\n}\n\nexport class HttpClient {\n constructor(private baseUrl: string) {}\n\n private url(path: string, params?: Record<string, unknown>): string {\n return this.baseUrl + path + buildQueryString(params);\n }\n\n async get<T>(path: string, params?: Record<string, unknown>): Promise<T> {\n const response = await fetch(this.url(path, params), {\n method: 'GET',\n headers: AlxConfig.getHeaders(),\n });\n return handleResponse<T>(response);\n }\n\n async post<T>(path: string, body?: unknown): Promise<T> {\n const response = await fetch(this.url(path), {\n method: 'POST',\n headers: AlxConfig.getHeaders(),\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n return handleResponse<T>(response);\n }\n\n async put<T>(path: string, body?: unknown): Promise<T> {\n const response = await fetch(this.url(path), {\n method: 'PUT',\n headers: AlxConfig.getHeaders(),\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n return handleResponse<T>(response);\n }\n\n async patch<T>(path: string, body?: unknown): Promise<T> {\n const response = await fetch(this.url(path), {\n method: 'PATCH',\n headers: AlxConfig.getHeaders(),\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n return handleResponse<T>(response);\n }\n\n async delete<T>(path: string): Promise<T> {\n const response = await fetch(this.url(path), {\n method: 'DELETE',\n headers: AlxConfig.getHeaders(),\n });\n return handleResponse<T>(response);\n }\n}\n","import { AlxConfig } from '../config.js';\nimport { HttpClient, type PaginationParams } from './http-client.js';\n\n/**\n * API client for the @astralibx/email-account-manager backend.\n *\n * @example\n * ```typescript\n * const api = new AccountAPI();\n * const accounts = await api.list({ page: 1, limit: 20 });\n * ```\n */\nexport class AccountAPI {\n private http: HttpClient;\n\n constructor(baseUrl?: string) {\n this.http = new HttpClient(baseUrl ?? AlxConfig.getApiUrl('accountManager'));\n }\n\n list(params?: PaginationParams & Record<string, unknown>): Promise<any> {\n return this.http.get('/accounts', params);\n }\n\n getById(id: string): Promise<any> {\n return this.http.get(`/accounts/${id}`);\n }\n\n create(data: Record<string, unknown>): Promise<any> {\n return this.http.post('/accounts', data);\n }\n\n update(id: string, data: Record<string, unknown>): Promise<any> {\n return this.http.put(`/accounts/${id}`, data);\n }\n\n remove(id: string): Promise<any> {\n return this.http.delete(`/accounts/${id}`);\n }\n\n testConnection(id: string): Promise<any> {\n return this.http.post(`/accounts/${id}/test`);\n }\n\n getHealth(id: string): Promise<any> {\n return this.http.get(`/accounts/${id}/health`);\n }\n\n getAllHealth(): Promise<any> {\n return this.http.get('/accounts/health');\n }\n\n getCapacity(): Promise<any> {\n return this.http.get('/accounts/capacity');\n }\n\n getWarmupStatus(id: string): Promise<any> {\n return this.http.get(`/accounts/${id}/warmup`);\n }\n\n startWarmup(id: string): Promise<any> {\n return this.http.post(`/accounts/${id}/warmup/start`);\n }\n\n getSettings(): Promise<any> {\n return this.http.get('/settings');\n }\n\n updateSettings(data: Record<string, unknown>): Promise<any> {\n return this.http.patch('/settings', data);\n }\n\n listIdentifiers(params?: PaginationParams & Record<string, unknown>): Promise<any> {\n return this.http.get('/identifiers', params);\n }\n\n listDrafts(params?: PaginationParams & Record<string, unknown>): Promise<any> {\n return this.http.get('/drafts', params);\n }\n\n approveDraft(id: string): Promise<any> {\n return this.http.post(`/drafts/${id}/approve`);\n }\n\n rejectDraft(id: string): Promise<any> {\n return this.http.post(`/drafts/${id}/reject`);\n }\n\n bulkApprove(ids: string[]): Promise<any> {\n return this.http.post('/drafts/bulk-approve', { ids });\n }\n}\n","import { AlxConfig } from '../config.js';\nimport { HttpClient, type PaginationParams } from './http-client.js';\n\n/**\n * API client for the @astralibx/email-rule-engine backend.\n *\n * @example\n * ```typescript\n * const api = new RuleAPI();\n * const templates = await api.listTemplates({ page: 1, limit: 20 });\n * ```\n */\nexport class RuleAPI {\n private http: HttpClient;\n\n constructor(baseUrl?: string) {\n this.http = new HttpClient(baseUrl ?? AlxConfig.getApiUrl('ruleEngine'));\n }\n\n listTemplates(params?: PaginationParams & Record<string, unknown>): Promise<any> {\n return this.http.get('/templates', params);\n }\n\n createTemplate(data: Record<string, unknown>): Promise<any> {\n return this.http.post('/templates', data);\n }\n\n updateTemplate(id: string, data: Record<string, unknown>): Promise<any> {\n return this.http.put(`/templates/${id}`, data);\n }\n\n deleteTemplate(id: string): Promise<any> {\n return this.http.delete(`/templates/${id}`);\n }\n\n previewTemplate(data: Record<string, unknown>): Promise<any> {\n return this.http.post('/templates/preview', data);\n }\n\n listRules(params?: PaginationParams & Record<string, unknown>): Promise<any> {\n return this.http.get('/rules', params);\n }\n\n createRule(data: Record<string, unknown>): Promise<any> {\n return this.http.post('/rules', data);\n }\n\n updateRule(id: string, data: Record<string, unknown>): Promise<any> {\n return this.http.put(`/rules/${id}`, data);\n }\n\n deleteRule(id: string): Promise<any> {\n return this.http.delete(`/rules/${id}`);\n }\n\n toggleRule(id: string): Promise<any> {\n return this.http.post(`/rules/${id}/toggle`);\n }\n\n dryRun(id: string): Promise<any> {\n return this.http.post(`/rules/${id}/dry-run`);\n }\n\n triggerRun(): Promise<any> {\n return this.http.post('/runner');\n }\n\n getRunHistory(params?: PaginationParams & Record<string, unknown>): Promise<any> {\n return this.http.get('/runner/logs', params);\n }\n\n getThrottleSettings(): Promise<any> {\n return this.http.get('/throttle');\n }\n\n updateThrottleSettings(data: Record<string, unknown>): Promise<any> {\n return this.http.put('/throttle', data);\n }\n}\n","import { AlxConfig } from '../config.js';\nimport { HttpClient } from './http-client.js';\n\n/**\n * API client for the @astralibx/email-analytics backend.\n *\n * @example\n * ```typescript\n * const api = new AnalyticsAPI();\n * const overview = await api.getOverview({ from: '2025-01-01', to: '2025-01-31' });\n * ```\n */\nexport class AnalyticsAPI {\n private http: HttpClient;\n\n constructor(baseUrl?: string) {\n this.http = new HttpClient(baseUrl ?? AlxConfig.getApiUrl('analytics'));\n }\n\n getOverview(params?: Record<string, unknown>): Promise<any> {\n return this.http.get('/overview', params);\n }\n\n getTimeline(params?: Record<string, unknown>): Promise<any> {\n return this.http.get('/timeline', params);\n }\n\n getAccountStats(params?: Record<string, unknown>): Promise<any> {\n return this.http.get('/accounts', params);\n }\n\n getRuleStats(params?: Record<string, unknown>): Promise<any> {\n return this.http.get('/rules', params);\n }\n\n getTemplateStats(params?: Record<string, unknown>): Promise<any> {\n return this.http.get('/templates', params);\n }\n\n triggerAggregation(data?: Record<string, unknown>): Promise<any> {\n return this.http.post('/aggregate', data);\n }\n}\n"],"names":[],"mappings":"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"}
|