@stablyai/email 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/README.md ADDED
@@ -0,0 +1,138 @@
1
+ <p align="center">
2
+ <a href="https://stably.ai">
3
+ <h3 align="center">Stably</h3>
4
+ </a>
5
+ </p>
6
+
7
+ <p align="center">
8
+ Code. Ship. <s>Test.</s>
9
+ </p>
10
+
11
+ <p align="center">
12
+ <a href="https://docs.stably.ai/"><strong>Documentation</strong></a> ·
13
+ <a href="https://stably.ai/"><strong>Homepage</strong></a>
14
+ </p>
15
+ <br/>
16
+
17
+ # @stablyai/email
18
+
19
+ Email inbox SDK for end-to-end testing. Receive, filter, and manage test emails in your Playwright tests.
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ npm install @stablyai/email
25
+ ```
26
+
27
+ ## Setup
28
+
29
+ Get your [API key](https://auth.stably.ai/account/api_keys) and project ID from the Stably dashboard.
30
+
31
+ Either set your Stably credentials as environment variables:
32
+
33
+ ```bash
34
+ export STABLY_API_KEY="your-api-key"
35
+ export STABLY_PROJECT_ID="your-project-id"
36
+ ```
37
+
38
+ Or pass them programmatically to `Inbox.build()`:
39
+
40
+ ```ts
41
+ const inbox = await Inbox.build({
42
+ apiKey: "your-api-key",
43
+ projectId: "your-project-id",
44
+ });
45
+ ```
46
+
47
+ ## Usage
48
+
49
+ ```ts
50
+ import { Inbox } from "@stablyai/email";
51
+
52
+ // Create an inbox with a unique suffix for test isolation
53
+ const inbox = await Inbox.build({ suffix: `test-${Date.now()}` });
54
+ console.log(inbox.address); // "your-org+test-1234567890@mail.stably.ai"
55
+
56
+ // Wait for an email to arrive
57
+ const email = await inbox.waitForEmail({
58
+ subject: "Welcome",
59
+ timeoutMs: 30000,
60
+ });
61
+
62
+ // Access email content
63
+ console.log(email.subject);
64
+ console.log(email.textBody);
65
+ console.log(email.htmlBody);
66
+
67
+ // List all emails in the inbox
68
+ const { emails } = await inbox.listEmails();
69
+
70
+ // Extract data from email using AI
71
+ const otp = await inbox.extractFromEmail({
72
+ id: email.id,
73
+ prompt: "Extract the OTP code",
74
+ });
75
+
76
+ // Clean up
77
+ await inbox.deleteAllEmails();
78
+ ```
79
+
80
+ ## API
81
+
82
+ ### `Inbox.build(options?)`
83
+
84
+ Creates an inbox instance scoped to your organization's email address.
85
+
86
+ - `suffix` - Optional suffix for test isolation (e.g., creates "org+suffix@mail.stably.ai")
87
+ - `apiKey` - Stably API key (defaults to `STABLY_API_KEY` env var)
88
+ - `projectId` - Stably project ID (defaults to `STABLY_PROJECT_ID` env var)
89
+
90
+ ### `inbox.waitForEmail(options?)`
91
+
92
+ Waits for an email matching the filters to arrive.
93
+
94
+ - `subject` - Filter by subject (substring match)
95
+ - `from` - Filter by sender address
96
+ - `timeoutMs` - Maximum wait time (default: 30000)
97
+ - `pollIntervalMs` - Poll interval (default: 2000)
98
+
99
+ ### `inbox.listEmails(options?)`
100
+
101
+ Lists emails in the inbox. By default, only returns emails received after the inbox was created.
102
+
103
+ ### `inbox.getEmail(id)`
104
+
105
+ Gets a specific email by ID.
106
+
107
+ ### `inbox.deleteEmail(id)`
108
+
109
+ Deletes a specific email.
110
+
111
+ ### `inbox.deleteAllEmails()`
112
+
113
+ Deletes all emails sent to this inbox's address.
114
+
115
+ ### `inbox.extractFromEmail(args)`
116
+
117
+ Extracts data from an email using AI.
118
+
119
+ - `id` - The email ID
120
+ - `prompt` - Description of what to extract
121
+ - `schema` - Optional Zod v4 schema for structured output
122
+
123
+ ```ts
124
+ // Simple extraction
125
+ const otp = await inbox.extractFromEmail({
126
+ id: email.id,
127
+ prompt: "Extract the OTP code",
128
+ });
129
+
130
+ // Structured extraction with Zod schema
131
+ import { z } from "zod/v4";
132
+
133
+ const { code, expiresAt } = await inbox.extractFromEmail({
134
+ id: email.id,
135
+ prompt: "Extract the verification code and expiry",
136
+ schema: z.object({ code: z.string(), expiresAt: z.string() }),
137
+ });
138
+ ```
package/dist/index.cjs ADDED
@@ -0,0 +1,456 @@
1
+ 'use strict';
2
+
3
+ var apiClient = require('@stablyai-internal/api-client');
4
+
5
+ const zodV4 = (() => {
6
+ try {
7
+ return require("zod/v4/core");
8
+ } catch {
9
+ return void 0;
10
+ }
11
+ })();
12
+ class EmailExtractionError extends Error {
13
+ /** The reason extraction failed. */
14
+ reason;
15
+ constructor(message, reason) {
16
+ super(message);
17
+ this.name = "EmailExtractionError";
18
+ this.reason = reason;
19
+ }
20
+ }
21
+ function toJsonSchema(schema) {
22
+ if (!zodV4) {
23
+ throw new EmailExtractionError(
24
+ "Schema support requires installing zod@4. Please add it to enable schemas.",
25
+ "zod_not_installed"
26
+ );
27
+ }
28
+ return zodV4.toJSONSchema(
29
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
30
+ schema
31
+ );
32
+ }
33
+ async function validateWithSchema(schema, data) {
34
+ const parsed = await schema.safeParseAsync(data);
35
+ if (!parsed.success) {
36
+ const issues = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
37
+ throw new EmailExtractionError(
38
+ `AI is unable to return data in the desired format${issues ? `: ${issues}` : ""}`,
39
+ "validation_failed"
40
+ );
41
+ }
42
+ return parsed.data;
43
+ }
44
+ async function extractFromEmail({
45
+ address,
46
+ client,
47
+ id,
48
+ options,
49
+ projectId,
50
+ prompt
51
+ }) {
52
+ const jsonSchema = options?.schema ? toJsonSchema(options.schema) : void 0;
53
+ const response = await apiClient.postV1ProjectsByProjectIdEmailInboxesByAddressMessagesByMessageIdExtract(
54
+ {
55
+ body: {
56
+ jsonSchema,
57
+ prompt
58
+ },
59
+ client,
60
+ path: {
61
+ address,
62
+ messageId: id,
63
+ projectId
64
+ }
65
+ }
66
+ );
67
+ if (response.error) {
68
+ const errorMessage = "error" in response.error ? response.error.error : "Unknown error";
69
+ throw new EmailExtractionError(errorMessage, "api_error");
70
+ }
71
+ const result = response.data;
72
+ if (!result.success) {
73
+ const reason2 = result.reason ?? "Unknown reason";
74
+ throw new EmailExtractionError(`Extract failed: ${reason2}`, reason2);
75
+ }
76
+ if (result.data === void 0) {
77
+ throw new EmailExtractionError(
78
+ "Extract failed: No data returned",
79
+ "No data returned"
80
+ );
81
+ }
82
+ const { reason } = result;
83
+ if (options?.schema) {
84
+ return {
85
+ data: await validateWithSchema(options.schema, result.data),
86
+ reason
87
+ };
88
+ }
89
+ return {
90
+ data: typeof result.data === "string" ? result.data : JSON.stringify(result.data),
91
+ reason
92
+ };
93
+ }
94
+
95
+ const packageVersion = "0.1.0" ;
96
+
97
+ function getApiUrl() {
98
+ return process.env.STABLY_API_URL ?? "https://api.stably.ai";
99
+ }
100
+ function createEmailClient(apiKey) {
101
+ return apiClient.createClient({
102
+ baseUrl: getApiUrl(),
103
+ headers: {
104
+ Authorization: `Bearer ${apiKey}`,
105
+ "X-Client-Name": "stably-email-sdk-js",
106
+ "X-Client-Version": packageVersion
107
+ }
108
+ });
109
+ }
110
+ function parseEmail(raw) {
111
+ const parsedDate = new Date(raw.receivedAt);
112
+ if (isNaN(parsedDate.getTime())) {
113
+ throw new Error("Invalid email format: receivedAt is not a valid date");
114
+ }
115
+ return {
116
+ from: raw.from,
117
+ html: raw.html,
118
+ id: raw.id,
119
+ mailbox: raw.mailbox,
120
+ receivedAt: parsedDate,
121
+ subject: raw.subject,
122
+ text: raw.text,
123
+ to: raw.to
124
+ };
125
+ }
126
+
127
+ async function listEmails({
128
+ address,
129
+ client,
130
+ createdAt,
131
+ options,
132
+ projectId
133
+ }) {
134
+ const formattedSubject = options?.subject && options.subjectMatch === "exact" ? `"${options.subject.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"` : options?.subject;
135
+ const since = options?.includeOlder ? void 0 : options?.since ?? createdAt;
136
+ const response = await apiClient.getV1ProjectsByProjectIdEmailInboxesByAddressMessages({
137
+ client,
138
+ path: { address, projectId },
139
+ query: {
140
+ cursor: options?.cursor,
141
+ from: options?.from,
142
+ limit: options?.limit,
143
+ since: since?.toISOString(),
144
+ subject: formattedSubject
145
+ }
146
+ });
147
+ if (response.error) {
148
+ const errorMessage = "error" in response.error ? response.error.error : "Unknown error";
149
+ throw new Error(`Failed to list emails: ${errorMessage}`);
150
+ }
151
+ const { data } = response;
152
+ return {
153
+ emails: data.emails.flatMap((raw) => {
154
+ try {
155
+ return [parseEmail(raw)];
156
+ } catch (error) {
157
+ console.warn(
158
+ `Failed to parse email ${raw.id}:`,
159
+ error instanceof Error ? error.message : error
160
+ );
161
+ return [];
162
+ }
163
+ }),
164
+ nextCursor: data.nextCursor
165
+ };
166
+ }
167
+
168
+ const DEFAULT_TIMEOUT_MS = 2 * 60 * 1e3;
169
+ const DEFAULT_POLL_INTERVAL_MS = 3e3;
170
+ const MIN_POLL_INTERVAL_MS = 1e3;
171
+ class EmailTimeoutError extends Error {
172
+ constructor(message) {
173
+ super(message);
174
+ this.name = "EmailTimeoutError";
175
+ }
176
+ }
177
+ function sleep(ms) {
178
+ return new Promise((resolve) => setTimeout(resolve, ms));
179
+ }
180
+ async function waitForEmail({
181
+ address,
182
+ client,
183
+ createdAt,
184
+ options,
185
+ projectId
186
+ }) {
187
+ const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
188
+ if (timeoutMs <= 0) {
189
+ throw new Error(`timeoutMs must be a positive number, got ${timeoutMs}`);
190
+ }
191
+ const pollIntervalMs = Math.max(
192
+ options?.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS,
193
+ MIN_POLL_INTERVAL_MS
194
+ );
195
+ const deadline = performance.now() + timeoutMs;
196
+ while (performance.now() < deadline) {
197
+ const { emails } = await listEmails({
198
+ address,
199
+ client,
200
+ createdAt,
201
+ options: {
202
+ from: options?.from,
203
+ limit: 1,
204
+ subject: options?.subject,
205
+ subjectMatch: options?.subjectMatch
206
+ },
207
+ projectId
208
+ });
209
+ if (emails.length > 0) {
210
+ return emails[0];
211
+ }
212
+ const remaining = Math.max(0, deadline - performance.now());
213
+ if (remaining <= 0) {
214
+ break;
215
+ }
216
+ await sleep(Math.min(pollIntervalMs, remaining));
217
+ }
218
+ throw new EmailTimeoutError(`No matching email found within ${timeoutMs}ms`);
219
+ }
220
+
221
+ class Inbox {
222
+ /** The full email address for this inbox (includes suffix if provided). */
223
+ address;
224
+ /** The suffix used when creating this inbox, if any. */
225
+ suffix;
226
+ /** When this inbox instance was created. Used for filtering out older emails. */
227
+ createdAt;
228
+ baseAddress;
229
+ client;
230
+ projectId;
231
+ /** @internal Use {@link Inbox.build} to create an Inbox instance. */
232
+ constructor(options) {
233
+ const atIndex = options.baseAddress.indexOf("@");
234
+ if (atIndex === -1 || options.baseAddress.includes("@", atIndex + 1)) {
235
+ throw new Error(
236
+ "Invalid baseAddress: must contain exactly one @ character"
237
+ );
238
+ }
239
+ const [localPart, domain] = options.baseAddress.split("@");
240
+ if (!localPart || !domain) {
241
+ throw new Error(
242
+ "Invalid baseAddress: local part and domain cannot be empty"
243
+ );
244
+ }
245
+ this.baseAddress = options.baseAddress;
246
+ this.client = options.client;
247
+ this.projectId = options.projectId;
248
+ this.suffix = options.suffix;
249
+ this.address = this.suffix ? `${localPart}+${this.suffix}@${domain}` : this.baseAddress;
250
+ this.createdAt = /* @__PURE__ */ new Date();
251
+ }
252
+ /**
253
+ * Creates an inbox instance for receiving and managing test emails.
254
+ *
255
+ * @param options - Configuration options including optional suffix for test isolation.
256
+ * @returns An inbox instance scoped to your organization's email address.
257
+ * @throws Error if API key or project ID is missing and not set in environment.
258
+ *
259
+ * @example
260
+ * ```ts
261
+ * // Use environment variables (STABLY_API_KEY, STABLY_PROJECT_ID)
262
+ * const inbox = await Inbox.build();
263
+ *
264
+ * // With suffix for test isolation
265
+ * const inbox = await Inbox.build({ suffix: `test-${Date.now()}` });
266
+ * console.log(inbox.address); // "my-org+test-1234567890@mail.stably.ai"
267
+ * ```
268
+ */
269
+ static async build(options) {
270
+ const apiKey = options?.apiKey ?? process.env.STABLY_API_KEY;
271
+ const projectId = options?.projectId ?? process.env.STABLY_PROJECT_ID;
272
+ if (!apiKey) {
273
+ throw new Error(
274
+ "Missing API key. Set STABLY_API_KEY or pass apiKey option."
275
+ );
276
+ }
277
+ if (!projectId) {
278
+ throw new Error(
279
+ "Missing project ID. Set STABLY_PROJECT_ID or pass projectId option."
280
+ );
281
+ }
282
+ const client = createEmailClient(apiKey);
283
+ const response = await apiClient.getV1ProjectsByProjectIdEmailAddress({
284
+ client,
285
+ path: { projectId }
286
+ });
287
+ if (response.error) {
288
+ const errorMessage = "error" in response.error ? response.error.error : "Unknown error";
289
+ throw new Error(`Failed to get inbox address: ${errorMessage}`);
290
+ }
291
+ return new Inbox({
292
+ baseAddress: response.data.address,
293
+ client,
294
+ projectId,
295
+ suffix: options?.suffix
296
+ });
297
+ }
298
+ /**
299
+ * Lists emails in this inbox.
300
+ *
301
+ * By default, only returns emails received **after this `Inbox` was created**
302
+ * (via `Inbox.build()`). This prevents stale emails from previous test runs
303
+ * from appearing in results. Use `includeOlder: true` to include older emails.
304
+ *
305
+ * @param options - Filter and pagination options.
306
+ * @returns The matching emails and an optional cursor for pagination.
307
+ *
308
+ * @example
309
+ * ```ts
310
+ * const { emails } = await inbox.listEmails();
311
+ * const { emails } = await inbox.listEmails({ includeOlder: true });
312
+ * ```
313
+ */
314
+ async listEmails(options) {
315
+ return listEmails({
316
+ address: this.address,
317
+ client: this.client,
318
+ createdAt: this.createdAt,
319
+ options,
320
+ projectId: this.projectId
321
+ });
322
+ }
323
+ /**
324
+ * Gets a specific email by ID.
325
+ *
326
+ * @param id - The email ID to retrieve (from {@link Inbox.listEmails}).
327
+ * @returns The email with the given ID.
328
+ * @throws Error if the email is not found or API call fails.
329
+ *
330
+ * @example
331
+ * ```ts
332
+ * const { emails } = await inbox.listEmails();
333
+ * const email = await inbox.getEmail(emails[0].id);
334
+ * console.log(email.subject);
335
+ * ```
336
+ */
337
+ async getEmail(id) {
338
+ const response = await apiClient.getV1ProjectsByProjectIdEmailInboxesByAddressMessagesByMessageId({
339
+ client: this.client,
340
+ path: {
341
+ address: this.address,
342
+ messageId: id,
343
+ projectId: this.projectId
344
+ }
345
+ });
346
+ if (response.error) {
347
+ const errorMessage = "error" in response.error ? response.error.error : "Unknown error";
348
+ throw new Error(`Failed to get email: ${errorMessage}`);
349
+ }
350
+ return parseEmail(response.data);
351
+ }
352
+ /**
353
+ * Deletes an email from this inbox.
354
+ *
355
+ * @param id - The unique identifier of the email to delete.
356
+ * @throws Error if the email doesn't exist or deletion fails.
357
+ *
358
+ * @example
359
+ * ```ts
360
+ * const { emails } = await inbox.listEmails({ subject: 'old notification' });
361
+ * if (emails.length > 0) {
362
+ * await inbox.deleteEmail(emails[0].id);
363
+ * }
364
+ * ```
365
+ */
366
+ async deleteEmail(id) {
367
+ const response = await apiClient.deleteV1ProjectsByProjectIdEmailInboxesByAddressMessagesByMessageId(
368
+ {
369
+ client: this.client,
370
+ path: {
371
+ address: this.address,
372
+ messageId: id,
373
+ projectId: this.projectId
374
+ }
375
+ }
376
+ );
377
+ if (response.error) {
378
+ const errorMessage = "error" in response.error ? response.error.error : "Unknown error";
379
+ throw new Error(`Failed to delete email: ${errorMessage}`);
380
+ }
381
+ }
382
+ /**
383
+ * Deletes all emails sent to this inbox's address.
384
+ *
385
+ * If the inbox was created with a suffix, only emails sent to that specific
386
+ * suffixed address are deleted (e.g., "org+test-123@mail.stably.ai"), not
387
+ * the entire org mailbox.
388
+ *
389
+ * @example
390
+ * ```ts
391
+ * const inbox = await Inbox.build({ suffix: `test-${Date.now()}` });
392
+ * // ... receive and process emails ...
393
+ * await inbox.deleteAllEmails(); // Only deletes emails sent to this suffix
394
+ * ```
395
+ */
396
+ async deleteAllEmails() {
397
+ const response = await apiClient.deleteV1ProjectsByProjectIdEmailInboxesByAddressMessages({
398
+ client: this.client,
399
+ path: { address: this.address, projectId: this.projectId }
400
+ });
401
+ if (response.error) {
402
+ const errorMessage = "error" in response.error ? response.error.error : "Unknown error";
403
+ throw new Error(`Failed to delete emails: ${errorMessage}`);
404
+ }
405
+ }
406
+ /**
407
+ * Waits for an email matching the filters to arrive.
408
+ *
409
+ * Only returns emails received after the inbox was created, preventing
410
+ * stale emails from previous test runs from being matched.
411
+ *
412
+ * @param options - Filter and timeout options.
413
+ * @returns The first matching email.
414
+ * @throws {EmailTimeoutError} If no matching email arrives within the timeout.
415
+ *
416
+ * @example
417
+ * ```ts
418
+ * const email = await inbox.waitForEmail({ subject: 'Welcome' });
419
+ *
420
+ * const email = await inbox.waitForEmail({
421
+ * from: 'noreply@example.com',
422
+ * subject: 'verification',
423
+ * timeoutMs: 60000,
424
+ * pollIntervalMs: 5000,
425
+ * });
426
+ * ```
427
+ */
428
+ async waitForEmail(options) {
429
+ return waitForEmail({
430
+ address: this.address,
431
+ client: this.client,
432
+ createdAt: this.createdAt,
433
+ options,
434
+ projectId: this.projectId
435
+ });
436
+ }
437
+ async extractFromEmail({
438
+ id,
439
+ prompt,
440
+ schema
441
+ }) {
442
+ return extractFromEmail({
443
+ address: this.address,
444
+ client: this.client,
445
+ id,
446
+ options: schema ? { schema } : void 0,
447
+ projectId: this.projectId,
448
+ prompt
449
+ });
450
+ }
451
+ }
452
+
453
+ exports.EmailExtractionError = EmailExtractionError;
454
+ exports.EmailTimeoutError = EmailTimeoutError;
455
+ exports.Inbox = Inbox;
456
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/extract.ts","../src/version.ts","../src/internal-api.ts","../src/list.ts","../src/wait-for-email.ts","../src/inbox.ts"],"sourcesContent":["import {\n type Client,\n postV1ProjectsByProjectIdEmailInboxesByAddressMessagesByMessageIdExtract,\n} from \"@stablyai-internal/api-client\";\nimport type * as z4 from \"zod/v4/core\";\n\n// eslint-disable-next-line @typescript-eslint/consistent-type-imports\ntype ZodV4 = typeof import(\"zod/v4/core\");\n\nexport const zodV4: ZodV4 | undefined = (() => {\n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\n return require(\"zod/v4/core\") as ZodV4;\n } catch {\n return undefined;\n }\n})();\n\n/**\n * Schema type for structured extraction. Supports Zod v4 schemas.\n */\nexport type ExtractSchema = {\n safeParseAsync(\n data: unknown,\n params?: z4.ParseContext<z4.$ZodIssue>,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ): Promise<z4.util.SafeParseResult<z4.output<any>>>;\n} & z4.$ZodType;\n\n/** Output type inferred from a schema. */\nexport type SchemaOutput<T extends ExtractSchema> = z4.output<T>;\n\n/** Result from email extraction. */\nexport type ExtractResult<T> = {\n data: T;\n reason: string;\n};\n\n/**\n * Error thrown when email extraction fails.\n */\nexport class EmailExtractionError extends Error {\n /** The reason extraction failed. */\n readonly reason: string;\n\n constructor(message: string, reason: string) {\n super(message);\n this.name = \"EmailExtractionError\";\n this.reason = reason;\n }\n}\n\n/**\n * Converts a Zod schema to JSON Schema for the API.\n * @throws Error if zod is not installed.\n */\nfunction toJsonSchema(\n schema: ExtractSchema,\n): Record<string, unknown> | undefined {\n if (!zodV4) {\n throw new EmailExtractionError(\n \"Schema support requires installing zod@4. Please add it to enable schemas.\",\n \"zod_not_installed\",\n );\n }\n return zodV4.toJSONSchema(\n // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\n schema as unknown as Parameters<ZodV4[\"toJSONSchema\"]>[0],\n ) as Record<string, unknown>;\n}\n\n/**\n * Validates data against a schema.\n * @throws Error if validation fails.\n */\nasync function validateWithSchema<T extends ExtractSchema>(\n schema: T,\n data: unknown,\n): Promise<SchemaOutput<T>> {\n const parsed = await schema.safeParseAsync(data);\n if (!parsed.success) {\n const issues = parsed.error.issues\n .map((i) => `${i.path.join(\".\")}: ${i.message}`)\n .join(\"; \");\n throw new EmailExtractionError(\n `AI is unable to return data in the desired format${issues ? `: ${issues}` : \"\"}`,\n \"validation_failed\",\n );\n }\n return parsed.data;\n}\n\n/**\n * Extracts data from an email using AI.\n * @internal Called by Inbox.extract, which is the public API for email extraction.\n */\nexport async function extractFromEmail<T extends ExtractSchema>({\n address,\n client,\n id,\n options,\n projectId,\n prompt,\n}: {\n client: Client;\n address: string;\n projectId: string;\n id: string;\n prompt: string;\n options?: { schema: T };\n}): Promise<ExtractResult<string | SchemaOutput<T>>> {\n // Build JSON schema from Zod if provided\n const jsonSchema = options?.schema ? toJsonSchema(options.schema) : undefined;\n\n const response =\n await postV1ProjectsByProjectIdEmailInboxesByAddressMessagesByMessageIdExtract(\n {\n body: {\n jsonSchema,\n prompt,\n },\n client,\n path: {\n address,\n messageId: id,\n projectId,\n },\n },\n );\n\n // Handle API errors\n if (response.error) {\n const errorMessage =\n \"error\" in response.error ? response.error.error : \"Unknown error\";\n throw new EmailExtractionError(errorMessage, \"api_error\");\n }\n\n const result = response.data;\n\n // Handle extraction failure\n if (!result.success) {\n const reason = result.reason ?? \"Unknown reason\";\n throw new EmailExtractionError(`Extract failed: ${reason}`, reason);\n }\n\n // Handle missing data for both schema and non-schema paths\n if (result.data === undefined) {\n throw new EmailExtractionError(\n \"Extract failed: No data returned\",\n \"No data returned\",\n );\n }\n\n const { reason } = result;\n\n // Validate with schema if provided\n if (options?.schema) {\n return {\n data: await validateWithSchema(options.schema, result.data),\n reason,\n };\n }\n return {\n data:\n typeof result.data === \"string\"\n ? result.data\n : JSON.stringify(result.data),\n reason,\n };\n}\n","declare const __PACKAGE_VERSION__: string;\n\nexport const packageVersion =\n typeof __PACKAGE_VERSION__ !== \"undefined\"\n ? __PACKAGE_VERSION__\n : (process.env.npm_package_version ?? \"local\");\n","import {\n createClient,\n type Client,\n type GetV1ProjectsByProjectIdEmailInboxesByAddressMessagesByMessageIdResponse,\n} from \"@stablyai-internal/api-client\";\n\nimport type { Email } from \"./list\";\nimport { packageVersion } from \"./version\";\n\nexport function getApiUrl(): string {\n return process.env.STABLY_API_URL ?? \"https://api.stably.ai\";\n}\n\nexport function createEmailClient(apiKey: string): Client {\n return createClient({\n baseUrl: getApiUrl(),\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"X-Client-Name\": \"stably-email-sdk-js\",\n \"X-Client-Version\": packageVersion,\n },\n });\n}\n\ntype RawEmail =\n GetV1ProjectsByProjectIdEmailInboxesByAddressMessagesByMessageIdResponse;\n\nexport function parseEmail(raw: RawEmail): Email {\n const parsedDate = new Date(raw.receivedAt);\n if (isNaN(parsedDate.getTime())) {\n throw new Error(\"Invalid email format: receivedAt is not a valid date\");\n }\n return {\n from: raw.from,\n html: raw.html,\n id: raw.id,\n mailbox: raw.mailbox,\n receivedAt: parsedDate,\n subject: raw.subject,\n text: raw.text,\n to: raw.to,\n };\n}\n","import {\n type Client,\n getV1ProjectsByProjectIdEmailInboxesByAddressMessages,\n} from \"@stablyai-internal/api-client\";\n\nimport { parseEmail } from \"./internal-api\";\n\n/** An email address with optional display name. */\nexport type EmailAddress = {\n /** The email address (e.g., \"user@example.com\"). */\n address: string;\n /** Optional display name (e.g., \"John Doe\"). */\n name?: string;\n};\n\n/** An email message. */\nexport type Email = {\n /** Unique identifier for the email. */\n id: string;\n /** The mailbox containing this email (e.g., \"INBOX\"). */\n mailbox: string;\n /** The sender's email address. */\n from: EmailAddress;\n /** The recipient email addresses. */\n to: EmailAddress[];\n /** The email subject line. */\n subject: string;\n /** When the email was received. */\n receivedAt: Date;\n /** HTML content of the email body, if available. */\n html?: string[];\n /** Plain text content of the email body, if available. */\n text?: string;\n};\n\n/** Options for listing emails in an inbox. */\nexport type ListEmailsOptions = {\n /** Filter by sender email address. */\n from?: string;\n /** Filter by subject line. */\n subject?: string;\n /** How to match the subject: \"contains\" (default) or \"exact\". */\n subjectMatch?: \"contains\" | \"exact\";\n /** Maximum number of emails to return. */\n limit?: number;\n /** Pagination cursor from a previous response. */\n cursor?: string;\n /** Only return emails received after this date. Overrides the default createdAt filter. */\n since?: Date;\n /** Set to true to include emails received before the inbox was created. */\n includeOlder?: boolean;\n};\n\n/**\n * Lists emails for an inbox address.\n */\nexport async function listEmails({\n address,\n client,\n createdAt,\n options,\n projectId,\n}: {\n client: Client;\n projectId: string;\n address: string;\n createdAt: Date;\n options?: ListEmailsOptions;\n}): Promise<{ emails: Email[]; nextCursor?: string }> {\n // Format subject for exact match if needed (wrap in quotes, escape internal quotes)\n const formattedSubject =\n options?.subject && options.subjectMatch === \"exact\"\n ? `\"${options.subject.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"')}\"`\n : options?.subject;\n\n const since = options?.includeOlder\n ? undefined\n : (options?.since ?? createdAt);\n\n const response = await getV1ProjectsByProjectIdEmailInboxesByAddressMessages({\n client,\n path: { address, projectId },\n query: {\n cursor: options?.cursor,\n from: options?.from,\n limit: options?.limit,\n since: since?.toISOString(),\n subject: formattedSubject,\n },\n });\n\n if (response.error) {\n const errorMessage =\n \"error\" in response.error ? response.error.error : \"Unknown error\";\n throw new Error(`Failed to list emails: ${errorMessage}`);\n }\n\n const { data } = response;\n return {\n emails: data.emails.flatMap((raw) => {\n try {\n return [parseEmail(raw)];\n } catch (error) {\n console.warn(\n `Failed to parse email ${raw.id}:`,\n error instanceof Error ? error.message : error,\n );\n return [];\n }\n }),\n nextCursor: data.nextCursor,\n };\n}\n","import type { Client } from \"@stablyai-internal/api-client\";\n\nimport { listEmails, type Email } from \"./list\";\n\nconst DEFAULT_TIMEOUT_MS = 2 * 60 * 1000;\nconst DEFAULT_POLL_INTERVAL_MS = 3_000;\nconst MIN_POLL_INTERVAL_MS = 1_000;\n\n/** Options for waiting for an email to arrive. */\nexport type WaitForEmailOptions = {\n /** Filter by sender email address. */\n from?: string;\n /** Filter by subject line. */\n subject?: string;\n /** How to match the subject: \"contains\" (default) or \"exact\". */\n subjectMatch?: \"contains\" | \"exact\";\n /** Maximum time to wait. @default 120000 (2 minutes) */\n timeoutMs?: number;\n /** Time between poll attempts. Minimum 1000ms. @default 3000 (3 seconds) */\n pollIntervalMs?: number;\n};\n\n/** Error thrown when waitForEmail() times out without finding a matching email. */\nexport class EmailTimeoutError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"EmailTimeoutError\";\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Waits for an email matching the filters to arrive.\n *\n * @throws {EmailTimeoutError} If no matching email arrives within the timeout.\n */\nexport async function waitForEmail({\n address,\n client,\n createdAt,\n options,\n projectId,\n}: {\n client: Client;\n projectId: string;\n address: string;\n createdAt: Date;\n options?: WaitForEmailOptions;\n}): Promise<Email> {\n const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\n if (timeoutMs <= 0) {\n throw new Error(`timeoutMs must be a positive number, got ${timeoutMs}`);\n }\n\n const pollIntervalMs = Math.max(\n options?.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS,\n MIN_POLL_INTERVAL_MS,\n );\n const deadline = performance.now() + timeoutMs;\n\n while (performance.now() < deadline) {\n const { emails } = await listEmails({\n address,\n client,\n createdAt,\n options: {\n from: options?.from,\n limit: 1,\n subject: options?.subject,\n subjectMatch: options?.subjectMatch,\n },\n projectId,\n });\n\n if (emails.length > 0) {\n return emails[0];\n }\n\n const remaining = Math.max(0, deadline - performance.now());\n if (remaining <= 0) {\n break;\n }\n await sleep(Math.min(pollIntervalMs, remaining));\n }\n\n throw new EmailTimeoutError(`No matching email found within ${timeoutMs}ms`);\n}\n","import {\n type Client,\n deleteV1ProjectsByProjectIdEmailInboxesByAddressMessages,\n deleteV1ProjectsByProjectIdEmailInboxesByAddressMessagesByMessageId,\n getV1ProjectsByProjectIdEmailAddress,\n getV1ProjectsByProjectIdEmailInboxesByAddressMessagesByMessageId,\n} from \"@stablyai-internal/api-client\";\n\nimport {\n type ExtractSchema,\n type SchemaOutput,\n extractFromEmail as extractFromEmailImpl,\n} from \"./extract\";\nimport { createEmailClient, parseEmail } from \"./internal-api\";\nimport { listEmails, type Email, type ListEmailsOptions } from \"./list\";\nimport {\n waitForEmail as waitForEmailImpl,\n type WaitForEmailOptions,\n} from \"./wait-for-email\";\n\n/** Options for creating an inbox instance. */\nexport type GetInboxOptions = {\n /** Optional suffix for test isolation (e.g., \"test-123\" creates \"org+test-123@mail.stably.ai\"). */\n suffix?: string;\n /** Stably API key. Defaults to STABLY_API_KEY environment variable. */\n apiKey?: string;\n /** Stably project ID. Defaults to STABLY_PROJECT_ID environment variable. */\n projectId?: string;\n};\n\n/**\n * An email inbox scoped to a specific address.\n *\n * Use {@link Inbox.build} to create an instance. All operations are automatically\n * scoped to this inbox's address and creation time for test isolation.\n *\n * @example\n * ```ts\n * const inbox = await Inbox.build({ suffix: `test-${Date.now()}` });\n * console.log(inbox.address); // \"org+test-123@mail.stably.ai\"\n *\n * const { emails } = await inbox.listEmails({ subject: 'verification' });\n * ```\n */\nexport class Inbox {\n /** The full email address for this inbox (includes suffix if provided). */\n readonly address: string;\n /** The suffix used when creating this inbox, if any. */\n readonly suffix?: string;\n /** When this inbox instance was created. Used for filtering out older emails. */\n readonly createdAt: Date;\n private readonly baseAddress: string;\n private readonly client: Client;\n private readonly projectId: string;\n\n /** @internal Use {@link Inbox.build} to create an Inbox instance. */\n private constructor(options: {\n /** The org's root email address (e.g., \"my-org@mail.stably.ai\") before any suffix is applied. */\n baseAddress: string;\n client: Client;\n projectId: string;\n suffix?: string;\n }) {\n const atIndex = options.baseAddress.indexOf(\"@\");\n if (atIndex === -1 || options.baseAddress.includes(\"@\", atIndex + 1)) {\n throw new Error(\n \"Invalid baseAddress: must contain exactly one @ character\",\n );\n }\n const [localPart, domain] = options.baseAddress.split(\"@\");\n if (!localPart || !domain) {\n throw new Error(\n \"Invalid baseAddress: local part and domain cannot be empty\",\n );\n }\n\n this.baseAddress = options.baseAddress;\n this.client = options.client;\n this.projectId = options.projectId;\n this.suffix = options.suffix;\n this.address = this.suffix\n ? `${localPart}+${this.suffix}@${domain}`\n : this.baseAddress;\n this.createdAt = new Date();\n }\n\n /**\n * Creates an inbox instance for receiving and managing test emails.\n *\n * @param options - Configuration options including optional suffix for test isolation.\n * @returns An inbox instance scoped to your organization's email address.\n * @throws Error if API key or project ID is missing and not set in environment.\n *\n * @example\n * ```ts\n * // Use environment variables (STABLY_API_KEY, STABLY_PROJECT_ID)\n * const inbox = await Inbox.build();\n *\n * // With suffix for test isolation\n * const inbox = await Inbox.build({ suffix: `test-${Date.now()}` });\n * console.log(inbox.address); // \"my-org+test-1234567890@mail.stably.ai\"\n * ```\n */\n static async build(options?: GetInboxOptions): Promise<Inbox> {\n const apiKey = options?.apiKey ?? process.env.STABLY_API_KEY;\n const projectId = options?.projectId ?? process.env.STABLY_PROJECT_ID;\n\n if (!apiKey) {\n throw new Error(\n \"Missing API key. Set STABLY_API_KEY or pass apiKey option.\",\n );\n }\n if (!projectId) {\n throw new Error(\n \"Missing project ID. Set STABLY_PROJECT_ID or pass projectId option.\",\n );\n }\n\n const client = createEmailClient(apiKey);\n\n const response = await getV1ProjectsByProjectIdEmailAddress({\n client,\n path: { projectId },\n });\n\n if (response.error) {\n const errorMessage =\n \"error\" in response.error ? response.error.error : \"Unknown error\";\n throw new Error(`Failed to get inbox address: ${errorMessage}`);\n }\n\n return new Inbox({\n baseAddress: response.data.address,\n client,\n projectId,\n suffix: options?.suffix,\n });\n }\n\n /**\n * Lists emails in this inbox.\n *\n * By default, only returns emails received **after this `Inbox` was created**\n * (via `Inbox.build()`). This prevents stale emails from previous test runs\n * from appearing in results. Use `includeOlder: true` to include older emails.\n *\n * @param options - Filter and pagination options.\n * @returns The matching emails and an optional cursor for pagination.\n *\n * @example\n * ```ts\n * const { emails } = await inbox.listEmails();\n * const { emails } = await inbox.listEmails({ includeOlder: true });\n * ```\n */\n async listEmails(\n options?: ListEmailsOptions,\n ): Promise<{ emails: Email[]; nextCursor?: string }> {\n return listEmails({\n address: this.address,\n client: this.client,\n createdAt: this.createdAt,\n options,\n projectId: this.projectId,\n });\n }\n\n /**\n * Gets a specific email by ID.\n *\n * @param id - The email ID to retrieve (from {@link Inbox.listEmails}).\n * @returns The email with the given ID.\n * @throws Error if the email is not found or API call fails.\n *\n * @example\n * ```ts\n * const { emails } = await inbox.listEmails();\n * const email = await inbox.getEmail(emails[0].id);\n * console.log(email.subject);\n * ```\n */\n async getEmail(id: string): Promise<Email> {\n const response =\n await getV1ProjectsByProjectIdEmailInboxesByAddressMessagesByMessageId({\n client: this.client,\n path: {\n address: this.address,\n messageId: id,\n projectId: this.projectId,\n },\n });\n\n if (response.error) {\n const errorMessage =\n \"error\" in response.error ? response.error.error : \"Unknown error\";\n throw new Error(`Failed to get email: ${errorMessage}`);\n }\n\n return parseEmail(response.data);\n }\n\n /**\n * Deletes an email from this inbox.\n *\n * @param id - The unique identifier of the email to delete.\n * @throws Error if the email doesn't exist or deletion fails.\n *\n * @example\n * ```ts\n * const { emails } = await inbox.listEmails({ subject: 'old notification' });\n * if (emails.length > 0) {\n * await inbox.deleteEmail(emails[0].id);\n * }\n * ```\n */\n async deleteEmail(id: string): Promise<void> {\n const response =\n await deleteV1ProjectsByProjectIdEmailInboxesByAddressMessagesByMessageId(\n {\n client: this.client,\n path: {\n address: this.address,\n messageId: id,\n projectId: this.projectId,\n },\n },\n );\n\n if (response.error) {\n const errorMessage =\n \"error\" in response.error ? response.error.error : \"Unknown error\";\n throw new Error(`Failed to delete email: ${errorMessage}`);\n }\n }\n\n /**\n * Deletes all emails sent to this inbox's address.\n *\n * If the inbox was created with a suffix, only emails sent to that specific\n * suffixed address are deleted (e.g., \"org+test-123@mail.stably.ai\"), not\n * the entire org mailbox.\n *\n * @example\n * ```ts\n * const inbox = await Inbox.build({ suffix: `test-${Date.now()}` });\n * // ... receive and process emails ...\n * await inbox.deleteAllEmails(); // Only deletes emails sent to this suffix\n * ```\n */\n async deleteAllEmails(): Promise<void> {\n const response =\n await deleteV1ProjectsByProjectIdEmailInboxesByAddressMessages({\n client: this.client,\n path: { address: this.address, projectId: this.projectId },\n });\n\n if (response.error) {\n const errorMessage =\n \"error\" in response.error ? response.error.error : \"Unknown error\";\n throw new Error(`Failed to delete emails: ${errorMessage}`);\n }\n }\n\n /**\n * Waits for an email matching the filters to arrive.\n *\n * Only returns emails received after the inbox was created, preventing\n * stale emails from previous test runs from being matched.\n *\n * @param options - Filter and timeout options.\n * @returns The first matching email.\n * @throws {EmailTimeoutError} If no matching email arrives within the timeout.\n *\n * @example\n * ```ts\n * const email = await inbox.waitForEmail({ subject: 'Welcome' });\n *\n * const email = await inbox.waitForEmail({\n * from: 'noreply@example.com',\n * subject: 'verification',\n * timeoutMs: 60000,\n * pollIntervalMs: 5000,\n * });\n * ```\n */\n async waitForEmail(options?: WaitForEmailOptions): Promise<Email> {\n return waitForEmailImpl({\n address: this.address,\n client: this.client,\n createdAt: this.createdAt,\n options,\n projectId: this.projectId,\n });\n }\n\n /**\n * Extracts data from an email using AI.\n *\n * @param args - The email ID and prompt describing what to extract.\n * @returns The extracted string.\n * @throws {@link EmailExtractionError} if extraction fails.\n *\n * @example\n * ```ts\n * const otp = await inbox.extractFromEmail({ id: email.id, prompt: 'Extract the OTP code' });\n * ```\n */\n extractFromEmail(args: { id: string; prompt: string }): Promise<string>;\n /**\n * Extracts structured data from an email using AI with a Zod schema.\n *\n * @param args - The email ID, prompt, and Zod v4 schema for structured output.\n * @returns The extracted data matching the schema type.\n * @throws {@link EmailExtractionError} if extraction fails.\n * @throws Error if schema validation fails or Zod v4 is not installed.\n *\n * @example\n * ```ts\n * import { z } from 'zod/v4';\n *\n * const { code, expiresAt } = await inbox.extractFromEmail({\n * id: email.id,\n * prompt: 'Extract the verification code and expiry',\n * schema: z.object({ code: z.string(), expiresAt: z.string() }),\n * });\n * ```\n */\n extractFromEmail<T extends ExtractSchema>(args: {\n id: string;\n prompt: string;\n schema: T;\n }): Promise<SchemaOutput<T>>;\n async extractFromEmail<T extends ExtractSchema>({\n id,\n prompt,\n schema,\n }: {\n id: string;\n prompt: string;\n schema?: T;\n }): Promise<string | SchemaOutput<T>> {\n return extractFromEmailImpl({\n address: this.address,\n client: this.client,\n id,\n options: schema ? { schema } : undefined,\n projectId: this.projectId,\n prompt,\n });\n }\n}\n"],"names":["postV1ProjectsByProjectIdEmailInboxesByAddressMessagesByMessageIdExtract","reason","createClient","getV1ProjectsByProjectIdEmailInboxesByAddressMessages","getV1ProjectsByProjectIdEmailAddress","getV1ProjectsByProjectIdEmailInboxesByAddressMessagesByMessageId","deleteV1ProjectsByProjectIdEmailInboxesByAddressMessagesByMessageId","deleteV1ProjectsByProjectIdEmailInboxesByAddressMessages","waitForEmailImpl","extractFromEmailImpl"],"mappings":"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"}