@schemavaults/send-email 0.3.0 → 0.4.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 +94 -1
- package/bun.lock +4 -4
- package/dist/cli.js +14377 -3777
- package/dist/cli.js.map +1 -1
- package/dist/create-email-attachment-from-file.d.ts +9 -0
- package/dist/create-email-attachment-from-file.js +27 -0
- package/dist/create-email-attachment-from-file.js.map +1 -0
- package/dist/create-email-attachment.d.ts +43 -0
- package/dist/create-email-attachment.js +58 -0
- package/dist/create-email-attachment.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/send-email-request-body-schema.d.ts +38 -113
- package/dist/send-email-request-body-schema.js +9 -0
- package/dist/send-email-request-body-schema.js.map +1 -1
- package/dist/send-email.d.ts +9 -0
- package/dist/send-email.js +8 -1
- package/dist/send-email.js.map +1 -1
- package/dist/validators/email-attachment-schema.d.ts +64 -0
- package/dist/validators/email-attachment-schema.js +120 -0
- package/dist/validators/email-attachment-schema.js.map +1 -0
- package/package.json +8 -3
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Maximum number of attachments accepted on a single email.
|
|
4
|
+
*/
|
|
5
|
+
export const MAX_ATTACHMENTS_PER_EMAIL = 20;
|
|
6
|
+
/**
|
|
7
|
+
* Maximum combined size, in decoded bytes, of every attachment on a single
|
|
8
|
+
* email (25 MiB). Most receiving mail systems reject whole messages larger
|
|
9
|
+
* than this, so bigger payloads would never be deliverable regardless of
|
|
10
|
+
* which transport the mail-server uses. The mail-server (and the platform
|
|
11
|
+
* it is hosted on) may enforce a lower limit of its own.
|
|
12
|
+
*/
|
|
13
|
+
export const MAX_TOTAL_ATTACHMENT_BYTES = 25 * 1024 * 1024;
|
|
14
|
+
/**
|
|
15
|
+
* Number of bytes a (padded, whitespace-free) base64 string decodes to,
|
|
16
|
+
* computed without actually decoding it.
|
|
17
|
+
*/
|
|
18
|
+
export function base64DecodedByteLength(base64) {
|
|
19
|
+
if (base64.length === 0)
|
|
20
|
+
return 0;
|
|
21
|
+
let padding = 0;
|
|
22
|
+
if (base64.endsWith("=="))
|
|
23
|
+
padding = 2;
|
|
24
|
+
else if (base64.endsWith("="))
|
|
25
|
+
padding = 1;
|
|
26
|
+
return Math.floor((base64.length * 3) / 4) - padding;
|
|
27
|
+
}
|
|
28
|
+
function hasControlCharacters(value) {
|
|
29
|
+
for (let i = 0; i < value.length; i++) {
|
|
30
|
+
const code = value.charCodeAt(i);
|
|
31
|
+
if (code < 0x20 || code === 0x7f)
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Name the recipient's mail client shows for the attached file. Unicode is
|
|
38
|
+
* allowed; path separators and control characters are not.
|
|
39
|
+
*/
|
|
40
|
+
export const attachmentFilenameSchema = z
|
|
41
|
+
.string()
|
|
42
|
+
.min(1, "Attachment filename must be non-empty!")
|
|
43
|
+
.max(255, "Attachment filename must be at most 255 characters!")
|
|
44
|
+
.refine((name) => name !== "." && name !== "..", {
|
|
45
|
+
message: "Attachment filename must not be '.' or '..'!",
|
|
46
|
+
})
|
|
47
|
+
.refine((name) => !name.includes("/") && !name.includes("\\"), {
|
|
48
|
+
message: "Attachment filename must not contain path separators!",
|
|
49
|
+
})
|
|
50
|
+
.refine((name) => !hasControlCharacters(name), {
|
|
51
|
+
message: "Attachment filename must not contain control characters!",
|
|
52
|
+
});
|
|
53
|
+
/**
|
|
54
|
+
* The attached file's bytes, base64-encoded (standard alphabet, padded, no
|
|
55
|
+
* line breaks). Request bodies are JSON, so binary content has to travel
|
|
56
|
+
* as text.
|
|
57
|
+
*/
|
|
58
|
+
export const attachmentContentSchema = z
|
|
59
|
+
.base64("Attachment content must be base64-encoded!")
|
|
60
|
+
.min(1, "Attachment content must not be empty!");
|
|
61
|
+
const MIME_TOKEN = "[A-Za-z0-9!#$&^_.+-]+";
|
|
62
|
+
/**
|
|
63
|
+
* MIME type of the attachment, e.g. "application/pdf" or
|
|
64
|
+
* "text/csv; charset=utf-8". When omitted, the mail-server's transport
|
|
65
|
+
* derives it from the filename's extension.
|
|
66
|
+
*/
|
|
67
|
+
export const attachmentContentTypeSchema = z
|
|
68
|
+
.string()
|
|
69
|
+
.min(1, "Attachment content type must be non-empty!")
|
|
70
|
+
.max(255, "Attachment content type must be at most 255 characters!")
|
|
71
|
+
.regex(new RegExp(`^${MIME_TOKEN}\\/${MIME_TOKEN}(?:\\s*;\\s*${MIME_TOKEN}=(?:"[^"]*"|[^\\s;"]+))*$`), "Attachment content type must be a MIME type such as 'application/pdf'!");
|
|
72
|
+
/**
|
|
73
|
+
* Content-ID for inline attachments. Setting it marks the attachment as
|
|
74
|
+
* inline so the HTML body can reference it as `<img src="cid:<contentId>">`.
|
|
75
|
+
* Pass the bare identifier -- without the angle brackets.
|
|
76
|
+
*/
|
|
77
|
+
export const attachmentContentIdSchema = z
|
|
78
|
+
.string()
|
|
79
|
+
.min(1, "Attachment content ID must be non-empty!")
|
|
80
|
+
.max(255, "Attachment content ID must be at most 255 characters!")
|
|
81
|
+
.regex(/^[^\s<>]+$/, "Attachment content ID must not contain whitespace or angle brackets!");
|
|
82
|
+
/**
|
|
83
|
+
* A single file attached to an email, in the shape accepted by the
|
|
84
|
+
* `@schemavaults/mail-server` `/api/send` route.
|
|
85
|
+
*/
|
|
86
|
+
export const emailAttachmentSchema = z
|
|
87
|
+
.object({
|
|
88
|
+
filename: attachmentFilenameSchema,
|
|
89
|
+
content: attachmentContentSchema,
|
|
90
|
+
contentType: attachmentContentTypeSchema.optional(),
|
|
91
|
+
contentId: attachmentContentIdSchema.optional(),
|
|
92
|
+
})
|
|
93
|
+
.required({
|
|
94
|
+
filename: true,
|
|
95
|
+
content: true,
|
|
96
|
+
})
|
|
97
|
+
.strict();
|
|
98
|
+
/**
|
|
99
|
+
* The full list of attachments on one email: between 1 and
|
|
100
|
+
* {@link MAX_ATTACHMENTS_PER_EMAIL} files whose decoded sizes add up to at
|
|
101
|
+
* most {@link MAX_TOTAL_ATTACHMENT_BYTES}.
|
|
102
|
+
*/
|
|
103
|
+
export const emailAttachmentsSchema = z
|
|
104
|
+
.array(emailAttachmentSchema)
|
|
105
|
+
.min(1, "Provide at least one attachment, or omit 'attachments' entirely!")
|
|
106
|
+
.max(MAX_ATTACHMENTS_PER_EMAIL, `An email may have at most ${MAX_ATTACHMENTS_PER_EMAIL} attachments!`)
|
|
107
|
+
.superRefine((attachments, ctx) => {
|
|
108
|
+
let totalBytes = 0;
|
|
109
|
+
for (const attachment of attachments) {
|
|
110
|
+
totalBytes += base64DecodedByteLength(attachment.content);
|
|
111
|
+
}
|
|
112
|
+
if (totalBytes > MAX_TOTAL_ATTACHMENT_BYTES) {
|
|
113
|
+
ctx.addIssue({
|
|
114
|
+
code: "custom",
|
|
115
|
+
message: `Attachments must total at most ${MAX_TOTAL_ATTACHMENT_BYTES} bytes (${MAX_TOTAL_ATTACHMENT_BYTES / (1024 * 1024)} MiB) once decoded; received ${totalBytes} bytes.`,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
export default emailAttachmentSchema;
|
|
120
|
+
//# sourceMappingURL=email-attachment-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-attachment-schema.js","sourceRoot":"","sources":["../../src/validators/email-attachment-schema.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@schemavaults/send-email",
|
|
3
3
|
"description": "Schema defining the shape of the request body to send an email via @schemavaults/mail-server",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.0",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"private": false,
|
|
7
7
|
"repository": {
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"schemavaults-send-email": "dist/cli.js"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"zod": "
|
|
20
|
-
"commander": "
|
|
19
|
+
"zod": "4.4.3",
|
|
20
|
+
"commander": "14.0.1"
|
|
21
21
|
},
|
|
22
22
|
"scripts": {
|
|
23
23
|
"build:lib": "tsc --project tsconfig.json && tsc-alias --project tsconfig.json",
|
|
@@ -51,6 +51,11 @@
|
|
|
51
51
|
"import": "./dist/index.js",
|
|
52
52
|
"require": "./dist/index.js"
|
|
53
53
|
},
|
|
54
|
+
"./create-email-attachment-from-file": {
|
|
55
|
+
"types": "./dist/create-email-attachment-from-file.d.ts",
|
|
56
|
+
"import": "./dist/create-email-attachment-from-file.js",
|
|
57
|
+
"require": "./dist/create-email-attachment-from-file.js"
|
|
58
|
+
},
|
|
54
59
|
"./*": {
|
|
55
60
|
"types": "./dist/*",
|
|
56
61
|
"import": "./dist/*",
|