@vrplatform/email 1.0.2-stage.446 → 1.0.2-stage.513
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/build/main/utils/send-email.d.ts +14 -2
- package/build/main/utils/send-email.js +63 -16
- package/build/main/utils/send-email.js.map +1 -1
- package/build/module/utils/send-email.d.ts +14 -2
- package/build/module/utils/send-email.js +62 -16
- package/build/module/utils/send-email.js.map +1 -1
- package/package.json +1 -1
- package/src/utils/send-email.ts +100 -19
|
@@ -1,7 +1,17 @@
|
|
|
1
|
+
type EmailDeliveryFailureDetails = {
|
|
2
|
+
httpStatus: number;
|
|
3
|
+
postmarkErrorCode?: number;
|
|
4
|
+
provider: 'postmark';
|
|
5
|
+
providerCategory: 'inactiveRecipient' | 'rejected';
|
|
6
|
+
};
|
|
7
|
+
type EmailDeliveryErrorDetails = EmailDeliveryFailureDetails | {
|
|
8
|
+
failureCount: number;
|
|
9
|
+
failures: EmailDeliveryFailureDetails[];
|
|
10
|
+
};
|
|
1
11
|
export declare class EmailDeliveryError extends Error {
|
|
2
|
-
readonly details?:
|
|
12
|
+
readonly details?: EmailDeliveryErrorDetails | undefined;
|
|
3
13
|
readonly expected: boolean;
|
|
4
|
-
constructor(message: string, details?:
|
|
14
|
+
constructor(message: string, details?: EmailDeliveryErrorDetails | undefined, expected?: boolean);
|
|
5
15
|
}
|
|
6
16
|
export declare function sendEmail(data: {
|
|
7
17
|
to: string;
|
|
@@ -15,3 +25,5 @@ export declare function sendEmail(data: {
|
|
|
15
25
|
}): Promise<{
|
|
16
26
|
status: number;
|
|
17
27
|
}>;
|
|
28
|
+
export declare function settleEmailDeliveries(deliveries: Promise<unknown>[]): Promise<void>;
|
|
29
|
+
export {};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.EmailDeliveryError = void 0;
|
|
4
4
|
exports.sendEmail = sendEmail;
|
|
5
|
+
exports.settleEmailDeliveries = settleEmailDeliveries;
|
|
5
6
|
class EmailDeliveryError extends Error {
|
|
6
7
|
constructor(message, details, expected = false) {
|
|
7
8
|
super(message);
|
|
@@ -33,27 +34,73 @@ async function sendEmail(data, options) {
|
|
|
33
34
|
}),
|
|
34
35
|
});
|
|
35
36
|
const providerResponse = await response.json().catch(() => ({}));
|
|
36
|
-
const
|
|
37
|
+
const responseErrorCode = providerResponse !== null &&
|
|
37
38
|
typeof providerResponse === 'object' &&
|
|
38
|
-
'
|
|
39
|
-
typeof providerResponse.
|
|
40
|
-
? providerResponse.
|
|
39
|
+
'ErrorCode' in providerResponse &&
|
|
40
|
+
typeof providerResponse.ErrorCode === 'number'
|
|
41
|
+
? providerResponse.ErrorCode
|
|
42
|
+
: undefined;
|
|
43
|
+
const headerErrorCode = response.headers.get('X-PM-ApiErrorCode');
|
|
44
|
+
const errorCode = responseErrorCode ??
|
|
45
|
+
(headerErrorCode && /^\d+$/.test(headerErrorCode)
|
|
46
|
+
? Number(headerErrorCode)
|
|
47
|
+
: undefined);
|
|
48
|
+
const postmarkErrorCode = typeof errorCode === 'number' &&
|
|
49
|
+
Number.isSafeInteger(errorCode) &&
|
|
50
|
+
errorCode >= 0
|
|
51
|
+
? errorCode
|
|
41
52
|
: undefined;
|
|
42
53
|
if (!response.ok) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
54
|
+
const inactiveRecipient = response.status === 422 && postmarkErrorCode === 406;
|
|
55
|
+
const details = {
|
|
56
|
+
provider: 'postmark',
|
|
57
|
+
providerCategory: inactiveRecipient ? 'inactiveRecipient' : 'rejected',
|
|
58
|
+
httpStatus: response.status,
|
|
59
|
+
...(postmarkErrorCode !== undefined ? { postmarkErrorCode } : {}),
|
|
60
|
+
};
|
|
61
|
+
if (inactiveRecipient) {
|
|
62
|
+
throw new EmailDeliveryError('Postmark recipient is inactive', details, true);
|
|
50
63
|
}
|
|
51
|
-
throw new EmailDeliveryError('Postmark rejected the email',
|
|
52
|
-
providerResponse,
|
|
53
|
-
status: response.status,
|
|
54
|
-
statusText: response.statusText,
|
|
55
|
-
});
|
|
64
|
+
throw new EmailDeliveryError('Postmark rejected the email', details);
|
|
56
65
|
}
|
|
57
66
|
return { status: response.status };
|
|
58
67
|
}
|
|
68
|
+
async function settleEmailDeliveries(deliveries) {
|
|
69
|
+
const failures = (await Promise.allSettled(deliveries)).filter((result) => result.status === 'rejected');
|
|
70
|
+
if (failures.length === 0)
|
|
71
|
+
return;
|
|
72
|
+
const unexpectedDeliveryError = failures.find((failure) => failure.reason instanceof EmailDeliveryError && !failure.reason.expected)?.reason;
|
|
73
|
+
const allExpected = failures.every((failure) => failure.reason instanceof EmailDeliveryError && failure.reason.expected);
|
|
74
|
+
const firstDeliveryError = failures.find((failure) => failure.reason instanceof EmailDeliveryError)?.reason;
|
|
75
|
+
const failureDetails = failures.flatMap((failure) => {
|
|
76
|
+
if (!(failure.reason instanceof EmailDeliveryError) ||
|
|
77
|
+
!failure.reason.details ||
|
|
78
|
+
!('provider' in failure.reason.details)) {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
const { httpStatus, postmarkErrorCode, provider, providerCategory } = failure.reason.details;
|
|
82
|
+
return [
|
|
83
|
+
{
|
|
84
|
+
httpStatus,
|
|
85
|
+
provider,
|
|
86
|
+
providerCategory,
|
|
87
|
+
...(postmarkErrorCode !== undefined ? { postmarkErrorCode } : {}),
|
|
88
|
+
},
|
|
89
|
+
];
|
|
90
|
+
});
|
|
91
|
+
const details = {
|
|
92
|
+
failureCount: failures.length,
|
|
93
|
+
failures: failureDetails,
|
|
94
|
+
};
|
|
95
|
+
if (unexpectedDeliveryError instanceof EmailDeliveryError) {
|
|
96
|
+
if (unexpectedDeliveryError.message === 'Postmark is not configured') {
|
|
97
|
+
throw new EmailDeliveryError('Postmark is not configured', details);
|
|
98
|
+
}
|
|
99
|
+
throw new EmailDeliveryError('Postmark rejected the email', details);
|
|
100
|
+
}
|
|
101
|
+
if (allExpected && firstDeliveryError instanceof EmailDeliveryError) {
|
|
102
|
+
throw new EmailDeliveryError('Postmark recipient is inactive', details, true);
|
|
103
|
+
}
|
|
104
|
+
throw new EmailDeliveryError('Email delivery failed', details);
|
|
105
|
+
}
|
|
59
106
|
//# sourceMappingURL=send-email.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"send-email.js","sourceRoot":"src/","sources":["utils/send-email.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"send-email.js","sourceRoot":"src/","sources":["utils/send-email.ts"],"names":[],"mappings":"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","sourcesContent":["type EmailDeliveryFailureDetails = {\n httpStatus: number;\n postmarkErrorCode?: number;\n provider: 'postmark';\n providerCategory: 'inactiveRecipient' | 'rejected';\n};\n\ntype EmailDeliveryErrorDetails =\n | EmailDeliveryFailureDetails\n | {\n failureCount: number;\n failures: EmailDeliveryFailureDetails[];\n };\n\nexport class EmailDeliveryError extends Error {\n constructor(\n message: string,\n public readonly details?: EmailDeliveryErrorDetails,\n public readonly expected = false\n ) {\n super(message);\n this.name = 'EmailDeliveryError';\n }\n}\n\nexport async function sendEmail(\n data: {\n to: string;\n subject: string;\n text: string;\n html: string;\n replyTo?: string;\n },\n options: {\n fetch?: (\n input: string | URL | Request,\n init?: RequestInit\n ) => Promise<Response>;\n postmarkToken?: string;\n }\n) {\n if (!options.postmarkToken) {\n throw new EmailDeliveryError('Postmark is not configured');\n }\n\n const response = await (options.fetch ?? fetch)(\n 'https://api.postmarkapp.com/email',\n {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n 'X-Postmark-Server-Token': options.postmarkToken,\n },\n body: JSON.stringify({\n From: 'VRPlatform <notifications@vrplatform.app>',\n To: data.to,\n Subject: data.subject,\n ReplyTo: data.replyTo,\n TextBody: data.text,\n HtmlBody: data.html,\n MessageStream: 'outbound',\n }),\n }\n );\n\n const providerResponse: unknown = await response.json().catch(() => ({}));\n const responseErrorCode =\n providerResponse !== null &&\n typeof providerResponse === 'object' &&\n 'ErrorCode' in providerResponse &&\n typeof providerResponse.ErrorCode === 'number'\n ? providerResponse.ErrorCode\n : undefined;\n const headerErrorCode = response.headers.get('X-PM-ApiErrorCode');\n const errorCode =\n responseErrorCode ??\n (headerErrorCode && /^\\d+$/.test(headerErrorCode)\n ? Number(headerErrorCode)\n : undefined);\n const postmarkErrorCode =\n typeof errorCode === 'number' &&\n Number.isSafeInteger(errorCode) &&\n errorCode >= 0\n ? errorCode\n : undefined;\n\n if (!response.ok) {\n const inactiveRecipient =\n response.status === 422 && postmarkErrorCode === 406;\n const details: EmailDeliveryFailureDetails = {\n provider: 'postmark',\n providerCategory: inactiveRecipient ? 'inactiveRecipient' : 'rejected',\n httpStatus: response.status,\n ...(postmarkErrorCode !== undefined ? { postmarkErrorCode } : {}),\n };\n\n if (inactiveRecipient) {\n throw new EmailDeliveryError(\n 'Postmark recipient is inactive',\n details,\n true\n );\n }\n\n throw new EmailDeliveryError('Postmark rejected the email', details);\n }\n\n return { status: response.status };\n}\n\nexport async function settleEmailDeliveries(deliveries: Promise<unknown>[]) {\n const failures = (await Promise.allSettled(deliveries)).filter(\n (result) => result.status === 'rejected'\n );\n if (failures.length === 0) return;\n\n const unexpectedDeliveryError = failures.find(\n (failure) =>\n failure.reason instanceof EmailDeliveryError && !failure.reason.expected\n )?.reason;\n const allExpected = failures.every(\n (failure) =>\n failure.reason instanceof EmailDeliveryError && failure.reason.expected\n );\n const firstDeliveryError = failures.find(\n (failure) => failure.reason instanceof EmailDeliveryError\n )?.reason;\n const failureDetails = failures.flatMap((failure) => {\n if (\n !(failure.reason instanceof EmailDeliveryError) ||\n !failure.reason.details ||\n !('provider' in failure.reason.details)\n ) {\n return [];\n }\n const { httpStatus, postmarkErrorCode, provider, providerCategory } =\n failure.reason.details;\n return [\n {\n httpStatus,\n provider,\n providerCategory,\n ...(postmarkErrorCode !== undefined ? { postmarkErrorCode } : {}),\n },\n ];\n });\n\n const details: EmailDeliveryErrorDetails = {\n failureCount: failures.length,\n failures: failureDetails,\n };\n if (unexpectedDeliveryError instanceof EmailDeliveryError) {\n if (unexpectedDeliveryError.message === 'Postmark is not configured') {\n throw new EmailDeliveryError('Postmark is not configured', details);\n }\n throw new EmailDeliveryError('Postmark rejected the email', details);\n }\n if (allExpected && firstDeliveryError instanceof EmailDeliveryError) {\n throw new EmailDeliveryError(\n 'Postmark recipient is inactive',\n details,\n true\n );\n }\n throw new EmailDeliveryError('Email delivery failed', details);\n}\n"]}
|
|
@@ -1,7 +1,17 @@
|
|
|
1
|
+
type EmailDeliveryFailureDetails = {
|
|
2
|
+
httpStatus: number;
|
|
3
|
+
postmarkErrorCode?: number;
|
|
4
|
+
provider: 'postmark';
|
|
5
|
+
providerCategory: 'inactiveRecipient' | 'rejected';
|
|
6
|
+
};
|
|
7
|
+
type EmailDeliveryErrorDetails = EmailDeliveryFailureDetails | {
|
|
8
|
+
failureCount: number;
|
|
9
|
+
failures: EmailDeliveryFailureDetails[];
|
|
10
|
+
};
|
|
1
11
|
export declare class EmailDeliveryError extends Error {
|
|
2
|
-
readonly details?:
|
|
12
|
+
readonly details?: EmailDeliveryErrorDetails | undefined;
|
|
3
13
|
readonly expected: boolean;
|
|
4
|
-
constructor(message: string, details?:
|
|
14
|
+
constructor(message: string, details?: EmailDeliveryErrorDetails | undefined, expected?: boolean);
|
|
5
15
|
}
|
|
6
16
|
export declare function sendEmail(data: {
|
|
7
17
|
to: string;
|
|
@@ -15,3 +25,5 @@ export declare function sendEmail(data: {
|
|
|
15
25
|
}): Promise<{
|
|
16
26
|
status: number;
|
|
17
27
|
}>;
|
|
28
|
+
export declare function settleEmailDeliveries(deliveries: Promise<unknown>[]): Promise<void>;
|
|
29
|
+
export {};
|
|
@@ -30,27 +30,73 @@ export async function sendEmail(data, options) {
|
|
|
30
30
|
}),
|
|
31
31
|
});
|
|
32
32
|
const providerResponse = await response.json().catch(() => ({}));
|
|
33
|
-
const
|
|
33
|
+
const responseErrorCode = providerResponse !== null &&
|
|
34
34
|
typeof providerResponse === 'object' &&
|
|
35
|
-
'
|
|
36
|
-
typeof providerResponse.
|
|
37
|
-
? providerResponse.
|
|
35
|
+
'ErrorCode' in providerResponse &&
|
|
36
|
+
typeof providerResponse.ErrorCode === 'number'
|
|
37
|
+
? providerResponse.ErrorCode
|
|
38
|
+
: undefined;
|
|
39
|
+
const headerErrorCode = response.headers.get('X-PM-ApiErrorCode');
|
|
40
|
+
const errorCode = responseErrorCode ??
|
|
41
|
+
(headerErrorCode && /^\d+$/.test(headerErrorCode)
|
|
42
|
+
? Number(headerErrorCode)
|
|
43
|
+
: undefined);
|
|
44
|
+
const postmarkErrorCode = typeof errorCode === 'number' &&
|
|
45
|
+
Number.isSafeInteger(errorCode) &&
|
|
46
|
+
errorCode >= 0
|
|
47
|
+
? errorCode
|
|
38
48
|
: undefined;
|
|
39
49
|
if (!response.ok) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
50
|
+
const inactiveRecipient = response.status === 422 && postmarkErrorCode === 406;
|
|
51
|
+
const details = {
|
|
52
|
+
provider: 'postmark',
|
|
53
|
+
providerCategory: inactiveRecipient ? 'inactiveRecipient' : 'rejected',
|
|
54
|
+
httpStatus: response.status,
|
|
55
|
+
...(postmarkErrorCode !== undefined ? { postmarkErrorCode } : {}),
|
|
56
|
+
};
|
|
57
|
+
if (inactiveRecipient) {
|
|
58
|
+
throw new EmailDeliveryError('Postmark recipient is inactive', details, true);
|
|
47
59
|
}
|
|
48
|
-
throw new EmailDeliveryError('Postmark rejected the email',
|
|
49
|
-
providerResponse,
|
|
50
|
-
status: response.status,
|
|
51
|
-
statusText: response.statusText,
|
|
52
|
-
});
|
|
60
|
+
throw new EmailDeliveryError('Postmark rejected the email', details);
|
|
53
61
|
}
|
|
54
62
|
return { status: response.status };
|
|
55
63
|
}
|
|
64
|
+
export async function settleEmailDeliveries(deliveries) {
|
|
65
|
+
const failures = (await Promise.allSettled(deliveries)).filter((result) => result.status === 'rejected');
|
|
66
|
+
if (failures.length === 0)
|
|
67
|
+
return;
|
|
68
|
+
const unexpectedDeliveryError = failures.find((failure) => failure.reason instanceof EmailDeliveryError && !failure.reason.expected)?.reason;
|
|
69
|
+
const allExpected = failures.every((failure) => failure.reason instanceof EmailDeliveryError && failure.reason.expected);
|
|
70
|
+
const firstDeliveryError = failures.find((failure) => failure.reason instanceof EmailDeliveryError)?.reason;
|
|
71
|
+
const failureDetails = failures.flatMap((failure) => {
|
|
72
|
+
if (!(failure.reason instanceof EmailDeliveryError) ||
|
|
73
|
+
!failure.reason.details ||
|
|
74
|
+
!('provider' in failure.reason.details)) {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
const { httpStatus, postmarkErrorCode, provider, providerCategory } = failure.reason.details;
|
|
78
|
+
return [
|
|
79
|
+
{
|
|
80
|
+
httpStatus,
|
|
81
|
+
provider,
|
|
82
|
+
providerCategory,
|
|
83
|
+
...(postmarkErrorCode !== undefined ? { postmarkErrorCode } : {}),
|
|
84
|
+
},
|
|
85
|
+
];
|
|
86
|
+
});
|
|
87
|
+
const details = {
|
|
88
|
+
failureCount: failures.length,
|
|
89
|
+
failures: failureDetails,
|
|
90
|
+
};
|
|
91
|
+
if (unexpectedDeliveryError instanceof EmailDeliveryError) {
|
|
92
|
+
if (unexpectedDeliveryError.message === 'Postmark is not configured') {
|
|
93
|
+
throw new EmailDeliveryError('Postmark is not configured', details);
|
|
94
|
+
}
|
|
95
|
+
throw new EmailDeliveryError('Postmark rejected the email', details);
|
|
96
|
+
}
|
|
97
|
+
if (allExpected && firstDeliveryError instanceof EmailDeliveryError) {
|
|
98
|
+
throw new EmailDeliveryError('Postmark recipient is inactive', details, true);
|
|
99
|
+
}
|
|
100
|
+
throw new EmailDeliveryError('Email delivery failed', details);
|
|
101
|
+
}
|
|
56
102
|
//# sourceMappingURL=send-email.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"send-email.js","sourceRoot":"src/","sources":["utils/send-email.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"send-email.js","sourceRoot":"src/","sources":["utils/send-email.ts"],"names":[],"mappings":"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","sourcesContent":["type EmailDeliveryFailureDetails = {\n httpStatus: number;\n postmarkErrorCode?: number;\n provider: 'postmark';\n providerCategory: 'inactiveRecipient' | 'rejected';\n};\n\ntype EmailDeliveryErrorDetails =\n | EmailDeliveryFailureDetails\n | {\n failureCount: number;\n failures: EmailDeliveryFailureDetails[];\n };\n\nexport class EmailDeliveryError extends Error {\n constructor(\n message: string,\n public readonly details?: EmailDeliveryErrorDetails,\n public readonly expected = false\n ) {\n super(message);\n this.name = 'EmailDeliveryError';\n }\n}\n\nexport async function sendEmail(\n data: {\n to: string;\n subject: string;\n text: string;\n html: string;\n replyTo?: string;\n },\n options: {\n fetch?: (\n input: string | URL | Request,\n init?: RequestInit\n ) => Promise<Response>;\n postmarkToken?: string;\n }\n) {\n if (!options.postmarkToken) {\n throw new EmailDeliveryError('Postmark is not configured');\n }\n\n const response = await (options.fetch ?? fetch)(\n 'https://api.postmarkapp.com/email',\n {\n method: 'POST',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/json',\n 'X-Postmark-Server-Token': options.postmarkToken,\n },\n body: JSON.stringify({\n From: 'VRPlatform <notifications@vrplatform.app>',\n To: data.to,\n Subject: data.subject,\n ReplyTo: data.replyTo,\n TextBody: data.text,\n HtmlBody: data.html,\n MessageStream: 'outbound',\n }),\n }\n );\n\n const providerResponse: unknown = await response.json().catch(() => ({}));\n const responseErrorCode =\n providerResponse !== null &&\n typeof providerResponse === 'object' &&\n 'ErrorCode' in providerResponse &&\n typeof providerResponse.ErrorCode === 'number'\n ? providerResponse.ErrorCode\n : undefined;\n const headerErrorCode = response.headers.get('X-PM-ApiErrorCode');\n const errorCode =\n responseErrorCode ??\n (headerErrorCode && /^\\d+$/.test(headerErrorCode)\n ? Number(headerErrorCode)\n : undefined);\n const postmarkErrorCode =\n typeof errorCode === 'number' &&\n Number.isSafeInteger(errorCode) &&\n errorCode >= 0\n ? errorCode\n : undefined;\n\n if (!response.ok) {\n const inactiveRecipient =\n response.status === 422 && postmarkErrorCode === 406;\n const details: EmailDeliveryFailureDetails = {\n provider: 'postmark',\n providerCategory: inactiveRecipient ? 'inactiveRecipient' : 'rejected',\n httpStatus: response.status,\n ...(postmarkErrorCode !== undefined ? { postmarkErrorCode } : {}),\n };\n\n if (inactiveRecipient) {\n throw new EmailDeliveryError(\n 'Postmark recipient is inactive',\n details,\n true\n );\n }\n\n throw new EmailDeliveryError('Postmark rejected the email', details);\n }\n\n return { status: response.status };\n}\n\nexport async function settleEmailDeliveries(deliveries: Promise<unknown>[]) {\n const failures = (await Promise.allSettled(deliveries)).filter(\n (result) => result.status === 'rejected'\n );\n if (failures.length === 0) return;\n\n const unexpectedDeliveryError = failures.find(\n (failure) =>\n failure.reason instanceof EmailDeliveryError && !failure.reason.expected\n )?.reason;\n const allExpected = failures.every(\n (failure) =>\n failure.reason instanceof EmailDeliveryError && failure.reason.expected\n );\n const firstDeliveryError = failures.find(\n (failure) => failure.reason instanceof EmailDeliveryError\n )?.reason;\n const failureDetails = failures.flatMap((failure) => {\n if (\n !(failure.reason instanceof EmailDeliveryError) ||\n !failure.reason.details ||\n !('provider' in failure.reason.details)\n ) {\n return [];\n }\n const { httpStatus, postmarkErrorCode, provider, providerCategory } =\n failure.reason.details;\n return [\n {\n httpStatus,\n provider,\n providerCategory,\n ...(postmarkErrorCode !== undefined ? { postmarkErrorCode } : {}),\n },\n ];\n });\n\n const details: EmailDeliveryErrorDetails = {\n failureCount: failures.length,\n failures: failureDetails,\n };\n if (unexpectedDeliveryError instanceof EmailDeliveryError) {\n if (unexpectedDeliveryError.message === 'Postmark is not configured') {\n throw new EmailDeliveryError('Postmark is not configured', details);\n }\n throw new EmailDeliveryError('Postmark rejected the email', details);\n }\n if (allExpected && firstDeliveryError instanceof EmailDeliveryError) {\n throw new EmailDeliveryError(\n 'Postmark recipient is inactive',\n details,\n true\n );\n }\n throw new EmailDeliveryError('Email delivery failed', details);\n}\n"]}
|
package/package.json
CHANGED
package/src/utils/send-email.ts
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
|
+
type EmailDeliveryFailureDetails = {
|
|
2
|
+
httpStatus: number;
|
|
3
|
+
postmarkErrorCode?: number;
|
|
4
|
+
provider: 'postmark';
|
|
5
|
+
providerCategory: 'inactiveRecipient' | 'rejected';
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
type EmailDeliveryErrorDetails =
|
|
9
|
+
| EmailDeliveryFailureDetails
|
|
10
|
+
| {
|
|
11
|
+
failureCount: number;
|
|
12
|
+
failures: EmailDeliveryFailureDetails[];
|
|
13
|
+
};
|
|
14
|
+
|
|
1
15
|
export class EmailDeliveryError extends Error {
|
|
2
16
|
constructor(
|
|
3
17
|
message: string,
|
|
4
|
-
public readonly details?:
|
|
18
|
+
public readonly details?: EmailDeliveryErrorDetails,
|
|
5
19
|
public readonly expected = false
|
|
6
20
|
) {
|
|
7
21
|
super(message);
|
|
@@ -51,36 +65,103 @@ export async function sendEmail(
|
|
|
51
65
|
);
|
|
52
66
|
|
|
53
67
|
const providerResponse: unknown = await response.json().catch(() => ({}));
|
|
54
|
-
const
|
|
68
|
+
const responseErrorCode =
|
|
55
69
|
providerResponse !== null &&
|
|
56
70
|
typeof providerResponse === 'object' &&
|
|
57
|
-
'
|
|
58
|
-
typeof providerResponse.
|
|
59
|
-
? providerResponse.
|
|
71
|
+
'ErrorCode' in providerResponse &&
|
|
72
|
+
typeof providerResponse.ErrorCode === 'number'
|
|
73
|
+
? providerResponse.ErrorCode
|
|
74
|
+
: undefined;
|
|
75
|
+
const headerErrorCode = response.headers.get('X-PM-ApiErrorCode');
|
|
76
|
+
const errorCode =
|
|
77
|
+
responseErrorCode ??
|
|
78
|
+
(headerErrorCode && /^\d+$/.test(headerErrorCode)
|
|
79
|
+
? Number(headerErrorCode)
|
|
80
|
+
: undefined);
|
|
81
|
+
const postmarkErrorCode =
|
|
82
|
+
typeof errorCode === 'number' &&
|
|
83
|
+
Number.isSafeInteger(errorCode) &&
|
|
84
|
+
errorCode >= 0
|
|
85
|
+
? errorCode
|
|
60
86
|
: undefined;
|
|
61
87
|
|
|
62
88
|
if (!response.ok) {
|
|
63
|
-
|
|
64
|
-
response.status === 422 &&
|
|
65
|
-
|
|
66
|
-
|
|
89
|
+
const inactiveRecipient =
|
|
90
|
+
response.status === 422 && postmarkErrorCode === 406;
|
|
91
|
+
const details: EmailDeliveryFailureDetails = {
|
|
92
|
+
provider: 'postmark',
|
|
93
|
+
providerCategory: inactiveRecipient ? 'inactiveRecipient' : 'rejected',
|
|
94
|
+
httpStatus: response.status,
|
|
95
|
+
...(postmarkErrorCode !== undefined ? { postmarkErrorCode } : {}),
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
if (inactiveRecipient) {
|
|
67
99
|
throw new EmailDeliveryError(
|
|
68
100
|
'Postmark recipient is inactive',
|
|
69
|
-
|
|
70
|
-
providerMessage,
|
|
71
|
-
status: response.status,
|
|
72
|
-
statusText: response.statusText,
|
|
73
|
-
},
|
|
101
|
+
details,
|
|
74
102
|
true
|
|
75
103
|
);
|
|
76
104
|
}
|
|
77
105
|
|
|
78
|
-
throw new EmailDeliveryError('Postmark rejected the email',
|
|
79
|
-
providerResponse,
|
|
80
|
-
status: response.status,
|
|
81
|
-
statusText: response.statusText,
|
|
82
|
-
});
|
|
106
|
+
throw new EmailDeliveryError('Postmark rejected the email', details);
|
|
83
107
|
}
|
|
84
108
|
|
|
85
109
|
return { status: response.status };
|
|
86
110
|
}
|
|
111
|
+
|
|
112
|
+
export async function settleEmailDeliveries(deliveries: Promise<unknown>[]) {
|
|
113
|
+
const failures = (await Promise.allSettled(deliveries)).filter(
|
|
114
|
+
(result) => result.status === 'rejected'
|
|
115
|
+
);
|
|
116
|
+
if (failures.length === 0) return;
|
|
117
|
+
|
|
118
|
+
const unexpectedDeliveryError = failures.find(
|
|
119
|
+
(failure) =>
|
|
120
|
+
failure.reason instanceof EmailDeliveryError && !failure.reason.expected
|
|
121
|
+
)?.reason;
|
|
122
|
+
const allExpected = failures.every(
|
|
123
|
+
(failure) =>
|
|
124
|
+
failure.reason instanceof EmailDeliveryError && failure.reason.expected
|
|
125
|
+
);
|
|
126
|
+
const firstDeliveryError = failures.find(
|
|
127
|
+
(failure) => failure.reason instanceof EmailDeliveryError
|
|
128
|
+
)?.reason;
|
|
129
|
+
const failureDetails = failures.flatMap((failure) => {
|
|
130
|
+
if (
|
|
131
|
+
!(failure.reason instanceof EmailDeliveryError) ||
|
|
132
|
+
!failure.reason.details ||
|
|
133
|
+
!('provider' in failure.reason.details)
|
|
134
|
+
) {
|
|
135
|
+
return [];
|
|
136
|
+
}
|
|
137
|
+
const { httpStatus, postmarkErrorCode, provider, providerCategory } =
|
|
138
|
+
failure.reason.details;
|
|
139
|
+
return [
|
|
140
|
+
{
|
|
141
|
+
httpStatus,
|
|
142
|
+
provider,
|
|
143
|
+
providerCategory,
|
|
144
|
+
...(postmarkErrorCode !== undefined ? { postmarkErrorCode } : {}),
|
|
145
|
+
},
|
|
146
|
+
];
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const details: EmailDeliveryErrorDetails = {
|
|
150
|
+
failureCount: failures.length,
|
|
151
|
+
failures: failureDetails,
|
|
152
|
+
};
|
|
153
|
+
if (unexpectedDeliveryError instanceof EmailDeliveryError) {
|
|
154
|
+
if (unexpectedDeliveryError.message === 'Postmark is not configured') {
|
|
155
|
+
throw new EmailDeliveryError('Postmark is not configured', details);
|
|
156
|
+
}
|
|
157
|
+
throw new EmailDeliveryError('Postmark rejected the email', details);
|
|
158
|
+
}
|
|
159
|
+
if (allExpected && firstDeliveryError instanceof EmailDeliveryError) {
|
|
160
|
+
throw new EmailDeliveryError(
|
|
161
|
+
'Postmark recipient is inactive',
|
|
162
|
+
details,
|
|
163
|
+
true
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
throw new EmailDeliveryError('Email delivery failed', details);
|
|
167
|
+
}
|