spinupmail 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +80 -0
- package/dist/index.cjs +870 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +615 -0
- package/dist/index.d.mts +615 -0
- package/dist/index.mjs +864 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +41 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,864 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
//#region ../contracts/src/index.ts
|
|
3
|
+
const apiErrorSchema = z.object({
|
|
4
|
+
error: z.string().min(1),
|
|
5
|
+
details: z.string().min(1).optional()
|
|
6
|
+
});
|
|
7
|
+
const sortDirectionSchema = z.enum(["asc", "desc"]);
|
|
8
|
+
const emailAddressSortBySchema = z.enum([
|
|
9
|
+
"createdAt",
|
|
10
|
+
"address",
|
|
11
|
+
"lastReceivedAt"
|
|
12
|
+
]);
|
|
13
|
+
const recentAddressActivitySortBySchema = z.enum(["recentActivity", "createdAt"]);
|
|
14
|
+
const maxReceivedEmailActionSchema = z.enum(["cleanAll", "rejectNew"]);
|
|
15
|
+
const emailOrderSchema = sortDirectionSchema;
|
|
16
|
+
const inboundRatePolicySchema = z.object({
|
|
17
|
+
senderDomainSoftMax: z.number().int().positive().optional(),
|
|
18
|
+
senderDomainSoftWindowSeconds: z.number().int().positive().optional(),
|
|
19
|
+
senderDomainBlockMax: z.number().int().positive().optional(),
|
|
20
|
+
senderDomainBlockWindowSeconds: z.number().int().positive().optional(),
|
|
21
|
+
senderAddressBlockMax: z.number().int().positive().optional(),
|
|
22
|
+
senderAddressBlockWindowSeconds: z.number().int().positive().optional(),
|
|
23
|
+
inboxBlockMax: z.number().int().positive().optional(),
|
|
24
|
+
inboxBlockWindowSeconds: z.number().int().positive().optional(),
|
|
25
|
+
dedupeWindowSeconds: z.number().int().positive().optional(),
|
|
26
|
+
initialBlockSeconds: z.number().int().positive().optional(),
|
|
27
|
+
maxBlockSeconds: z.number().int().positive().optional()
|
|
28
|
+
}).partial();
|
|
29
|
+
const domainConfigSchema = z.object({
|
|
30
|
+
items: z.array(z.string().min(1)),
|
|
31
|
+
default: z.string().nullable(),
|
|
32
|
+
forcedLocalPartPrefix: z.string().nullable(),
|
|
33
|
+
maxReceivedEmailsPerOrganization: z.number().int().positive(),
|
|
34
|
+
maxReceivedEmailsPerAddress: z.number().int().positive()
|
|
35
|
+
});
|
|
36
|
+
const organizationStatsItemSchema = z.object({
|
|
37
|
+
organizationId: z.string().min(1),
|
|
38
|
+
memberCount: z.number().int().nonnegative(),
|
|
39
|
+
addressCount: z.number().int().nonnegative(),
|
|
40
|
+
emailCount: z.number().int().nonnegative()
|
|
41
|
+
});
|
|
42
|
+
z.object({ items: z.array(organizationStatsItemSchema) });
|
|
43
|
+
const emailAddressSchema = z.object({
|
|
44
|
+
id: z.string().min(1),
|
|
45
|
+
address: z.string().min(1),
|
|
46
|
+
localPart: z.string().min(1),
|
|
47
|
+
domain: z.string().min(1),
|
|
48
|
+
meta: z.unknown().optional(),
|
|
49
|
+
emailCount: z.number().int().nonnegative(),
|
|
50
|
+
allowedFromDomains: z.array(z.string().min(1)).optional(),
|
|
51
|
+
blockedSenderDomains: z.array(z.string().min(1)).optional(),
|
|
52
|
+
inboundRatePolicy: inboundRatePolicySchema.nullable().optional(),
|
|
53
|
+
maxReceivedEmailCount: z.number().int().positive().nullable(),
|
|
54
|
+
maxReceivedEmailAction: maxReceivedEmailActionSchema.nullable(),
|
|
55
|
+
createdAt: z.string().nullable(),
|
|
56
|
+
createdAtMs: z.number().nullable(),
|
|
57
|
+
expiresAt: z.string().nullable(),
|
|
58
|
+
expiresAtMs: z.number().nullable(),
|
|
59
|
+
lastReceivedAt: z.string().nullable(),
|
|
60
|
+
lastReceivedAtMs: z.number().nullable()
|
|
61
|
+
});
|
|
62
|
+
const createEmailAddressResponseSchema = emailAddressSchema;
|
|
63
|
+
const emailAddressListResponseSchema = z.object({
|
|
64
|
+
items: z.array(emailAddressSchema),
|
|
65
|
+
page: z.number().int().positive(),
|
|
66
|
+
pageSize: z.number().int().positive(),
|
|
67
|
+
totalItems: z.number().int().nonnegative(),
|
|
68
|
+
addressLimit: z.number().int().positive(),
|
|
69
|
+
totalPages: z.number().int().positive(),
|
|
70
|
+
sortBy: emailAddressSortBySchema,
|
|
71
|
+
sortDirection: sortDirectionSchema
|
|
72
|
+
});
|
|
73
|
+
const listEmailAddressesParamsSchema = z.object({
|
|
74
|
+
page: z.number().int().positive().optional(),
|
|
75
|
+
pageSize: z.number().int().positive().optional(),
|
|
76
|
+
search: z.string().optional(),
|
|
77
|
+
sortBy: emailAddressSortBySchema.optional(),
|
|
78
|
+
sortDirection: sortDirectionSchema.optional()
|
|
79
|
+
});
|
|
80
|
+
const listRecentAddressActivityParamsSchema = z.object({
|
|
81
|
+
limit: z.number().int().positive().optional(),
|
|
82
|
+
cursor: z.string().min(1).optional(),
|
|
83
|
+
search: z.string().optional(),
|
|
84
|
+
sortBy: recentAddressActivitySortBySchema.optional(),
|
|
85
|
+
sortDirection: sortDirectionSchema.optional()
|
|
86
|
+
});
|
|
87
|
+
const recentAddressActivityResponseSchema = z.object({
|
|
88
|
+
items: z.array(emailAddressSchema),
|
|
89
|
+
nextCursor: z.string().nullable(),
|
|
90
|
+
totalItems: z.number().int().nonnegative()
|
|
91
|
+
});
|
|
92
|
+
const createEmailAddressRequestSchema = z.object({
|
|
93
|
+
localPart: z.string().min(1),
|
|
94
|
+
ttlMinutes: z.number().int().positive().optional(),
|
|
95
|
+
meta: z.unknown().optional(),
|
|
96
|
+
domain: z.string().min(1).optional(),
|
|
97
|
+
allowedFromDomains: z.array(z.string().min(1)).optional(),
|
|
98
|
+
blockedSenderDomains: z.array(z.string().min(1)).optional(),
|
|
99
|
+
inboundRatePolicy: inboundRatePolicySchema.optional(),
|
|
100
|
+
maxReceivedEmailCount: z.number().int().positive().optional(),
|
|
101
|
+
maxReceivedEmailAction: maxReceivedEmailActionSchema.optional(),
|
|
102
|
+
acceptedRiskNotice: z.literal(true)
|
|
103
|
+
});
|
|
104
|
+
const updateEmailAddressRequestSchema = z.object({
|
|
105
|
+
localPart: z.string().min(1).optional(),
|
|
106
|
+
ttlMinutes: z.number().int().positive().nullable().optional(),
|
|
107
|
+
meta: z.unknown().optional(),
|
|
108
|
+
domain: z.string().min(1).optional(),
|
|
109
|
+
allowedFromDomains: z.array(z.string().min(1)).optional(),
|
|
110
|
+
blockedSenderDomains: z.array(z.string().min(1)).nullable().optional(),
|
|
111
|
+
inboundRatePolicy: inboundRatePolicySchema.nullable().optional(),
|
|
112
|
+
maxReceivedEmailCount: z.number().int().positive().nullable().optional(),
|
|
113
|
+
maxReceivedEmailAction: maxReceivedEmailActionSchema.optional()
|
|
114
|
+
});
|
|
115
|
+
const deleteEmailAddressResponseSchema = z.object({
|
|
116
|
+
id: z.string().min(1),
|
|
117
|
+
address: z.string().min(1),
|
|
118
|
+
deleted: z.literal(true)
|
|
119
|
+
});
|
|
120
|
+
const emailAttachmentSchema = z.object({
|
|
121
|
+
id: z.string().min(1),
|
|
122
|
+
filename: z.string().min(1),
|
|
123
|
+
contentType: z.string().min(1),
|
|
124
|
+
size: z.number().int().nonnegative(),
|
|
125
|
+
disposition: z.string().nullable(),
|
|
126
|
+
contentId: z.string().nullable(),
|
|
127
|
+
inlinePath: z.string().min(1),
|
|
128
|
+
downloadPath: z.string().min(1)
|
|
129
|
+
});
|
|
130
|
+
const emailListItemSchema = z.object({
|
|
131
|
+
id: z.string().min(1),
|
|
132
|
+
addressId: z.string().min(1),
|
|
133
|
+
to: z.string().min(1),
|
|
134
|
+
from: z.string().min(1),
|
|
135
|
+
sender: z.string().nullable().optional(),
|
|
136
|
+
senderLabel: z.string().min(1),
|
|
137
|
+
subject: z.string().nullable().optional(),
|
|
138
|
+
messageId: z.string().nullable().optional(),
|
|
139
|
+
rawSize: z.number().nullable().optional(),
|
|
140
|
+
rawTruncated: z.boolean(),
|
|
141
|
+
isSample: z.boolean(),
|
|
142
|
+
hasHtml: z.boolean(),
|
|
143
|
+
hasText: z.boolean(),
|
|
144
|
+
attachmentCount: z.number().int().nonnegative(),
|
|
145
|
+
receivedAt: z.string().nullable(),
|
|
146
|
+
receivedAtMs: z.number().nullable()
|
|
147
|
+
});
|
|
148
|
+
const emailListResponseSchema = z.object({
|
|
149
|
+
address: z.string().min(1),
|
|
150
|
+
addressId: z.string().min(1),
|
|
151
|
+
items: z.array(emailListItemSchema)
|
|
152
|
+
});
|
|
153
|
+
const listEmailsParamsSchema = z.object({
|
|
154
|
+
address: z.string().min(1).optional(),
|
|
155
|
+
addressId: z.string().min(1).optional(),
|
|
156
|
+
search: z.string().max(30).optional(),
|
|
157
|
+
limit: z.number().int().positive().optional(),
|
|
158
|
+
order: emailOrderSchema.optional(),
|
|
159
|
+
after: z.union([z.string(), z.number()]).optional(),
|
|
160
|
+
before: z.union([z.string(), z.number()]).optional()
|
|
161
|
+
});
|
|
162
|
+
const emailDetailSchema = z.object({
|
|
163
|
+
id: z.string().min(1),
|
|
164
|
+
addressId: z.string().min(1),
|
|
165
|
+
address: z.string().min(1).optional(),
|
|
166
|
+
to: z.string().min(1),
|
|
167
|
+
from: z.string().min(1),
|
|
168
|
+
sender: z.string().nullable().optional(),
|
|
169
|
+
senderLabel: z.string().min(1),
|
|
170
|
+
subject: z.string().nullable().optional(),
|
|
171
|
+
messageId: z.string().nullable().optional(),
|
|
172
|
+
headers: z.unknown(),
|
|
173
|
+
html: z.string().nullable().optional(),
|
|
174
|
+
text: z.string().nullable().optional(),
|
|
175
|
+
raw: z.string().nullable().optional(),
|
|
176
|
+
rawSize: z.number().nullable().optional(),
|
|
177
|
+
rawTruncated: z.boolean(),
|
|
178
|
+
isSample: z.boolean(),
|
|
179
|
+
rawDownloadPath: z.string().optional(),
|
|
180
|
+
attachments: z.array(emailAttachmentSchema),
|
|
181
|
+
receivedAt: z.string().nullable(),
|
|
182
|
+
receivedAtMs: z.number().nullable()
|
|
183
|
+
});
|
|
184
|
+
const deleteEmailResponseSchema = z.object({
|
|
185
|
+
id: z.string().min(1),
|
|
186
|
+
deleted: z.literal(true)
|
|
187
|
+
});
|
|
188
|
+
const emailActivityDaySchema = z.object({
|
|
189
|
+
date: z.string().min(1),
|
|
190
|
+
count: z.number().int().nonnegative()
|
|
191
|
+
});
|
|
192
|
+
const emailActivityResponseSchema = z.object({
|
|
193
|
+
timezone: z.string().min(1),
|
|
194
|
+
daily: z.array(emailActivityDaySchema)
|
|
195
|
+
});
|
|
196
|
+
const emailSummaryDomainSchema = z.object({
|
|
197
|
+
domain: z.string().min(1),
|
|
198
|
+
count: z.number().int().nonnegative()
|
|
199
|
+
});
|
|
200
|
+
const busiestInboxSchema = z.object({
|
|
201
|
+
addressId: z.string().min(1),
|
|
202
|
+
address: z.string().min(1),
|
|
203
|
+
count: z.number().int().nonnegative()
|
|
204
|
+
});
|
|
205
|
+
const dormantInboxSchema = z.object({
|
|
206
|
+
addressId: z.string().min(1),
|
|
207
|
+
address: z.string().min(1),
|
|
208
|
+
createdAt: z.string().nullable()
|
|
209
|
+
});
|
|
210
|
+
const emailSummaryResponseSchema = z.object({
|
|
211
|
+
totalEmailCount: z.number().int().nonnegative(),
|
|
212
|
+
attachmentCount: z.number().int().nonnegative(),
|
|
213
|
+
attachmentSizeTotal: z.number().int().nonnegative(),
|
|
214
|
+
attachmentSizeLimit: z.number().int().nonnegative(),
|
|
215
|
+
topDomains: z.array(emailSummaryDomainSchema),
|
|
216
|
+
busiestInboxes: z.array(busiestInboxSchema),
|
|
217
|
+
dormantInboxes: z.array(dormantInboxSchema)
|
|
218
|
+
});
|
|
219
|
+
const organizationPickerItemSchema = z.object({
|
|
220
|
+
id: z.string().min(1),
|
|
221
|
+
name: z.string().min(1),
|
|
222
|
+
slug: z.string().min(1),
|
|
223
|
+
logo: z.string().nullable().optional()
|
|
224
|
+
});
|
|
225
|
+
const extensionBootstrapUserSchema = z.object({
|
|
226
|
+
id: z.string().min(1),
|
|
227
|
+
email: z.string().email().nullable(),
|
|
228
|
+
name: z.string().nullable(),
|
|
229
|
+
image: z.string().nullable(),
|
|
230
|
+
emailVerified: z.boolean()
|
|
231
|
+
});
|
|
232
|
+
z.object({
|
|
233
|
+
user: extensionBootstrapUserSchema,
|
|
234
|
+
organizations: z.array(organizationPickerItemSchema),
|
|
235
|
+
defaultOrganizationId: z.string().min(1).nullable()
|
|
236
|
+
});
|
|
237
|
+
//#endregion
|
|
238
|
+
//#region src/errors.ts
|
|
239
|
+
var SpinupMailError = class extends Error {
|
|
240
|
+
constructor(message, options) {
|
|
241
|
+
super(message, options);
|
|
242
|
+
this.name = new.target.name;
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
var SpinupMailValidationError = class extends SpinupMailError {
|
|
246
|
+
source;
|
|
247
|
+
issues;
|
|
248
|
+
constructor(args) {
|
|
249
|
+
super(args.message, args.cause ? { cause: args.cause } : void 0);
|
|
250
|
+
this.source = args.source;
|
|
251
|
+
this.issues = args.issues ?? [];
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
var SpinupMailApiError = class extends SpinupMailError {
|
|
255
|
+
status;
|
|
256
|
+
response;
|
|
257
|
+
body;
|
|
258
|
+
constructor(args) {
|
|
259
|
+
super(args.message);
|
|
260
|
+
this.status = args.status;
|
|
261
|
+
this.response = args.response;
|
|
262
|
+
this.body = args.body;
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
var SpinupMailTimeoutError = class extends SpinupMailError {
|
|
266
|
+
timeoutMs;
|
|
267
|
+
constructor(message, timeoutMs, options) {
|
|
268
|
+
super(message, options);
|
|
269
|
+
this.timeoutMs = timeoutMs;
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
//#endregion
|
|
273
|
+
//#region src/file.ts
|
|
274
|
+
const parseFilenameFromDisposition = (headerValue) => {
|
|
275
|
+
if (!headerValue) return null;
|
|
276
|
+
const utf8Match = headerValue.match(/filename\*=UTF-8''([^;]+)/i);
|
|
277
|
+
if (utf8Match?.[1]) try {
|
|
278
|
+
return decodeURIComponent(utf8Match[1]);
|
|
279
|
+
} catch {
|
|
280
|
+
return utf8Match[1];
|
|
281
|
+
}
|
|
282
|
+
const fallbackMatch = headerValue.match(/filename="([^"]+)"/i);
|
|
283
|
+
if (fallbackMatch?.[1]) return fallbackMatch[1];
|
|
284
|
+
return null;
|
|
285
|
+
};
|
|
286
|
+
var SpinupMailFile = class {
|
|
287
|
+
filename;
|
|
288
|
+
contentType;
|
|
289
|
+
contentLength;
|
|
290
|
+
response;
|
|
291
|
+
constructor(response) {
|
|
292
|
+
this.response = response;
|
|
293
|
+
this.filename = parseFilenameFromDisposition(response.headers.get("content-disposition"));
|
|
294
|
+
this.contentType = response.headers.get("content-type");
|
|
295
|
+
const contentLength = response.headers.get("content-length");
|
|
296
|
+
const parsed = contentLength ? Number(contentLength) : NaN;
|
|
297
|
+
this.contentLength = Number.isFinite(parsed) ? parsed : null;
|
|
298
|
+
}
|
|
299
|
+
arrayBuffer() {
|
|
300
|
+
return this.response.clone().arrayBuffer();
|
|
301
|
+
}
|
|
302
|
+
text() {
|
|
303
|
+
return this.response.clone().text();
|
|
304
|
+
}
|
|
305
|
+
blob() {
|
|
306
|
+
const clone = this.response.clone();
|
|
307
|
+
if (typeof clone.blob !== "function") throw new Error("Response.blob() is not available in this runtime.");
|
|
308
|
+
return clone.blob();
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
//#endregion
|
|
312
|
+
//#region src/client.ts
|
|
313
|
+
const DEFAULT_LIST_ALL_PAGE_SIZE = 50;
|
|
314
|
+
const MAX_LIST_ALL_PAGES = 200;
|
|
315
|
+
const DEFAULT_WAIT_TIMEOUT_MS = 3e4;
|
|
316
|
+
const DEFAULT_POLL_INTERVAL_MS = 1e3;
|
|
317
|
+
const DEFAULT_SPINUPMAIL_BASE_URL = "https://api.spinupmail.com";
|
|
318
|
+
const RANDOM_LOCAL_PART_PREFIX = "sum";
|
|
319
|
+
const RANDOM_LOCAL_PART_SIZE = 12;
|
|
320
|
+
const issuePathToString = (path) => path.length === 0 ? "<root>" : path.map(String).join(".");
|
|
321
|
+
const formatSchemaIssues = (issues) => issues.map((issue) => `${issuePathToString(issue.path)}: ${issue.message}`);
|
|
322
|
+
const validateWithSchema = (schema, value, source, message) => {
|
|
323
|
+
const parsed = schema.safeParse(value);
|
|
324
|
+
if (!parsed.success) throw new SpinupMailValidationError({
|
|
325
|
+
message,
|
|
326
|
+
source,
|
|
327
|
+
issues: formatSchemaIssues(parsed.error.issues),
|
|
328
|
+
cause: parsed.error
|
|
329
|
+
});
|
|
330
|
+
return parsed.data;
|
|
331
|
+
};
|
|
332
|
+
const normalizeString = (value, label) => {
|
|
333
|
+
const normalized = value.trim();
|
|
334
|
+
if (!normalized) throw new SpinupMailValidationError({
|
|
335
|
+
message: `${label} is required.`,
|
|
336
|
+
source: "request"
|
|
337
|
+
});
|
|
338
|
+
return normalized;
|
|
339
|
+
};
|
|
340
|
+
const resolveFetch = (candidate) => {
|
|
341
|
+
if (candidate) return candidate;
|
|
342
|
+
if (typeof globalThis.fetch === "function") return globalThis.fetch.bind(globalThis);
|
|
343
|
+
throw new SpinupMailValidationError({
|
|
344
|
+
message: "A fetch implementation is required in this runtime.",
|
|
345
|
+
source: "request"
|
|
346
|
+
});
|
|
347
|
+
};
|
|
348
|
+
const normalizeBaseUrl = (baseUrl) => normalizeString(baseUrl, "baseUrl").replace(/\/+$/, "");
|
|
349
|
+
const getProcessEnv = () => {
|
|
350
|
+
if (typeof process !== "undefined" && typeof process.env === "object" && process.env !== null) return process.env;
|
|
351
|
+
};
|
|
352
|
+
const readEnvValue = (keys) => {
|
|
353
|
+
const env = getProcessEnv();
|
|
354
|
+
if (!env) return void 0;
|
|
355
|
+
for (const key of keys) {
|
|
356
|
+
const value = env[key]?.trim();
|
|
357
|
+
if (value) return value;
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
const createRandomBytes = (size) => {
|
|
361
|
+
if (typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function") return crypto.getRandomValues(new Uint8Array(size));
|
|
362
|
+
return Uint8Array.from(Array.from({ length: size }, () => Math.floor(Math.random() * 256)));
|
|
363
|
+
};
|
|
364
|
+
const generateRandomLocalPart = () => {
|
|
365
|
+
const alphabet = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
366
|
+
const bytes = createRandomBytes(RANDOM_LOCAL_PART_SIZE);
|
|
367
|
+
let suffix = "";
|
|
368
|
+
for (const byte of bytes) suffix += alphabet[byte % 36];
|
|
369
|
+
return `${RANDOM_LOCAL_PART_PREFIX}-${suffix}`;
|
|
370
|
+
};
|
|
371
|
+
const resolveOrganizationId = (context, organizationId, orgScoped) => {
|
|
372
|
+
if (!orgScoped) return void 0;
|
|
373
|
+
const resolved = organizationId ?? context.organizationId;
|
|
374
|
+
if (!resolved?.trim()) throw new SpinupMailValidationError({
|
|
375
|
+
message: "organizationId is required for this SpinupMail API method when using API keys.",
|
|
376
|
+
source: "request"
|
|
377
|
+
});
|
|
378
|
+
return resolved.trim();
|
|
379
|
+
};
|
|
380
|
+
const normalizeTimestamp = (value) => {
|
|
381
|
+
if (value === void 0) return void 0;
|
|
382
|
+
if (value instanceof Date) return value.toISOString();
|
|
383
|
+
return typeof value === "number" ? String(value) : value;
|
|
384
|
+
};
|
|
385
|
+
const normalizeText = (value) => (value ?? "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
386
|
+
const matchesText = (value, expected) => {
|
|
387
|
+
if (!expected?.trim()) return true;
|
|
388
|
+
return normalizeText(value).includes(normalizeText(expected));
|
|
389
|
+
};
|
|
390
|
+
const matchesAnyText = (values, expected) => {
|
|
391
|
+
if (!expected?.trim()) return true;
|
|
392
|
+
return values.some((value) => matchesText(value, expected));
|
|
393
|
+
};
|
|
394
|
+
const createQueryString = (values) => {
|
|
395
|
+
const query = new URLSearchParams();
|
|
396
|
+
for (const [key, value] of Object.entries(values)) {
|
|
397
|
+
if (value === void 0) continue;
|
|
398
|
+
query.set(key, String(value));
|
|
399
|
+
}
|
|
400
|
+
const serialized = query.toString();
|
|
401
|
+
return serialized.length > 0 ? `?${serialized}` : "";
|
|
402
|
+
};
|
|
403
|
+
const parseErrorPayload = async (response) => {
|
|
404
|
+
try {
|
|
405
|
+
const payload = await response.clone().json();
|
|
406
|
+
const parsed = apiErrorSchema.safeParse(payload);
|
|
407
|
+
if (parsed.success) return parsed.data;
|
|
408
|
+
return payload;
|
|
409
|
+
} catch {
|
|
410
|
+
const text = await response.clone().text();
|
|
411
|
+
return text ? { error: text } : void 0;
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
const requestJson = async (context, options) => {
|
|
415
|
+
const headers = new Headers(context.headers);
|
|
416
|
+
headers.set("x-api-key", context.apiKey);
|
|
417
|
+
headers.set("accept", "application/json");
|
|
418
|
+
const resolvedOrganizationId = resolveOrganizationId(context, options.organizationId, options.orgScoped ?? false);
|
|
419
|
+
if (resolvedOrganizationId) headers.set("x-org-id", resolvedOrganizationId);
|
|
420
|
+
let body;
|
|
421
|
+
if (options.body !== void 0) {
|
|
422
|
+
headers.set("content-type", "application/json");
|
|
423
|
+
body = JSON.stringify(options.body);
|
|
424
|
+
}
|
|
425
|
+
const response = await context.fetch(`${context.baseUrl}${options.path}`, {
|
|
426
|
+
method: options.method ?? "GET",
|
|
427
|
+
headers,
|
|
428
|
+
body,
|
|
429
|
+
signal: options.signal
|
|
430
|
+
});
|
|
431
|
+
if (!response.ok) {
|
|
432
|
+
const errorBody = await parseErrorPayload(response);
|
|
433
|
+
throw new SpinupMailApiError({
|
|
434
|
+
message: typeof errorBody === "object" && errorBody !== null && "error" in errorBody && typeof errorBody.error === "string" ? errorBody.error : response.statusText || "SpinupMail request failed",
|
|
435
|
+
status: response.status,
|
|
436
|
+
response,
|
|
437
|
+
body: errorBody
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
let payload;
|
|
441
|
+
try {
|
|
442
|
+
payload = await response.json();
|
|
443
|
+
} catch (error) {
|
|
444
|
+
throw new SpinupMailValidationError({
|
|
445
|
+
message: "SpinupMail returned a non-JSON success response.",
|
|
446
|
+
source: "response",
|
|
447
|
+
cause: error
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
return validateWithSchema(options.responseSchema, payload, "response", "SpinupMail returned an unexpected response shape.");
|
|
451
|
+
};
|
|
452
|
+
const requestBinary = async (context, options) => {
|
|
453
|
+
const headers = new Headers(context.headers);
|
|
454
|
+
headers.set("x-api-key", context.apiKey);
|
|
455
|
+
const resolvedOrganizationId = resolveOrganizationId(context, options.organizationId, options.orgScoped ?? false);
|
|
456
|
+
if (resolvedOrganizationId) headers.set("x-org-id", resolvedOrganizationId);
|
|
457
|
+
const response = await context.fetch(`${context.baseUrl}${options.path}`, {
|
|
458
|
+
method: "GET",
|
|
459
|
+
headers,
|
|
460
|
+
signal: options.signal
|
|
461
|
+
});
|
|
462
|
+
if (!response.ok) {
|
|
463
|
+
const errorBody = await parseErrorPayload(response);
|
|
464
|
+
throw new SpinupMailApiError({
|
|
465
|
+
message: typeof errorBody === "object" && errorBody !== null && "error" in errorBody && typeof errorBody.error === "string" ? errorBody.error : response.statusText || "SpinupMail request failed",
|
|
466
|
+
status: response.status,
|
|
467
|
+
response,
|
|
468
|
+
body: errorBody
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
return new SpinupMailFile(response);
|
|
472
|
+
};
|
|
473
|
+
const ensureInboxSelector = (options) => {
|
|
474
|
+
if (!options.address && !options.addressId) throw new SpinupMailValidationError({
|
|
475
|
+
message: "Either address or addressId is required.",
|
|
476
|
+
source: "request"
|
|
477
|
+
});
|
|
478
|
+
};
|
|
479
|
+
const validateListEmailsOptions = (options) => {
|
|
480
|
+
ensureInboxSelector(options);
|
|
481
|
+
validateWithSchema(listEmailsParamsSchema, {
|
|
482
|
+
...options,
|
|
483
|
+
after: normalizeTimestamp(options.after),
|
|
484
|
+
before: normalizeTimestamp(options.before)
|
|
485
|
+
}, "request", "Invalid listEmails options.");
|
|
486
|
+
if (options.search && (options.after !== void 0 || options.before !== void 0 || options.order === "asc")) throw new SpinupMailValidationError({
|
|
487
|
+
message: "search does not support after, before, or order='asc' parameters.",
|
|
488
|
+
source: "request"
|
|
489
|
+
});
|
|
490
|
+
};
|
|
491
|
+
const matchesListItemFilters = (item, options) => {
|
|
492
|
+
if (!matchesText(item.subject, options.subjectIncludes)) return false;
|
|
493
|
+
if (!matchesText(item.to, options.toIncludes)) return false;
|
|
494
|
+
if (!matchesAnyText([
|
|
495
|
+
item.from,
|
|
496
|
+
item.sender,
|
|
497
|
+
item.senderLabel
|
|
498
|
+
], options.fromIncludes)) return false;
|
|
499
|
+
return options.match ? options.match(item) : true;
|
|
500
|
+
};
|
|
501
|
+
const matchesDetailFilters = (detail, options) => {
|
|
502
|
+
if (!matchesText(`${detail.html ?? ""}\n${detail.text ?? ""}`, options.bodyIncludes)) return false;
|
|
503
|
+
return options.matchDetail ? options.matchDetail(detail) : true;
|
|
504
|
+
};
|
|
505
|
+
const sleep = (ms, signal) => new Promise((resolve, reject) => {
|
|
506
|
+
if (signal?.aborted) {
|
|
507
|
+
reject(signal.reason ?? /* @__PURE__ */ new Error("The operation was aborted."));
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
const timeoutId = setTimeout(() => {
|
|
511
|
+
cleanup();
|
|
512
|
+
resolve();
|
|
513
|
+
}, ms);
|
|
514
|
+
const onAbort = () => {
|
|
515
|
+
clearTimeout(timeoutId);
|
|
516
|
+
cleanup();
|
|
517
|
+
reject(signal?.reason ?? /* @__PURE__ */ new Error("The operation was aborted."));
|
|
518
|
+
};
|
|
519
|
+
const cleanup = () => {
|
|
520
|
+
signal?.removeEventListener("abort", onAbort);
|
|
521
|
+
};
|
|
522
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
523
|
+
});
|
|
524
|
+
const runPollingLoop = async (emails, options, args) => {
|
|
525
|
+
ensureInboxSelector(options);
|
|
526
|
+
validateListEmailsOptions({
|
|
527
|
+
address: options.address,
|
|
528
|
+
addressId: options.addressId,
|
|
529
|
+
search: options.search,
|
|
530
|
+
limit: options.limit,
|
|
531
|
+
order: options.order,
|
|
532
|
+
after: options.after,
|
|
533
|
+
before: options.before,
|
|
534
|
+
organizationId: options.organizationId,
|
|
535
|
+
signal: options.signal
|
|
536
|
+
});
|
|
537
|
+
const startedAt = Date.now();
|
|
538
|
+
const deadline = args.timeoutMs > 0 ? startedAt + args.timeoutMs : Number.NEGATIVE_INFINITY;
|
|
539
|
+
const seenEmailIds = /* @__PURE__ */ new Set();
|
|
540
|
+
let attempts = 0;
|
|
541
|
+
let lastResponse;
|
|
542
|
+
let lastFreshItems;
|
|
543
|
+
while (true) {
|
|
544
|
+
attempts += 1;
|
|
545
|
+
lastResponse = await emails.list({
|
|
546
|
+
address: options.address,
|
|
547
|
+
addressId: options.addressId,
|
|
548
|
+
search: options.search,
|
|
549
|
+
limit: options.limit,
|
|
550
|
+
order: options.order,
|
|
551
|
+
after: options.after,
|
|
552
|
+
before: options.before,
|
|
553
|
+
organizationId: options.organizationId,
|
|
554
|
+
signal: options.signal
|
|
555
|
+
});
|
|
556
|
+
lastFreshItems = lastResponse.items.filter((item) => {
|
|
557
|
+
if (seenEmailIds.has(item.id)) return false;
|
|
558
|
+
seenEmailIds.add(item.id);
|
|
559
|
+
return true;
|
|
560
|
+
});
|
|
561
|
+
const matchedEmail = lastFreshItems.find((item) => matchesListItemFilters(item, options)) ?? null;
|
|
562
|
+
if (matchedEmail) return {
|
|
563
|
+
response: lastResponse,
|
|
564
|
+
items: lastResponse.items,
|
|
565
|
+
freshItems: lastFreshItems,
|
|
566
|
+
matchedEmail,
|
|
567
|
+
timedOut: false,
|
|
568
|
+
attempts,
|
|
569
|
+
elapsedMs: Date.now() - startedAt,
|
|
570
|
+
polledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
571
|
+
};
|
|
572
|
+
if (args.timeoutMs <= 0 || Date.now() >= deadline) break;
|
|
573
|
+
const remainingMs = deadline - Date.now();
|
|
574
|
+
if (remainingMs <= 0) break;
|
|
575
|
+
await sleep(Math.min(options.intervalMs ?? DEFAULT_POLL_INTERVAL_MS, remainingMs), options.signal);
|
|
576
|
+
}
|
|
577
|
+
const result = {
|
|
578
|
+
response: lastResponse,
|
|
579
|
+
items: lastResponse.items,
|
|
580
|
+
freshItems: lastFreshItems,
|
|
581
|
+
matchedEmail: null,
|
|
582
|
+
timedOut: args.timeoutMs > 0,
|
|
583
|
+
attempts,
|
|
584
|
+
elapsedMs: Date.now() - startedAt,
|
|
585
|
+
polledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
586
|
+
};
|
|
587
|
+
if (args.throwOnTimeout) throw new SpinupMailTimeoutError(`No matching email arrived before the ${args.timeoutMs}ms timeout elapsed.`, args.timeoutMs);
|
|
588
|
+
return result;
|
|
589
|
+
};
|
|
590
|
+
const waitForEmailDetail = async (emails, options) => {
|
|
591
|
+
ensureInboxSelector(options);
|
|
592
|
+
validateListEmailsOptions({
|
|
593
|
+
address: options.address,
|
|
594
|
+
addressId: options.addressId,
|
|
595
|
+
search: options.search,
|
|
596
|
+
limit: options.limit,
|
|
597
|
+
order: options.order,
|
|
598
|
+
after: options.after,
|
|
599
|
+
before: options.before,
|
|
600
|
+
organizationId: options.organizationId,
|
|
601
|
+
signal: options.signal
|
|
602
|
+
});
|
|
603
|
+
const startedAt = Date.now();
|
|
604
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS;
|
|
605
|
+
const deadline = timeoutMs > 0 ? startedAt + timeoutMs : Number.NEGATIVE_INFINITY;
|
|
606
|
+
const seenEmailIds = /* @__PURE__ */ new Set();
|
|
607
|
+
while (true) {
|
|
608
|
+
const freshCandidates = (await emails.list({
|
|
609
|
+
address: options.address,
|
|
610
|
+
addressId: options.addressId,
|
|
611
|
+
search: options.search,
|
|
612
|
+
limit: options.limit,
|
|
613
|
+
order: options.order,
|
|
614
|
+
after: options.after,
|
|
615
|
+
before: options.before,
|
|
616
|
+
organizationId: options.organizationId,
|
|
617
|
+
signal: options.signal
|
|
618
|
+
})).items.filter((item) => {
|
|
619
|
+
if (seenEmailIds.has(item.id)) return false;
|
|
620
|
+
seenEmailIds.add(item.id);
|
|
621
|
+
return matchesListItemFilters(item, options);
|
|
622
|
+
});
|
|
623
|
+
for (const item of freshCandidates) {
|
|
624
|
+
const detail = await emails.get(item.id, {
|
|
625
|
+
organizationId: options.organizationId,
|
|
626
|
+
signal: options.signal
|
|
627
|
+
});
|
|
628
|
+
if (!matchesDetailFilters(detail, options)) continue;
|
|
629
|
+
if (options.deleteAfterRead) await emails.delete(item.id, {
|
|
630
|
+
organizationId: options.organizationId,
|
|
631
|
+
signal: options.signal
|
|
632
|
+
});
|
|
633
|
+
return detail;
|
|
634
|
+
}
|
|
635
|
+
if (timeoutMs <= 0 || Date.now() >= deadline) break;
|
|
636
|
+
const remainingMs = deadline - Date.now();
|
|
637
|
+
if (remainingMs <= 0) break;
|
|
638
|
+
await sleep(Math.min(options.intervalMs ?? DEFAULT_POLL_INTERVAL_MS, remainingMs), options.signal);
|
|
639
|
+
}
|
|
640
|
+
throw new SpinupMailTimeoutError(`No matching email arrived before the ${timeoutMs}ms timeout elapsed.`, timeoutMs);
|
|
641
|
+
};
|
|
642
|
+
const createSpinupMailClient = (options) => {
|
|
643
|
+
const context = {
|
|
644
|
+
baseUrl: normalizeBaseUrl(options.baseUrl),
|
|
645
|
+
apiKey: normalizeString(options.apiKey, "apiKey"),
|
|
646
|
+
organizationId: options.organizationId?.trim() || void 0,
|
|
647
|
+
fetch: resolveFetch(options.fetch),
|
|
648
|
+
headers: options.headers
|
|
649
|
+
};
|
|
650
|
+
const addresses = {
|
|
651
|
+
list: async (options = {}) => {
|
|
652
|
+
const validated = validateWithSchema(listEmailAddressesParamsSchema, options, "request", "Invalid listEmailAddresses options.");
|
|
653
|
+
return requestJson(context, {
|
|
654
|
+
path: `/api/email-addresses${createQueryString({
|
|
655
|
+
page: validated.page,
|
|
656
|
+
pageSize: validated.pageSize,
|
|
657
|
+
search: validated.search,
|
|
658
|
+
sortBy: validated.sortBy,
|
|
659
|
+
sortDirection: validated.sortDirection
|
|
660
|
+
})}`,
|
|
661
|
+
responseSchema: emailAddressListResponseSchema,
|
|
662
|
+
organizationId: options.organizationId,
|
|
663
|
+
orgScoped: true,
|
|
664
|
+
signal: options.signal
|
|
665
|
+
});
|
|
666
|
+
},
|
|
667
|
+
listAll: async (options = {}) => {
|
|
668
|
+
const items = [];
|
|
669
|
+
let page = 1;
|
|
670
|
+
let totalPages = 1;
|
|
671
|
+
while (page <= totalPages) {
|
|
672
|
+
if (page > MAX_LIST_ALL_PAGES) throw new SpinupMailValidationError({
|
|
673
|
+
message: `Address pagination exceeded the safety limit of ${MAX_LIST_ALL_PAGES} pages.`,
|
|
674
|
+
source: "request"
|
|
675
|
+
});
|
|
676
|
+
const response = await addresses.list({
|
|
677
|
+
...options,
|
|
678
|
+
page,
|
|
679
|
+
pageSize: options.pageSize ?? DEFAULT_LIST_ALL_PAGE_SIZE
|
|
680
|
+
});
|
|
681
|
+
items.push(...response.items);
|
|
682
|
+
totalPages = response.totalPages;
|
|
683
|
+
page += 1;
|
|
684
|
+
}
|
|
685
|
+
return items;
|
|
686
|
+
},
|
|
687
|
+
listRecentActivity: async (options = {}) => {
|
|
688
|
+
const validated = validateWithSchema(listRecentAddressActivityParamsSchema, options, "request", "Invalid listRecentAddressActivity options.");
|
|
689
|
+
return requestJson(context, {
|
|
690
|
+
path: `/api/email-addresses/recent-activity${createQueryString({
|
|
691
|
+
limit: validated.limit,
|
|
692
|
+
cursor: validated.cursor,
|
|
693
|
+
search: validated.search,
|
|
694
|
+
sortBy: validated.sortBy,
|
|
695
|
+
sortDirection: validated.sortDirection
|
|
696
|
+
})}`,
|
|
697
|
+
responseSchema: recentAddressActivityResponseSchema,
|
|
698
|
+
organizationId: options.organizationId,
|
|
699
|
+
orgScoped: true,
|
|
700
|
+
signal: options.signal
|
|
701
|
+
});
|
|
702
|
+
},
|
|
703
|
+
get: (addressId, options = {}) => requestJson(context, {
|
|
704
|
+
path: `/api/email-addresses/${encodeURIComponent(normalizeString(addressId, "addressId"))}`,
|
|
705
|
+
responseSchema: emailAddressSchema,
|
|
706
|
+
organizationId: options.organizationId,
|
|
707
|
+
orgScoped: true,
|
|
708
|
+
signal: options.signal
|
|
709
|
+
}),
|
|
710
|
+
create: (payload, options = {}) => {
|
|
711
|
+
return requestJson(context, {
|
|
712
|
+
method: "POST",
|
|
713
|
+
path: "/api/email-addresses",
|
|
714
|
+
responseSchema: createEmailAddressResponseSchema,
|
|
715
|
+
body: validateWithSchema(createEmailAddressRequestSchema, {
|
|
716
|
+
...payload,
|
|
717
|
+
localPart: payload.localPart?.trim() || generateRandomLocalPart()
|
|
718
|
+
}, "request", "Invalid createEmailAddress payload."),
|
|
719
|
+
organizationId: options.organizationId,
|
|
720
|
+
orgScoped: true,
|
|
721
|
+
signal: options.signal
|
|
722
|
+
});
|
|
723
|
+
},
|
|
724
|
+
update: (addressId, payload, options = {}) => requestJson(context, {
|
|
725
|
+
method: "PATCH",
|
|
726
|
+
path: `/api/email-addresses/${encodeURIComponent(normalizeString(addressId, "addressId"))}`,
|
|
727
|
+
responseSchema: emailAddressSchema,
|
|
728
|
+
body: validateWithSchema(updateEmailAddressRequestSchema, payload, "request", "Invalid updateEmailAddress payload."),
|
|
729
|
+
organizationId: options.organizationId,
|
|
730
|
+
orgScoped: true,
|
|
731
|
+
signal: options.signal
|
|
732
|
+
}),
|
|
733
|
+
delete: (addressId, options = {}) => requestJson(context, {
|
|
734
|
+
method: "DELETE",
|
|
735
|
+
path: `/api/email-addresses/${encodeURIComponent(normalizeString(addressId, "addressId"))}`,
|
|
736
|
+
responseSchema: deleteEmailAddressResponseSchema,
|
|
737
|
+
organizationId: options.organizationId,
|
|
738
|
+
orgScoped: true,
|
|
739
|
+
signal: options.signal
|
|
740
|
+
})
|
|
741
|
+
};
|
|
742
|
+
const emails = {
|
|
743
|
+
list: async (options) => {
|
|
744
|
+
validateListEmailsOptions(options);
|
|
745
|
+
return requestJson(context, {
|
|
746
|
+
path: `/api/emails${createQueryString({
|
|
747
|
+
address: options.address,
|
|
748
|
+
addressId: options.addressId,
|
|
749
|
+
search: options.search,
|
|
750
|
+
limit: options.limit,
|
|
751
|
+
order: options.order,
|
|
752
|
+
after: normalizeTimestamp(options.after),
|
|
753
|
+
before: normalizeTimestamp(options.before)
|
|
754
|
+
})}`,
|
|
755
|
+
responseSchema: emailListResponseSchema,
|
|
756
|
+
organizationId: options.organizationId,
|
|
757
|
+
orgScoped: true,
|
|
758
|
+
signal: options.signal
|
|
759
|
+
});
|
|
760
|
+
},
|
|
761
|
+
get: (emailId, options = {}) => requestJson(context, {
|
|
762
|
+
path: `/api/emails/${encodeURIComponent(normalizeString(emailId, "emailId"))}${createQueryString({ raw: options.raw ? 1 : void 0 })}`,
|
|
763
|
+
responseSchema: emailDetailSchema,
|
|
764
|
+
organizationId: options.organizationId,
|
|
765
|
+
orgScoped: true,
|
|
766
|
+
signal: options.signal
|
|
767
|
+
}),
|
|
768
|
+
delete: (emailId, options = {}) => requestJson(context, {
|
|
769
|
+
method: "DELETE",
|
|
770
|
+
path: `/api/emails/${encodeURIComponent(normalizeString(emailId, "emailId"))}`,
|
|
771
|
+
responseSchema: deleteEmailResponseSchema,
|
|
772
|
+
organizationId: options.organizationId,
|
|
773
|
+
orgScoped: true,
|
|
774
|
+
signal: options.signal
|
|
775
|
+
}),
|
|
776
|
+
getRaw: (emailId, options = {}) => requestBinary(context, {
|
|
777
|
+
path: `/api/emails/${encodeURIComponent(normalizeString(emailId, "emailId"))}/raw`,
|
|
778
|
+
organizationId: options.organizationId,
|
|
779
|
+
orgScoped: true,
|
|
780
|
+
signal: options.signal
|
|
781
|
+
}),
|
|
782
|
+
getAttachment: (emailId, attachmentId, options = {}) => requestBinary(context, {
|
|
783
|
+
path: `/api/emails/${encodeURIComponent(normalizeString(emailId, "emailId"))}/attachments/${encodeURIComponent(normalizeString(attachmentId, "attachmentId"))}${createQueryString({ inline: options.inline ? 1 : void 0 })}`,
|
|
784
|
+
organizationId: options.organizationId,
|
|
785
|
+
orgScoped: true,
|
|
786
|
+
signal: options.signal
|
|
787
|
+
})
|
|
788
|
+
};
|
|
789
|
+
return {
|
|
790
|
+
domains: { get: () => requestJson(context, {
|
|
791
|
+
path: "/api/domains",
|
|
792
|
+
responseSchema: domainConfigSchema
|
|
793
|
+
}) },
|
|
794
|
+
addresses,
|
|
795
|
+
emails,
|
|
796
|
+
stats: {
|
|
797
|
+
getEmailActivity: (options = {}) => requestJson(context, {
|
|
798
|
+
path: `/api/organizations/stats/email-activity${createQueryString({
|
|
799
|
+
days: options.days,
|
|
800
|
+
timezone: options.timezone
|
|
801
|
+
})}`,
|
|
802
|
+
responseSchema: emailActivityResponseSchema,
|
|
803
|
+
organizationId: options.organizationId,
|
|
804
|
+
orgScoped: true,
|
|
805
|
+
signal: options.signal
|
|
806
|
+
}),
|
|
807
|
+
getEmailSummary: (options = {}) => requestJson(context, {
|
|
808
|
+
path: "/api/organizations/stats/email-summary",
|
|
809
|
+
responseSchema: emailSummaryResponseSchema,
|
|
810
|
+
organizationId: options.organizationId,
|
|
811
|
+
orgScoped: true,
|
|
812
|
+
signal: options.signal
|
|
813
|
+
})
|
|
814
|
+
},
|
|
815
|
+
inboxes: {
|
|
816
|
+
poll: (options) => runPollingLoop(emails, options, {
|
|
817
|
+
timeoutMs: options.timeoutMs ?? 0,
|
|
818
|
+
throwOnTimeout: false
|
|
819
|
+
}),
|
|
820
|
+
waitForEmail: (options) => waitForEmailDetail(emails, options)
|
|
821
|
+
}
|
|
822
|
+
};
|
|
823
|
+
};
|
|
824
|
+
/**
|
|
825
|
+
* API-key SDK for SpinupMail.
|
|
826
|
+
*
|
|
827
|
+
* Typical usage:
|
|
828
|
+
*
|
|
829
|
+
* ```ts
|
|
830
|
+
* const spinupmail = new SpinupMail();
|
|
831
|
+
* const address = await spinupmail.addresses.create({ acceptedRiskNotice: true });
|
|
832
|
+
* const email = await spinupmail.inboxes.waitForEmail({ addressId: address.id });
|
|
833
|
+
* ```
|
|
834
|
+
*/
|
|
835
|
+
var SpinupMail = class {
|
|
836
|
+
domains;
|
|
837
|
+
addresses;
|
|
838
|
+
emails;
|
|
839
|
+
stats;
|
|
840
|
+
inboxes;
|
|
841
|
+
/** Creates a new SDK client using constructor options or environment defaults. */
|
|
842
|
+
constructor(options = {}) {
|
|
843
|
+
const resolvedOptions = typeof options === "string" ? { apiKey: options } : options;
|
|
844
|
+
const apiKey = resolvedOptions.apiKey ?? readEnvValue(["SPINUPMAIL_API_KEY"]);
|
|
845
|
+
const baseUrl = resolvedOptions.baseUrl ?? readEnvValue(["SPINUPMAIL_BASE_URL"]) ?? DEFAULT_SPINUPMAIL_BASE_URL;
|
|
846
|
+
const organizationId = resolvedOptions.organizationId ?? readEnvValue(["SPINUPMAIL_ORGANIZATION_ID", "SPINUPMAIL_ORG_ID"]);
|
|
847
|
+
const client = createSpinupMailClient({
|
|
848
|
+
baseUrl,
|
|
849
|
+
apiKey: normalizeString(apiKey ?? "", "apiKey"),
|
|
850
|
+
organizationId,
|
|
851
|
+
fetch: resolvedOptions.fetch,
|
|
852
|
+
headers: resolvedOptions.headers
|
|
853
|
+
});
|
|
854
|
+
this.domains = client.domains;
|
|
855
|
+
this.addresses = client.addresses;
|
|
856
|
+
this.emails = client.emails;
|
|
857
|
+
this.stats = client.stats;
|
|
858
|
+
this.inboxes = client.inboxes;
|
|
859
|
+
}
|
|
860
|
+
};
|
|
861
|
+
//#endregion
|
|
862
|
+
export { SpinupMail, SpinupMailApiError, SpinupMailError, SpinupMailFile, SpinupMailTimeoutError, SpinupMailValidationError };
|
|
863
|
+
|
|
864
|
+
//# sourceMappingURL=index.mjs.map
|