@automate.ax/integration-contracts 0.98.0 → 0.99.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.
@@ -0,0 +1,273 @@
1
+ import * as z from "zod";
2
+ export const RESEND_EMAIL_STATUS_SCHEMA = z.enum([
3
+ "bounced",
4
+ "canceled",
5
+ "clicked",
6
+ "complained",
7
+ "delivered",
8
+ "delivery_delayed",
9
+ "failed",
10
+ "opened",
11
+ "queued",
12
+ "scheduled",
13
+ "sent",
14
+ "suppressed",
15
+ ]);
16
+ export const RESEND_TAG_SCHEMA = z.object({
17
+ name: z.string(),
18
+ value: z.string(),
19
+ });
20
+ export const RESEND_ID_RESPONSE_SCHEMA = z.object({ id: z.string().min(1) });
21
+ export const RESEND_MUTATION_RESPONSE_SCHEMA = z.object({
22
+ id: z.string().min(1),
23
+ object: z.string(),
24
+ });
25
+ export const RESEND_EMAIL_MUTATION_RESPONSE_SCHEMA = z.object({
26
+ id: z.string().min(1),
27
+ object: z.literal("email"),
28
+ });
29
+ export const RESEND_CONTACT_MUTATION_RESPONSE_SCHEMA = z.object({
30
+ id: z.string().min(1),
31
+ object: z.literal("contact"),
32
+ });
33
+ export const RESEND_CONTACT_IMPORT_MUTATION_RESPONSE_SCHEMA = z.object({
34
+ id: z.string().min(1),
35
+ object: z.literal("contact_import"),
36
+ });
37
+ export const RESEND_EVENT_MUTATION_RESPONSE_SCHEMA = z.object({
38
+ id: z.string().min(1),
39
+ object: z.literal("event"),
40
+ });
41
+ export const RESEND_CONTACT_DELETE_RESPONSE_SCHEMA = RESEND_CONTACT_MUTATION_RESPONSE_SCHEMA.extend({ deleted: z.boolean() });
42
+ export const RESEND_EVENT_DELETE_RESPONSE_SCHEMA = RESEND_EVENT_MUTATION_RESPONSE_SCHEMA.extend({ deleted: z.boolean() });
43
+ export const RESEND_DELETE_RESPONSE_SCHEMA = RESEND_MUTATION_RESPONSE_SCHEMA.extend({ deleted: z.boolean() });
44
+ export const RESEND_EMAIL_SUMMARY_WIRE_SCHEMA = z.object({
45
+ bcc: z.string().array().nullable().optional(),
46
+ cc: z.string().array().nullable().optional(),
47
+ created_at: z.string(),
48
+ from: z.string(),
49
+ id: z.string(),
50
+ last_event: RESEND_EMAIL_STATUS_SCHEMA,
51
+ message_id: z.string().nullable().optional(),
52
+ reply_to: z.string().array().nullable().optional(),
53
+ scheduled_at: z.string().nullable().optional(),
54
+ subject: z.string(),
55
+ to: z.string().array(),
56
+ topic_id: z.string().nullable().optional(),
57
+ });
58
+ export const RESEND_EMAIL_SUMMARY_SCHEMA = RESEND_EMAIL_SUMMARY_WIRE_SCHEMA.transform(({ created_at: createdAt, last_event: lastEvent, message_id: messageId, reply_to: replyTo, scheduled_at: scheduledAt, topic_id: topicId, ...email }) => ({
59
+ ...email,
60
+ createdAt,
61
+ lastEvent,
62
+ messageId,
63
+ replyTo,
64
+ scheduledAt,
65
+ topicId,
66
+ }));
67
+ export const RESEND_EMAIL_WIRE_SCHEMA = RESEND_EMAIL_SUMMARY_WIRE_SCHEMA.extend({
68
+ bcc: z.string().array().nullable().optional(),
69
+ cc: z.string().array().nullable().optional(),
70
+ html: z.string().nullable().optional(),
71
+ object: z.literal("email"),
72
+ reply_to: z.string().array().nullable().optional(),
73
+ tags: RESEND_TAG_SCHEMA.array().optional(),
74
+ text: z.string().nullable().optional(),
75
+ });
76
+ export const RESEND_EMAIL_SCHEMA = RESEND_EMAIL_WIRE_SCHEMA.transform(({ created_at: createdAt, last_event: lastEvent, message_id: messageId, reply_to: replyTo, scheduled_at: scheduledAt, topic_id: topicId, ...email }) => ({
77
+ ...email,
78
+ createdAt,
79
+ lastEvent,
80
+ messageId,
81
+ replyTo,
82
+ scheduledAt,
83
+ topicId,
84
+ }));
85
+ export const RESEND_ATTACHMENT_WIRE_SCHEMA = z.object({
86
+ content_disposition: z.enum(["attachment", "inline"]).nullable(),
87
+ content_id: z.string(),
88
+ content_type: z.string(),
89
+ download_url: z.url(),
90
+ expires_at: z.string(),
91
+ filename: z.string().nullable(),
92
+ id: z.string(),
93
+ size: z.number().int().nonnegative(),
94
+ });
95
+ export const RESEND_ATTACHMENT_SCHEMA = RESEND_ATTACHMENT_WIRE_SCHEMA.transform(({ content_disposition: contentDisposition, content_id: contentId, content_type: contentType, download_url: downloadUrl, expires_at: expiresAt, ...attachment }) => ({
96
+ ...attachment,
97
+ contentDisposition,
98
+ contentId,
99
+ contentType,
100
+ downloadUrl,
101
+ expiresAt,
102
+ }));
103
+ const RESEND_RECEIVED_ATTACHMENT_WIRE_SCHEMA = z.object({
104
+ content_disposition: z.string().nullable(),
105
+ content_id: z.string().nullable(),
106
+ content_type: z.string(),
107
+ filename: z.string().nullable(),
108
+ id: z.string(),
109
+ size: z.number().int().nonnegative(),
110
+ });
111
+ const RESEND_RECEIVED_ATTACHMENT_SCHEMA = RESEND_RECEIVED_ATTACHMENT_WIRE_SCHEMA.transform(({ content_disposition: contentDisposition, content_id: contentId, content_type: contentType, ...attachment }) => ({ ...attachment, contentDisposition, contentId, contentType }));
112
+ export const RESEND_RECEIVED_EMAIL_SUMMARY_WIRE_SCHEMA = z.object({
113
+ attachments: RESEND_RECEIVED_ATTACHMENT_WIRE_SCHEMA.array(),
114
+ bcc: z.string().array().nullable(),
115
+ cc: z.string().array().nullable(),
116
+ created_at: z.string(),
117
+ from: z.string(),
118
+ id: z.string(),
119
+ message_id: z.string(),
120
+ reply_to: z.string().array().nullable(),
121
+ subject: z.string().nullable(),
122
+ to: z.string().array(),
123
+ });
124
+ export const RESEND_RECEIVED_EMAIL_SUMMARY_SCHEMA = RESEND_RECEIVED_EMAIL_SUMMARY_WIRE_SCHEMA.transform(({ attachments, created_at: createdAt, message_id: messageId, reply_to: replyTo, ...email }) => ({
125
+ ...email,
126
+ attachments: attachments.map((attachment) => RESEND_RECEIVED_ATTACHMENT_SCHEMA.parse(attachment)),
127
+ createdAt,
128
+ messageId,
129
+ replyTo,
130
+ }));
131
+ export const RESEND_RECEIVED_EMAIL_WIRE_SCHEMA = RESEND_RECEIVED_EMAIL_SUMMARY_WIRE_SCHEMA.extend({
132
+ headers: z.record(z.string(), z.string()).nullable(),
133
+ html: z.string().nullable(),
134
+ object: z.literal("email"),
135
+ raw: z
136
+ .object({ download_url: z.url(), expires_at: z.string() })
137
+ .nullable()
138
+ .optional(),
139
+ received_for: z.string().array().optional(),
140
+ subject: z.string(),
141
+ text: z.string().nullable(),
142
+ });
143
+ export const RESEND_RECEIVED_EMAIL_SCHEMA = RESEND_RECEIVED_EMAIL_WIRE_SCHEMA.transform(({ attachments, created_at: createdAt, message_id: messageId, raw, received_for: receivedFor, reply_to: replyTo, ...email }) => ({
144
+ ...email,
145
+ attachments: attachments.map((attachment) => RESEND_RECEIVED_ATTACHMENT_SCHEMA.parse(attachment)),
146
+ createdAt,
147
+ messageId,
148
+ raw: raw === null || raw === undefined
149
+ ? raw
150
+ : { downloadUrl: raw.download_url, expiresAt: raw.expires_at },
151
+ receivedFor,
152
+ replyTo,
153
+ }));
154
+ export const RESEND_CONTACT_SUMMARY_WIRE_SCHEMA = z.object({
155
+ created_at: z.string(),
156
+ email: z.email(),
157
+ first_name: z.string().nullable(),
158
+ id: z.string(),
159
+ last_name: z.string().nullable(),
160
+ unsubscribed: z.boolean(),
161
+ });
162
+ export const RESEND_CONTACT_SUMMARY_SCHEMA = RESEND_CONTACT_SUMMARY_WIRE_SCHEMA.transform(({ created_at: createdAt, first_name: firstName, last_name: lastName, ...contact }) => ({ ...contact, createdAt, firstName, lastName }));
163
+ export const RESEND_CONTACT_WIRE_SCHEMA = RESEND_CONTACT_SUMMARY_WIRE_SCHEMA.extend({
164
+ object: z.literal("contact"),
165
+ properties: z.record(z.string(), z.discriminatedUnion("type", [
166
+ z.object({ type: z.literal("number"), value: z.number() }),
167
+ z.object({ type: z.literal("string"), value: z.string() }),
168
+ ])),
169
+ });
170
+ export const RESEND_CONTACT_SCHEMA = RESEND_CONTACT_WIRE_SCHEMA.transform(({ created_at: createdAt, first_name: firstName, last_name: lastName, ...contact }) => ({ ...contact, createdAt, firstName, lastName }));
171
+ export const RESEND_SEGMENT_SUMMARY_WIRE_SCHEMA = z.object({
172
+ created_at: z.string(),
173
+ id: z.string(),
174
+ name: z.string(),
175
+ });
176
+ export const RESEND_SEGMENT_SUMMARY_SCHEMA = RESEND_SEGMENT_SUMMARY_WIRE_SCHEMA.transform(({ created_at: createdAt, ...segment }) => ({ ...segment, createdAt }));
177
+ export const RESEND_CONTACT_IMPORT_COUNTS_SCHEMA = z.object({
178
+ created: z.number().int().nonnegative(),
179
+ failed: z.number().int().nonnegative(),
180
+ skipped: z.number().int().nonnegative(),
181
+ total: z.number().int().nonnegative(),
182
+ updated: z.number().int().nonnegative(),
183
+ });
184
+ export const RESEND_CONTACT_IMPORT_WIRE_SCHEMA = z.object({
185
+ completed_at: z.string().nullable().optional(),
186
+ counts: RESEND_CONTACT_IMPORT_COUNTS_SCHEMA.optional(),
187
+ created_at: z.string(),
188
+ id: z.string(),
189
+ object: z.literal("contact_import"),
190
+ status: z.enum(["completed", "failed", "in_progress", "queued"]),
191
+ });
192
+ export const RESEND_CONTACT_IMPORT_SCHEMA = RESEND_CONTACT_IMPORT_WIRE_SCHEMA.transform(({ completed_at: completedAt, created_at: createdAt, ...contactImport }) => ({ ...contactImport, completedAt, createdAt }));
193
+ export const RESEND_CONTACT_TOPIC_SCHEMA = z.object({
194
+ description: z.string(),
195
+ id: z.string(),
196
+ name: z.string(),
197
+ subscription: z.enum(["opt_in", "opt_out"]),
198
+ });
199
+ export const RESEND_CONTACT_SEGMENT_DELETE_WIRE_SCHEMA = z.object({
200
+ contact_id: z.string(),
201
+ deleted: z.boolean(),
202
+ object: z.literal("contact_segment"),
203
+ segment_id: z.string(),
204
+ });
205
+ export const RESEND_CONTACT_SEGMENT_MUTATION_WIRE_SCHEMA = z.object({
206
+ contact_id: z.string(),
207
+ object: z.literal("contact_segment"),
208
+ segment_id: z.string(),
209
+ });
210
+ export const RESEND_CONTACT_SEGMENT_MUTATION_SCHEMA = RESEND_CONTACT_SEGMENT_MUTATION_WIRE_SCHEMA.transform(({ contact_id: contactId, segment_id: segmentId, ...result }) => ({
211
+ ...result,
212
+ contactId,
213
+ segmentId,
214
+ }));
215
+ export const RESEND_CONTACT_SEGMENT_DELETE_SCHEMA = RESEND_CONTACT_SEGMENT_DELETE_WIRE_SCHEMA.transform(({ contact_id: contactId, segment_id: segmentId, ...result }) => ({
216
+ ...result,
217
+ contactId,
218
+ segmentId,
219
+ }));
220
+ export const RESEND_CONTACT_TOPICS_UPDATE_WIRE_SCHEMA = z.object({
221
+ contact_id: z.string(),
222
+ object: z.literal("contact_topics"),
223
+ topics: z
224
+ .object({
225
+ id: z.string(),
226
+ subscription: z.enum(["opt_in", "opt_out"]),
227
+ })
228
+ .array(),
229
+ });
230
+ export const RESEND_CONTACT_TOPICS_UPDATE_SCHEMA = RESEND_CONTACT_TOPICS_UPDATE_WIRE_SCHEMA.transform(({ contact_id: contactId, ...result }) => ({ ...result, contactId }));
231
+ export const RESEND_EVENT_SEND_RESPONSE_SCHEMA = z.object({
232
+ event: z.string(),
233
+ object: z.literal("event"),
234
+ });
235
+ export const RESEND_EVENT_VALUE_TYPE_SCHEMA = z.enum([
236
+ "boolean",
237
+ "date",
238
+ "number",
239
+ "string",
240
+ ]);
241
+ export const RESEND_EVENT_DEFINITION_WIRE_SCHEMA = z.object({
242
+ created_at: z.string(),
243
+ id: z.string(),
244
+ name: z.string(),
245
+ object: z.literal("event").optional(),
246
+ schema: z.record(z.string(), RESEND_EVENT_VALUE_TYPE_SCHEMA).nullable(),
247
+ updated_at: z.string().nullable(),
248
+ });
249
+ export const RESEND_EVENT_DEFINITION_SCHEMA = RESEND_EVENT_DEFINITION_WIRE_SCHEMA.transform(({ created_at: createdAt, updated_at: updatedAt, ...event }) => ({
250
+ ...event,
251
+ createdAt,
252
+ updatedAt,
253
+ }));
254
+ /**
255
+ * Creates a Resend list response schema for one provider item schema.
256
+ *
257
+ * @param item - Provider item schema contained in the list.
258
+ */
259
+ export function resendListWireSchema(item) {
260
+ return z.object({
261
+ data: item.array(),
262
+ has_more: z.boolean(),
263
+ object: z.literal("list"),
264
+ });
265
+ }
266
+ /**
267
+ * Creates a normalized Resend list response schema.
268
+ *
269
+ * @param item - Normalized item schema contained in the list.
270
+ */
271
+ export function resendListSchema(item) {
272
+ return resendListWireSchema(item).transform(({ has_more: hasMore, ...page }) => ({ ...page, hasMore }));
273
+ }
@@ -0,0 +1,95 @@
1
+ import type { JsonValue } from "type-fest";
2
+ import * as z from "zod";
3
+ export * from "./action-schemas.js";
4
+ export declare const RESEND_USER_AGENT = "automate.ax/resend";
5
+ /** Scalar value serialized into a Resend query parameter. */
6
+ type ResendQueryScalar = boolean | number | string;
7
+ /** Provider rate-limit values returned with a Resend response. */
8
+ export interface ResendRateLimitMetadata {
9
+ /** Maximum requests allowed in the current window. */
10
+ limit?: number;
11
+ /** Requests remaining in the current window. */
12
+ remaining?: number;
13
+ /** Seconds until the current window resets. */
14
+ reset?: number;
15
+ }
16
+ /** Provider email-quota usage returned with a Resend response. */
17
+ export interface ResendQuotaMetadata {
18
+ /** Used daily quota, when Resend supplies the free-plan header. */
19
+ daily?: number;
20
+ /** Used monthly quota. */
21
+ monthly?: number;
22
+ }
23
+ /** Options for one authenticated Resend API call. */
24
+ export interface ResendApiCallOptions<TSchema extends z.ZodType> {
25
+ /** Provider-native JSON or multipart request body. */
26
+ body?: FormData | JsonValue;
27
+ /** Additional request headers such as `Idempotency-Key`. */
28
+ headers?: RequestInit["headers"];
29
+ /** HTTP method. Defaults to `POST` with a body and `GET` otherwise. */
30
+ httpMethod?: "DELETE" | "GET" | "PATCH" | "POST";
31
+ /** Provider-native URL query parameters. Array values are repeated. */
32
+ query?: Record<string, ResendQueryScalar | ResendQueryScalar[] | undefined>;
33
+ /** Schema for the complete successful provider response. */
34
+ responseSchema: TSchema;
35
+ }
36
+ /** Authenticated, schema-validating Resend REST API helper. */
37
+ export interface ResendApi {
38
+ /**
39
+ * Calls a Resend endpoint and validates its successful response.
40
+ *
41
+ * @param path - Resend API path such as `/emails`.
42
+ * @param options - Request data and response schema.
43
+ */
44
+ call<TSchema extends z.ZodType>(path: string, options: ResendApiCallOptions<TSchema>): Promise<z.output<TSchema>>;
45
+ }
46
+ /** Structured failure returned by the Resend API. */
47
+ export declare class ResendApiError extends Error {
48
+ /** API path that failed. */
49
+ readonly path: string;
50
+ /** Provider error classification. */
51
+ readonly providerName?: string;
52
+ /** Provider status code from the JSON error body. */
53
+ readonly providerStatusCode?: number;
54
+ /** Email-quota usage returned with the failed response. */
55
+ readonly quota: ResendQuotaMetadata;
56
+ /** Rate-limit values returned with the failed response. */
57
+ readonly rateLimit: ResendRateLimitMetadata;
58
+ /** Delay in seconds before the request should be retried. */
59
+ readonly retryAfter?: number;
60
+ /** HTTP status returned by Resend. */
61
+ readonly status: number;
62
+ /**
63
+ * Creates a structured Resend provider error.
64
+ *
65
+ * @param options - Provider and transport failure details.
66
+ * @param options.message - Human-readable provider failure.
67
+ * @param options.path - API path that failed.
68
+ * @param options.providerName - Provider error classification.
69
+ * @param options.providerStatusCode - Provider status code from JSON.
70
+ * @param options.quota - Provider email-quota metadata.
71
+ * @param options.rateLimit - Provider rate-limit metadata.
72
+ * @param options.retryAfter - Provider retry delay in seconds.
73
+ * @param options.status - HTTP status.
74
+ */
75
+ constructor(options: {
76
+ message: string;
77
+ path: string;
78
+ providerName?: string;
79
+ providerStatusCode?: number;
80
+ quota: ResendQuotaMetadata;
81
+ rateLimit: ResendRateLimitMetadata;
82
+ retryAfter?: number;
83
+ status: number;
84
+ });
85
+ }
86
+ /**
87
+ * Creates a raw authenticated Resend REST client.
88
+ *
89
+ * The helper accepts either an API-key secret (`{ apiKey }`) or OAuth secret
90
+ * (`{ accessToken }`). Callers must supply a response schema for every
91
+ * request.
92
+ *
93
+ * @param secret - Resolved Resend integration secret.
94
+ */
95
+ export declare function getResendApi(secret: Record<string, unknown>): ResendApi;
@@ -0,0 +1,171 @@
1
+ import * as z from "zod";
2
+ export * from "./action-schemas.js";
3
+ const RESEND_API_BASE_URL = "https://api.resend.com/";
4
+ const RESEND_API_ORIGIN = new URL(RESEND_API_BASE_URL).origin;
5
+ export const RESEND_USER_AGENT = "automate.ax/resend";
6
+ const RESEND_API_KEY_SECRET_SCHEMA = z.object({
7
+ accessToken: z.never().optional(),
8
+ apiKey: z.string().min(1),
9
+ });
10
+ const RESEND_OAUTH_SECRET_SCHEMA = z.object({
11
+ accessToken: z.string().min(1),
12
+ apiKey: z.never().optional(),
13
+ });
14
+ const RESEND_SECRET_SCHEMA = z.union([
15
+ RESEND_API_KEY_SECRET_SCHEMA,
16
+ RESEND_OAUTH_SECRET_SCHEMA,
17
+ ]);
18
+ const RESEND_ERROR_SCHEMA = z.looseObject({
19
+ message: z.string().optional(),
20
+ name: z.string().optional(),
21
+ statusCode: z.number().int().optional(),
22
+ });
23
+ /** Structured failure returned by the Resend API. */
24
+ export class ResendApiError extends Error {
25
+ /** API path that failed. */
26
+ path;
27
+ /** Provider error classification. */
28
+ providerName;
29
+ /** Provider status code from the JSON error body. */
30
+ providerStatusCode;
31
+ /** Email-quota usage returned with the failed response. */
32
+ quota;
33
+ /** Rate-limit values returned with the failed response. */
34
+ rateLimit;
35
+ /** Delay in seconds before the request should be retried. */
36
+ retryAfter;
37
+ /** HTTP status returned by Resend. */
38
+ status;
39
+ /**
40
+ * Creates a structured Resend provider error.
41
+ *
42
+ * @param options - Provider and transport failure details.
43
+ * @param options.message - Human-readable provider failure.
44
+ * @param options.path - API path that failed.
45
+ * @param options.providerName - Provider error classification.
46
+ * @param options.providerStatusCode - Provider status code from JSON.
47
+ * @param options.quota - Provider email-quota metadata.
48
+ * @param options.rateLimit - Provider rate-limit metadata.
49
+ * @param options.retryAfter - Provider retry delay in seconds.
50
+ * @param options.status - HTTP status.
51
+ */
52
+ constructor(options) {
53
+ super(`Resend ${options.path} failed: ${options.message}`);
54
+ this.name = "ResendApiError";
55
+ this.path = options.path;
56
+ this.providerName = options.providerName;
57
+ this.providerStatusCode = options.providerStatusCode;
58
+ this.quota = options.quota;
59
+ this.rateLimit = options.rateLimit;
60
+ this.retryAfter = options.retryAfter;
61
+ this.status = options.status;
62
+ }
63
+ }
64
+ /**
65
+ * Creates a raw authenticated Resend REST client.
66
+ *
67
+ * The helper accepts either an API-key secret (`{ apiKey }`) or OAuth secret
68
+ * (`{ accessToken }`). Callers must supply a response schema for every
69
+ * request.
70
+ *
71
+ * @param secret - Resolved Resend integration secret.
72
+ */
73
+ export function getResendApi(secret) {
74
+ const credential = RESEND_SECRET_SCHEMA.parse(secret);
75
+ const token = "apiKey" in credential ? credential.apiKey : credential.accessToken;
76
+ return {
77
+ call: async (path, options) => {
78
+ const url = new URL(path, RESEND_API_BASE_URL);
79
+ if (url.origin !== RESEND_API_ORIGIN) {
80
+ throw new Error("Resend API paths must use the Resend API origin.");
81
+ }
82
+ const normalizedPath = `${url.pathname}${url.search}`;
83
+ for (const [name, value] of Object.entries(options.query ?? {})) {
84
+ for (const item of Array.isArray(value) ? value : [value]) {
85
+ if (item !== undefined)
86
+ url.searchParams.append(name, String(item));
87
+ }
88
+ }
89
+ const headers = new Headers(options.headers);
90
+ headers.set("Accept", "application/json");
91
+ headers.set("Authorization", `Bearer ${token}`);
92
+ headers.set("User-Agent", RESEND_USER_AGENT);
93
+ const body = options.body;
94
+ const isMultipart = body instanceof FormData;
95
+ if (body !== undefined && !isMultipart) {
96
+ headers.set("Content-Type", "application/json");
97
+ }
98
+ const response = await fetch(url, {
99
+ body: body instanceof FormData
100
+ ? body
101
+ : body === undefined
102
+ ? undefined
103
+ : JSON.stringify(body),
104
+ headers,
105
+ method: options.httpMethod ?? (options.body === undefined ? "GET" : "POST"),
106
+ });
107
+ const responseText = await response.text();
108
+ const metadata = getResponseMetadata(response.headers);
109
+ if (!response.ok) {
110
+ const result = RESEND_ERROR_SCHEMA.safeParse(parseJson(responseText));
111
+ throw new ResendApiError({
112
+ message: (result.success ? result.data.message : undefined) ??
113
+ (responseText.trim() || `HTTP ${response.status}`),
114
+ path: normalizedPath,
115
+ providerName: result.success ? result.data.name : undefined,
116
+ providerStatusCode: result.success
117
+ ? result.data.statusCode
118
+ : undefined,
119
+ status: response.status,
120
+ ...metadata,
121
+ });
122
+ }
123
+ return options.responseSchema.parse(responseText.trim() ? parseJson(responseText) : undefined);
124
+ },
125
+ };
126
+ }
127
+ /**
128
+ * Parses JSON while preserving useful plain-text error handling.
129
+ *
130
+ * @param value - Response text to parse.
131
+ */
132
+ function parseJson(value) {
133
+ try {
134
+ return JSON.parse(value);
135
+ }
136
+ catch {
137
+ return undefined;
138
+ }
139
+ }
140
+ /**
141
+ * Extracts retry, rate-limit, and quota headers from one response.
142
+ *
143
+ * @param headers - Resend response headers.
144
+ */
145
+ function getResponseMetadata(headers) {
146
+ return {
147
+ quota: {
148
+ daily: parseNumberHeader(headers, "x-resend-daily-quota"),
149
+ monthly: parseNumberHeader(headers, "x-resend-monthly-quota"),
150
+ },
151
+ rateLimit: {
152
+ limit: parseNumberHeader(headers, "ratelimit-limit"),
153
+ remaining: parseNumberHeader(headers, "ratelimit-remaining"),
154
+ reset: parseNumberHeader(headers, "ratelimit-reset"),
155
+ },
156
+ retryAfter: parseNumberHeader(headers, "retry-after"),
157
+ };
158
+ }
159
+ /**
160
+ * Parses one numeric response header.
161
+ *
162
+ * @param headers - Resend response headers.
163
+ * @param name - Case-insensitive header name.
164
+ */
165
+ function parseNumberHeader(headers, name) {
166
+ const value = headers.get(name);
167
+ if (value === null)
168
+ return undefined;
169
+ const parsed = Number(value);
170
+ return Number.isFinite(parsed) ? parsed : undefined;
171
+ }