@schemavaults/send-email 0.3.1 → 0.4.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @schemavaults/send-email
2
2
 
3
- TypeScript client for sending emails via the `@schemavaults/mail-server` API. Provides request body validation (via Zod), environment-aware server URL resolution, and helpers for sending to individual recipients, mailing lists, and listing available email templates.
3
+ TypeScript client for sending emails via the `@schemavaults/mail-server` API. Provides request body validation (via Zod), environment-aware server URL resolution, and helpers for sending to individual recipients, mailing lists, attaching files, and listing available email templates.
4
4
 
5
5
  ## Installation
6
6
 
@@ -81,6 +81,59 @@ bunx schemavaults-send-email --transport smtp-primary send \
81
81
 
82
82
  Transport names are defined by whoever configures the mail-server; this package only validates that the value looks like an identifier. An unknown transport name is rejected by the mail-server, not by the client.
83
83
 
84
+ ### Attach files
85
+
86
+ Request bodies are JSON, so each attachment's bytes travel as a base64 string under `body.attachments`. You rarely need to encode anything yourself: the `attachments` option on `sendEmail()` (and `sendEmailToMailingList()`) accepts raw bytes, plain text, or already-encoded attachments and base64-encodes them for you before appending them to `body.attachments`.
87
+
88
+ ```ts
89
+ import { sendEmail } from "@schemavaults/send-email";
90
+ // Node/Bun only -- reads from disk, so it lives on its own subpath
91
+ import { createEmailAttachmentFromFile } from "@schemavaults/send-email/create-email-attachment-from-file";
92
+
93
+ await sendEmail({
94
+ body: {
95
+ to: "user@example.com",
96
+ subject: "Your invoice",
97
+ message: { text: "Invoice attached.", html: "<p>Invoice attached.</p>" },
98
+ },
99
+ attachments: [
100
+ // Binary: any Uint8Array / Buffer / ArrayBuffer
101
+ { filename: "invoice.pdf", content: pdfBytes, contentType: "application/pdf" },
102
+ // Plain text: say so, otherwise a string is assumed to be base64 already
103
+ { filename: "summary.txt", content: "Thanks for your order!", encoding: "utf8" },
104
+ // A file on disk; the basename becomes the attachment filename
105
+ await createEmailAttachmentFromFile("./reports/q3.csv"),
106
+ ],
107
+ });
108
+ ```
109
+
110
+ To build the wire shape yourself -- for example to validate it up front or to put it in `body.attachments` directly -- use `createEmailAttachment()`, which takes the same input and returns a validated `EmailAttachment`:
111
+
112
+ ```ts
113
+ import { createEmailAttachment } from "@schemavaults/send-email";
114
+
115
+ const attachment = createEmailAttachment({
116
+ filename: "logo.png",
117
+ content: logoBytes,
118
+ contentType: "image/png",
119
+ contentId: "logo", // inline image: reference it as <img src="cid:logo"> in the HTML body
120
+ });
121
+ // => { filename: "logo.png", content: "iVBORw0KGgo...", contentType: "image/png", contentId: "logo" }
122
+ ```
123
+
124
+ Setting `contentId` marks the attachment as inline so the HTML body can embed it via a `cid:` URL; pass the bare identifier without angle brackets, in printable ASCII. `contentType` is optional -- when omitted, the mail-server's transport derives it from the filename's extension. Non-ASCII names belong in `filename`, not in a `contentType` parameter.
125
+
126
+ Limits enforced client-side (and exported as `MAX_ATTACHMENTS_PER_EMAIL` / `MAX_TOTAL_ATTACHMENT_BYTES`): at most 20 attachments per email, totalling at most 25 MiB once decoded. Filenames may not contain path separators, control characters, or bidirectional formatting characters (which can disguise `invoice\u202Efdp.exe` as `invoiceexe.pdf`). `contentType` and `contentId` must be printable ASCII, and `subject` must not contain line breaks or other control characters, so no value can break out of its MIME header. The mail-server, its hosting platform, and the receiving mail systems may enforce lower limits of their own.
127
+
128
+ From the CLI, `--attach <path>` is repeatable on both `send` and `send-to-mailing-list`, and may be combined with `--body-file` (the files are appended to any `attachments` already in the JSON):
129
+
130
+ ```bash
131
+ bunx schemavaults-send-email send \
132
+ --to user@example.com --subject "Your invoice" \
133
+ --text "Invoice attached." --html "<p>Invoice attached.</p>" \
134
+ --attach ./invoice.pdf --attach ./reports/q3.csv
135
+ ```
136
+
84
137
  ### Send to a mailing list
85
138
 
86
139
  ```ts
@@ -141,6 +194,7 @@ interface ISendEmailOpts {
141
194
  environment?: "production" | "development" | "staging";
142
195
  dryRun?: boolean; // convenience; sets body.dryRun
143
196
  transport?: string; // convenience; sets body.transport
197
+ attachments?: EmailAttachmentInput[]; // convenience; encoded + appended to body.attachments
144
198
  }
145
199
  ```
146
200
 
@@ -157,9 +211,30 @@ interface ISendEmailToMailingListOpts {
157
211
  environment?: "production" | "development" | "staging";
158
212
  dryRun?: boolean; // convenience; sets body.dryRun
159
213
  transport?: string; // convenience; sets body.transport
214
+ attachments?: EmailAttachmentInput[]; // convenience; encoded + appended to body.attachments
215
+ }
216
+ ```
217
+
218
+ ### `createEmailAttachment(input)`
219
+
220
+ Builds a validated `EmailAttachment` (the wire shape used in `body.attachments`), base64-encoding the content for you. Throws a `TypeError` describing the problem when the result is invalid.
221
+
222
+ ```ts
223
+ interface EmailAttachmentInput {
224
+ filename: string; // shown to the recipient; no path separators
225
+ content: string | Uint8Array | ArrayBuffer; // bytes, or a string (see `encoding`)
226
+ encoding?: "base64" | "utf8"; // how to read a string `content`; default "base64"
227
+ contentType?: string; // MIME type; derived from the filename when omitted
228
+ contentId?: string; // set to embed inline via cid: in the HTML body
160
229
  }
161
230
  ```
162
231
 
232
+ An `EmailAttachment` is itself valid `EmailAttachmentInput` (its `content` is base64), so pre-built attachments and raw inputs can be mixed freely.
233
+
234
+ ### `createEmailAttachmentFromFile(path, opts?)`
235
+
236
+ Node/Bun only. Reads a file from disk and returns a validated `EmailAttachment`; the file's basename is the attachment filename unless `opts.filename` overrides it. `opts` may also set `contentType` and `contentId`. Imported from the `@schemavaults/send-email/create-email-attachment-from-file` subpath so the package's main entry stays free of filesystem imports.
237
+
163
238
  ### `listEmailTemplates(opts?)`
164
239
 
165
240
  Returns the list of registered email templates from the mail-server catalog.
@@ -197,6 +272,10 @@ Zod schema for validating template IDs: lowercase alphanumeric with hyphens/unde
197
272
 
198
273
  Zod schema for validating transport names: lowercase alphanumeric with hyphens/underscores, 1-64 characters, must start with a lowercase letter and end with a lowercase alphanumeric character. Whether a well-formed name corresponds to a transport the mail-server actually has configured is decided server-side.
199
274
 
275
+ ### `emailAttachmentSchema` / `emailAttachmentsSchema`
276
+
277
+ Zod schemas for a single attachment and for the `attachments` array respectively. The array schema enforces the count (`MAX_ATTACHMENTS_PER_EMAIL`, 20) and total decoded size (`MAX_TOTAL_ATTACHMENT_BYTES`, 25 MiB) limits; both constants are exported.
278
+
200
279
  ## Request body shape
201
280
 
202
281
  The `message` field accepts either a template reference or raw content:
@@ -206,9 +285,16 @@ type Message =
206
285
  | { template_id: string; template_props?: unknown }
207
286
  | { text: string; html: string }; // both required
208
287
 
288
+ type EmailAttachment = {
289
+ filename: string; // 1-255 chars; no path separators, control or bidi formatting characters
290
+ content: string; // base64-encoded bytes (standard alphabet, padded)
291
+ contentType?: string; // MIME type, printable ASCII; derived from the filename when omitted
292
+ contentId?: string; // printable ASCII; marks the attachment inline; reference as cid:<contentId>
293
+ };
294
+
209
295
  type SendEmailRequestBody = {
210
296
  to: string | string[]; // email address(es) or mailing list UUID
211
- subject: string;
297
+ subject: string; // no line breaks or control characters (tabs allowed)
212
298
  message: Message;
213
299
  from?: string; // defaults to mail-server's configured sender
214
300
  replyTo?: string;
@@ -216,6 +302,7 @@ type SendEmailRequestBody = {
216
302
  bcc?: string | string[];
217
303
  dryRun?: boolean; // server validates without dispatching
218
304
  transport?: string; // which mail-server transport delivers this email
305
+ attachments?: EmailAttachment[]; // 1-20 files, at most 25 MiB in total once decoded
219
306
  };
220
307
  ```
221
308
 
@@ -228,3 +315,9 @@ bun test
228
315
  bun run typecheck
229
316
  bun run lint
230
317
  ```
318
+
319
+ ### Keeping the package root browser-safe
320
+
321
+ Consumers bundle the package root for the browser (the mail-server's admin UI imports `sendEmail` from a client component), so nothing reachable from `src/index.ts` may need a Node/Bun runtime. `src/browser-compatibility.test.ts` enforces this on every `bun test`: it bundles the root with esbuild for the browser platform, asserts no Node built-in is imported anywhere in the graph, and evaluates the bundle in a `node:vm` context with no `process` / `Buffer` / `require` before exercising the public API.
322
+
323
+ Anything that does need Node or Bun -- filesystem access, for example -- goes in its own module exposed through a package subpath (as `create-email-attachment-from-file` is) and is added to that test's `NODE_ONLY_MODULES` list, which both checks the root never imports it and confirms it really is not browser-safe.
package/dist/cli.js CHANGED
@@ -18008,6 +18008,91 @@ var transportIdSchema = external_exports.string().min(1, "Transport ID must be n
18008
18008
  "Transport ID must start with a lowercase letter and end with a lowercase alphanumeric character, and may only contain lowercase alphanumeric characters, hyphens and underscores."
18009
18009
  );
18010
18010
 
18011
+ // src/validators/email-attachment-schema.ts
18012
+ var MAX_ATTACHMENTS_PER_EMAIL = 20;
18013
+ var MAX_TOTAL_ATTACHMENT_BYTES = 25 * 1024 * 1024;
18014
+ function base64DecodedByteLength(base643) {
18015
+ if (base643.length === 0) return 0;
18016
+ let padding = 0;
18017
+ if (base643.endsWith("==")) padding = 2;
18018
+ else if (base643.endsWith("=")) padding = 1;
18019
+ return Math.floor(base643.length * 3 / 4) - padding;
18020
+ }
18021
+ function hasControlCharacters(value) {
18022
+ for (let i = 0; i < value.length; i++) {
18023
+ const code = value.charCodeAt(i);
18024
+ if (code < 32 || code >= 127 && code <= 159) return true;
18025
+ }
18026
+ return false;
18027
+ }
18028
+ var BIDI_FORMATTING = /* @__PURE__ */ new Set([
18029
+ 1564,
18030
+ 8206,
18031
+ 8207,
18032
+ 8234,
18033
+ 8235,
18034
+ 8236,
18035
+ 8237,
18036
+ 8238,
18037
+ 8294,
18038
+ 8295,
18039
+ 8296,
18040
+ 8297
18041
+ ]);
18042
+ function hasBidiFormatting(value) {
18043
+ for (let i = 0; i < value.length; i++) {
18044
+ if (BIDI_FORMATTING.has(value.charCodeAt(i))) return true;
18045
+ }
18046
+ return false;
18047
+ }
18048
+ var attachmentFilenameSchema = external_exports.string().min(1, "Attachment filename must be non-empty!").max(255, "Attachment filename must be at most 255 characters!").refine((name) => name !== "." && name !== "..", {
18049
+ message: "Attachment filename must not be '.' or '..'!"
18050
+ }).refine((name) => !name.includes("/") && !name.includes("\\"), {
18051
+ message: "Attachment filename must not contain path separators!"
18052
+ }).refine((name) => !hasControlCharacters(name), {
18053
+ message: "Attachment filename must not contain control characters!"
18054
+ }).refine((name) => !hasBidiFormatting(name), {
18055
+ message: "Attachment filename must not contain bidirectional formatting characters!"
18056
+ });
18057
+ var attachmentContentSchema = external_exports.base64("Attachment content must be base64-encoded!").min(1, "Attachment content must not be empty!");
18058
+ var MIME_TOKEN = "[A-Za-z0-9!#$&^_.+-]+";
18059
+ var MIME_PARAM_VALUE = '(?:"[\\x20\\x21\\x23-\\x7E]*"|[\\x21\\x23-\\x3A\\x3C-\\x7E]+)';
18060
+ var attachmentContentTypeSchema = external_exports.string().min(1, "Attachment content type must be non-empty!").max(255, "Attachment content type must be at most 255 characters!").regex(
18061
+ // `[ \t]*`, not `\s*`: `\s` matches CR/LF.
18062
+ new RegExp(
18063
+ `^${MIME_TOKEN}\\/${MIME_TOKEN}(?:[ \\t]*;[ \\t]*${MIME_TOKEN}=${MIME_PARAM_VALUE})*$`
18064
+ ),
18065
+ "Attachment content type must be a MIME type such as 'application/pdf'!"
18066
+ );
18067
+ var attachmentContentIdSchema = external_exports.string().min(1, "Attachment content ID must be non-empty!").max(255, "Attachment content ID must be at most 255 characters!").regex(
18068
+ /^[\x21-\x3B\x3D\x3F-\x7E]+$/,
18069
+ "Attachment content ID must be printable ASCII without spaces or angle brackets!"
18070
+ );
18071
+ var emailAttachmentSchema = external_exports.object({
18072
+ filename: attachmentFilenameSchema,
18073
+ content: attachmentContentSchema,
18074
+ contentType: attachmentContentTypeSchema.optional(),
18075
+ contentId: attachmentContentIdSchema.optional()
18076
+ }).required({
18077
+ filename: true,
18078
+ content: true
18079
+ }).strict();
18080
+ var emailAttachmentsSchema = external_exports.array(emailAttachmentSchema).min(1, "Provide at least one attachment, or omit 'attachments' entirely!").max(
18081
+ MAX_ATTACHMENTS_PER_EMAIL,
18082
+ `An email may have at most ${MAX_ATTACHMENTS_PER_EMAIL} attachments!`
18083
+ ).superRefine((attachments, ctx) => {
18084
+ let totalBytes = 0;
18085
+ for (const attachment of attachments) {
18086
+ totalBytes += base64DecodedByteLength(attachment.content);
18087
+ }
18088
+ if (totalBytes > MAX_TOTAL_ATTACHMENT_BYTES) {
18089
+ ctx.addIssue({
18090
+ code: "custom",
18091
+ message: `Attachments must total at most ${MAX_TOTAL_ATTACHMENT_BYTES} bytes (${MAX_TOTAL_ATTACHMENT_BYTES / (1024 * 1024)} MiB) once decoded; received ${totalBytes} bytes.`
18092
+ });
18093
+ }
18094
+ });
18095
+
18011
18096
  // src/send-email-request-body-schema.ts
18012
18097
  var sendEmailTemplateOptions = external_exports.object({
18013
18098
  template_id: emailTemplateIdSchema,
@@ -18023,6 +18108,13 @@ var sendRawEmailOptions = external_exports.object({
18023
18108
  html: true
18024
18109
  }).strict();
18025
18110
  var MAX_RECIPIENTS = 50;
18111
+ function hasHeaderUnsafeCharacters(value) {
18112
+ for (let i = 0; i < value.length; i++) {
18113
+ const code = value.charCodeAt(i);
18114
+ if (code < 32 && code !== 9 || code === 127) return true;
18115
+ }
18116
+ return false;
18117
+ }
18026
18118
  function createRecipientSchema(allow_mailing_list_ids_as_recipients = false) {
18027
18119
  if (allow_mailing_list_ids_as_recipients) {
18028
18120
  return external_exports.union([
@@ -18046,7 +18138,9 @@ function createSendEmailRequestBodySchema(allow_mailing_list_ids_as_recipients =
18046
18138
  return external_exports.object({
18047
18139
  to: createRecipientSchema(allow_mailing_list_ids_as_recipients),
18048
18140
  from: external_exports.string().email().optional(),
18049
- subject: external_exports.string().nonempty(),
18141
+ subject: external_exports.string().nonempty().refine((subject) => !hasHeaderUnsafeCharacters(subject), {
18142
+ message: "Subject must not contain line breaks or control characters!"
18143
+ }),
18050
18144
  message: external_exports.union([sendEmailTemplateOptions, sendRawEmailOptions]),
18051
18145
  replyTo: external_exports.string().email().optional(),
18052
18146
  cc: external_exports.union([
@@ -18064,7 +18158,15 @@ function createSendEmailRequestBodySchema(allow_mailing_list_ids_as_recipients =
18064
18158
  * instance has more than one transport configured; when omitted, the
18065
18159
  * mail-server picks its own default transport.
18066
18160
  */
18067
- transport: transportIdSchema.optional()
18161
+ transport: transportIdSchema.optional(),
18162
+ /**
18163
+ * Files to attach to the email. Each attachment carries its bytes as a
18164
+ * base64 string (request bodies are JSON); see `createEmailAttachment`
18165
+ * for encoding binary or plain-text content. Between 1 and
18166
+ * `MAX_ATTACHMENTS_PER_EMAIL` files, totalling at most
18167
+ * `MAX_TOTAL_ATTACHMENT_BYTES` once decoded. Omit when there are none.
18168
+ */
18169
+ attachments: emailAttachmentsSchema.optional()
18068
18170
  }).required({
18069
18171
  to: true,
18070
18172
  message: true,
@@ -18073,6 +18175,48 @@ function createSendEmailRequestBodySchema(allow_mailing_list_ids_as_recipients =
18073
18175
  }
18074
18176
  var sendEmailRequestBodySchema = createSendEmailRequestBodySchema();
18075
18177
 
18178
+ // src/create-email-attachment.ts
18179
+ function encodeBytesAsBase64(bytes) {
18180
+ if (typeof Buffer !== "undefined" && typeof Buffer.from === "function") {
18181
+ return Buffer.from(bytes).toString("base64");
18182
+ }
18183
+ let binary = "";
18184
+ const CHUNK_SIZE = 32768;
18185
+ for (let i = 0; i < bytes.length; i += CHUNK_SIZE) {
18186
+ binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK_SIZE));
18187
+ }
18188
+ return btoa(binary);
18189
+ }
18190
+ function createEmailAttachment(input) {
18191
+ let content;
18192
+ if (typeof input.content === "string") {
18193
+ content = input.encoding === "utf8" ? encodeBytesAsBase64(new TextEncoder().encode(input.content)) : input.content;
18194
+ } else if (input.content instanceof Uint8Array) {
18195
+ content = encodeBytesAsBase64(input.content);
18196
+ } else if (input.content instanceof ArrayBuffer) {
18197
+ content = encodeBytesAsBase64(new Uint8Array(input.content));
18198
+ } else {
18199
+ throw new TypeError(
18200
+ "Expected attachment 'content' to be a string, Uint8Array or ArrayBuffer!",
18201
+ { cause: `Received type '${typeof input.content}'` }
18202
+ );
18203
+ }
18204
+ const candidate = {
18205
+ filename: input.filename,
18206
+ content,
18207
+ ...input.contentType !== void 0 ? { contentType: input.contentType } : {},
18208
+ ...input.contentId !== void 0 ? { contentId: input.contentId } : {}
18209
+ };
18210
+ const parsed = emailAttachmentSchema.safeParse(candidate);
18211
+ if (!parsed.success) {
18212
+ const reasons = parsed.error.issues.map((issue2) => issue2.message);
18213
+ throw new TypeError(
18214
+ `Invalid email attachment '${String(input.filename)}': ${reasons.join(" ")}`
18215
+ );
18216
+ }
18217
+ return parsed.data;
18218
+ }
18219
+
18076
18220
  // src/env/get-api-key.ts
18077
18221
  function getSchemaVaultsMailApiKey() {
18078
18222
  if (!process.env.SCHEMAVAULTS_MAIL_API_KEY || typeof process.env.SCHEMAVAULTS_MAIL_API_KEY !== "string") {
@@ -18134,10 +18278,16 @@ async function sendEmail({
18134
18278
  body,
18135
18279
  ...opts
18136
18280
  }) {
18281
+ const { attachments: bodyAttachments, ...bodyWithoutAttachments } = body;
18282
+ const attachments = [
18283
+ ...bodyAttachments ?? [],
18284
+ ...(opts.attachments ?? []).map(createEmailAttachment)
18285
+ ];
18137
18286
  const effectiveBody = {
18138
- ...body,
18287
+ ...bodyWithoutAttachments,
18139
18288
  ...typeof opts.dryRun === "boolean" ? { dryRun: opts.dryRun } : {},
18140
- ...typeof opts.transport === "string" ? { transport: opts.transport } : {}
18289
+ ...typeof opts.transport === "string" ? { transport: opts.transport } : {},
18290
+ ...attachments.length > 0 ? { attachments } : {}
18141
18291
  };
18142
18292
  const parsed = await body_schema.safeParseAsync(effectiveBody);
18143
18293
  if (!parsed.success) {
@@ -18265,6 +18415,20 @@ async function listEmailTemplates(opts = {}) {
18265
18415
  }
18266
18416
  var list_email_templates_default = listEmailTemplates;
18267
18417
 
18418
+ // src/create-email-attachment-from-file.ts
18419
+ import { readFile } from "node:fs/promises";
18420
+ import { basename } from "node:path";
18421
+ async function createEmailAttachmentFromFile(path, opts = {}) {
18422
+ const bytes = await readFile(path);
18423
+ return createEmailAttachment({
18424
+ filename: opts.filename ?? basename(path),
18425
+ content: bytes,
18426
+ ...opts.contentType !== void 0 ? { contentType: opts.contentType } : {},
18427
+ ...opts.contentId !== void 0 ? { contentId: opts.contentId } : {}
18428
+ });
18429
+ }
18430
+ var create_email_attachment_from_file_default = createEmailAttachmentFromFile;
18431
+
18268
18432
  // src/cli.ts
18269
18433
  function readTextFile(path) {
18270
18434
  return readFileSync(path, "utf8");
@@ -18301,6 +18465,12 @@ function loadBodyFile(path) {
18301
18465
  throw new Error(`Failed to parse ${path} as JSON: ${msg}`);
18302
18466
  }
18303
18467
  }
18468
+ async function loadAttachments(paths) {
18469
+ if (!paths || paths.length === 0) return [];
18470
+ return await Promise.all(
18471
+ paths.map((path) => create_email_attachment_from_file_default(path))
18472
+ );
18473
+ }
18304
18474
  async function withErrorHandling(fn) {
18305
18475
  try {
18306
18476
  await fn();
@@ -18320,8 +18490,11 @@ function attachBodyOptions(cmd) {
18320
18490
  "--text-file <path>",
18321
18491
  "Path to a file containing the plain-text body"
18322
18492
  ).option("--html-file <path>", "Path to a file containing the HTML body").option(
18493
+ "--attach <path...>",
18494
+ "Path to a file to attach (repeatable); its basename becomes the attachment filename"
18495
+ ).option(
18323
18496
  "--body-file <path>",
18324
- "Path to a JSON file containing the full request body (overrides other body flags)"
18497
+ "Path to a JSON file containing the full request body (overrides other body flags, except --attach which is appended)"
18325
18498
  );
18326
18499
  }
18327
18500
  var program2 = new Command();
@@ -18365,8 +18538,10 @@ attachBodyOptions(
18365
18538
  ...opts.bcc && opts.bcc.length > 0 ? { bcc: opts.bcc.length === 1 ? opts.bcc[0] : opts.bcc } : {}
18366
18539
  };
18367
18540
  }
18541
+ const attachments = await loadAttachments(opts.attach);
18368
18542
  await send_email_default({
18369
18543
  body,
18544
+ ...attachments.length > 0 ? { attachments } : {},
18370
18545
  ...global.apiKey ? { bearerToken: global.apiKey } : {},
18371
18546
  ...global.dryRun ? { dryRun: true } : {},
18372
18547
  ...global.transport ? { transport: global.transport } : {}
@@ -18392,7 +18567,8 @@ attachBodyOptions(
18392
18567
  message: fullBody.message,
18393
18568
  ...fullBody.from ? { from: fullBody.from } : {},
18394
18569
  ...fullBody.replyTo ? { replyTo: fullBody.replyTo } : {},
18395
- ...fullBody.transport ? { transport: fullBody.transport } : {}
18570
+ ...fullBody.transport ? { transport: fullBody.transport } : {},
18571
+ ...fullBody.attachments ? { attachments: fullBody.attachments } : {}
18396
18572
  };
18397
18573
  } else {
18398
18574
  if (!opts.subject) {
@@ -18405,8 +18581,10 @@ attachBodyOptions(
18405
18581
  ...opts.replyTo ? { replyTo: opts.replyTo } : {}
18406
18582
  };
18407
18583
  }
18584
+ const attachments = await loadAttachments(opts.attach);
18408
18585
  await send_email_to_mailing_list_default({
18409
18586
  body,
18587
+ ...attachments.length > 0 ? { attachments } : {},
18410
18588
  ...opts.mailingListId ? { mailingListId: opts.mailingListId } : {},
18411
18589
  ...global.apiKey ? { bearerToken: global.apiKey } : {},
18412
18590
  ...global.dryRun ? { dryRun: true } : {},
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import { type EmailAttachmentInput } from "./create-email-attachment.js";
2
+ import type { EmailAttachment } from "./validators/email-attachment-schema.js";
3
+ export type ICreateEmailAttachmentFromFileOpts = Partial<Pick<EmailAttachmentInput, "filename" | "contentType" | "contentId">>;
4
+ /**
5
+ * Read a file from disk and turn it into a validated {@link EmailAttachment}.
6
+ * The file's basename is used as the attachment filename unless overridden.
7
+ */
8
+ export declare function createEmailAttachmentFromFile(path: string, opts?: ICreateEmailAttachmentFromFileOpts): Promise<EmailAttachment>;
9
+ export default createEmailAttachmentFromFile;
@@ -0,0 +1,27 @@
1
+ // create-email-attachment-from-file.ts
2
+ //
3
+ // Node/Bun only: reads from the filesystem. Deliberately NOT re-exported from
4
+ // the package's main entry point, which must stay free of `node:` imports so
5
+ // it can be bundled for environments without a filesystem. Import it via the
6
+ // "@schemavaults/send-email/create-email-attachment-from-file" subpath.
7
+ // `src/browser-compatibility.test.ts` enforces this separation.
8
+ import { readFile } from "node:fs/promises";
9
+ import { basename } from "node:path";
10
+ import { createEmailAttachment, } from "./create-email-attachment.js";
11
+ /**
12
+ * Read a file from disk and turn it into a validated {@link EmailAttachment}.
13
+ * The file's basename is used as the attachment filename unless overridden.
14
+ */
15
+ export async function createEmailAttachmentFromFile(path, opts = {}) {
16
+ const bytes = await readFile(path);
17
+ return createEmailAttachment({
18
+ filename: opts.filename ?? basename(path),
19
+ content: bytes,
20
+ ...(opts.contentType !== undefined
21
+ ? { contentType: opts.contentType }
22
+ : {}),
23
+ ...(opts.contentId !== undefined ? { contentId: opts.contentId } : {}),
24
+ });
25
+ }
26
+ export default createEmailAttachmentFromFile;
27
+ //# sourceMappingURL=create-email-attachment-from-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-email-attachment-from-file.js","sourceRoot":"","sources":["../src/create-email-attachment-from-file.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,EAAE;AACF,8EAA8E;AAC9E,6EAA6E;AAC7E,6EAA6E;AAC7E,wEAAwE;AACxE,gEAAgE;AAEhE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EACL,qBAAqB,GAEtB,MAAM,2BAA2B,CAAC;AAOnC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,6BAA6B,CACjD,IAAY,EACZ,OAA2C,EAAE;IAE7C,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;IACnC,OAAO,qBAAqB,CAAC;QAC3B,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,IAAI,CAAC;QACzC,OAAO,EAAE,KAAK;QACd,GAAG,CAAC,IAAI,CAAC,WAAW,KAAK,SAAS;YAChC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE;YACnC,CAAC,CAAC,EAAE,CAAC;QACP,GAAG,CAAC,IAAI,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACvE,CAAC,CAAC;AACL,CAAC;AAED,eAAe,6BAA6B,CAAC"}
@@ -0,0 +1,43 @@
1
+ import { type EmailAttachment } from "./validators/email-attachment-schema.js";
2
+ /**
3
+ * Input accepted by {@link createEmailAttachment}. A ready-made
4
+ * {@link EmailAttachment} is also valid input (its `content` is base64,
5
+ * which is the default string interpretation), so the two can be mixed
6
+ * freely in `ISendEmailOpts.attachments`.
7
+ */
8
+ export interface EmailAttachmentInput {
9
+ /** Name the recipient's mail client shows for the file. */
10
+ filename: string;
11
+ /**
12
+ * The file's bytes. Binary data may be passed as a `Uint8Array` (Node's
13
+ * `Buffer` included) or an `ArrayBuffer`. A string is treated as
14
+ * base64-encoded bytes -- the request body's wire format -- unless
15
+ * `encoding` is set to "utf8", in which case the string itself is the
16
+ * file's (plain-text) content.
17
+ */
18
+ content: string | Uint8Array | ArrayBuffer;
19
+ /**
20
+ * How to interpret a string `content`. Ignored for binary content.
21
+ * - "base64" (default): `content` is already base64-encoded.
22
+ * - "utf8": `content` is the file's text and will be encoded here.
23
+ */
24
+ encoding?: "base64" | "utf8";
25
+ /** MIME type; derived from the filename's extension when omitted. */
26
+ contentType?: string;
27
+ /** Content-ID for inline use via `cid:` references in the HTML body. */
28
+ contentId?: string;
29
+ }
30
+ /**
31
+ * Base64-encode raw bytes, with the standard alphabet and padding. Uses
32
+ * `Buffer` where available (Node, Bun) and falls back to `btoa` elsewhere.
33
+ */
34
+ export declare function encodeBytesAsBase64(bytes: Uint8Array): string;
35
+ /**
36
+ * Build a validated {@link EmailAttachment} for `body.attachments`, taking
37
+ * care of base64-encoding binary or plain-text content.
38
+ *
39
+ * @throws TypeError when the resulting attachment fails validation (bad
40
+ * filename, malformed base64, unsupported content type, ...).
41
+ */
42
+ export declare function createEmailAttachment(input: EmailAttachmentInput): EmailAttachment;
43
+ export default createEmailAttachment;
@@ -0,0 +1,58 @@
1
+ // create-email-attachment.ts
2
+ import { emailAttachmentSchema, } from "./validators/email-attachment-schema.js";
3
+ /**
4
+ * Base64-encode raw bytes, with the standard alphabet and padding. Uses
5
+ * `Buffer` where available (Node, Bun) and falls back to `btoa` elsewhere.
6
+ */
7
+ export function encodeBytesAsBase64(bytes) {
8
+ if (typeof Buffer !== "undefined" && typeof Buffer.from === "function") {
9
+ return Buffer.from(bytes).toString("base64");
10
+ }
11
+ let binary = "";
12
+ const CHUNK_SIZE = 0x8000;
13
+ for (let i = 0; i < bytes.length; i += CHUNK_SIZE) {
14
+ binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK_SIZE));
15
+ }
16
+ return btoa(binary);
17
+ }
18
+ /**
19
+ * Build a validated {@link EmailAttachment} for `body.attachments`, taking
20
+ * care of base64-encoding binary or plain-text content.
21
+ *
22
+ * @throws TypeError when the resulting attachment fails validation (bad
23
+ * filename, malformed base64, unsupported content type, ...).
24
+ */
25
+ export function createEmailAttachment(input) {
26
+ let content;
27
+ if (typeof input.content === "string") {
28
+ content =
29
+ input.encoding === "utf8"
30
+ ? encodeBytesAsBase64(new TextEncoder().encode(input.content))
31
+ : input.content;
32
+ }
33
+ else if (input.content instanceof Uint8Array) {
34
+ content = encodeBytesAsBase64(input.content);
35
+ }
36
+ else if (input.content instanceof ArrayBuffer) {
37
+ content = encodeBytesAsBase64(new Uint8Array(input.content));
38
+ }
39
+ else {
40
+ throw new TypeError("Expected attachment 'content' to be a string, Uint8Array or ArrayBuffer!", { cause: `Received type '${typeof input.content}'` });
41
+ }
42
+ const candidate = {
43
+ filename: input.filename,
44
+ content,
45
+ ...(input.contentType !== undefined
46
+ ? { contentType: input.contentType }
47
+ : {}),
48
+ ...(input.contentId !== undefined ? { contentId: input.contentId } : {}),
49
+ };
50
+ const parsed = emailAttachmentSchema.safeParse(candidate);
51
+ if (!parsed.success) {
52
+ const reasons = parsed.error.issues.map((issue) => issue.message);
53
+ throw new TypeError(`Invalid email attachment '${String(input.filename)}': ${reasons.join(" ")}`);
54
+ }
55
+ return parsed.data;
56
+ }
57
+ export default createEmailAttachment;
58
+ //# sourceMappingURL=create-email-attachment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-email-attachment.js","sourceRoot":"","sources":["../src/create-email-attachment.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAE7B,OAAO,EAEL,qBAAqB,GACtB,MAAM,sCAAsC,CAAC;AA+B9C;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAiB;IACnD,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QACvE,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC/C,CAAC;IACD,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,MAAM,UAAU,GAAG,MAAM,CAAC;IAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,UAAU,EAAE,CAAC;QAClD,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;AACtB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAA2B;IAE3B,IAAI,OAAe,CAAC;IACpB,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACtC,OAAO;YACL,KAAK,CAAC,QAAQ,KAAK,MAAM;gBACvB,CAAC,CAAC,mBAAmB,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBAC9D,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;IACtB,CAAC;SAAM,IAAI,KAAK,CAAC,OAAO,YAAY,UAAU,EAAE,CAAC;QAC/C,OAAO,GAAG,mBAAmB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC/C,CAAC;SAAM,IAAI,KAAK,CAAC,OAAO,YAAY,WAAW,EAAE,CAAC;QAChD,OAAO,GAAG,mBAAmB,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/D,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,SAAS,CACjB,0EAA0E,EAC1E,EAAE,KAAK,EAAE,kBAAkB,OAAO,KAAK,CAAC,OAAO,GAAG,EAAE,CACrD,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAoB;QACjC,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,OAAO;QACP,GAAG,CAAC,KAAK,CAAC,WAAW,KAAK,SAAS;YACjC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE;YACpC,CAAC,CAAC,EAAE,CAAC;QACP,GAAG,CAAC,KAAK,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACzE,CAAC;IAEF,MAAM,MAAM,GAAG,qBAAqB,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IAC1D,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAClE,MAAM,IAAI,SAAS,CACjB,6BAA6B,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAC7E,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED,eAAe,qBAAqB,CAAC"}
package/dist/index.d.ts CHANGED
@@ -2,6 +2,10 @@ export { sendEmailRequestBodySchema, createSendEmailRequestBodySchema, } from ".
2
2
  export type { SendEmailRequestBody } from "./send-email-request-body-schema.js";
3
3
  export { emailTemplateIdSchema } from "./validators/email-template-id-schema.js";
4
4
  export { transportIdSchema } from "./validators/transport-id-schema.js";
5
+ export { emailAttachmentSchema, emailAttachmentsSchema, MAX_ATTACHMENTS_PER_EMAIL, MAX_TOTAL_ATTACHMENT_BYTES, } from "./validators/email-attachment-schema.js";
6
+ export type { EmailAttachment } from "./validators/email-attachment-schema.js";
7
+ export { createEmailAttachment } from "./create-email-attachment.js";
8
+ export type { EmailAttachmentInput } from "./create-email-attachment.js";
5
9
  export { sendEmail, sendEmail as default, } from "./send-email.js";
6
10
  export type { ISendEmailOpts } from './send-email.js';
7
11
  export { getSchemaVaultsMailApiKey, } from "./env/get-api-key.js";
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  export { sendEmailRequestBodySchema, createSendEmailRequestBodySchema, } from "./send-email-request-body-schema.js";
2
2
  export { emailTemplateIdSchema } from "./validators/email-template-id-schema.js";
3
3
  export { transportIdSchema } from "./validators/transport-id-schema.js";
4
+ export { emailAttachmentSchema, emailAttachmentsSchema, MAX_ATTACHMENTS_PER_EMAIL, MAX_TOTAL_ATTACHMENT_BYTES, } from "./validators/email-attachment-schema.js";
5
+ export { createEmailAttachment } from "./create-email-attachment.js";
4
6
  export { sendEmail, sendEmail as default, } from "./send-email.js";
5
7
  export { getSchemaVaultsMailApiKey, } from "./env/get-api-key.js";
6
8
  export { getSchemaVaultsMailingListId, } from "./env/get-mailing-list-id.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,EAC1B,gCAAgC,GACjC,MAAM,kCAAkC,CAAC;AAG1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAE9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAErE,OAAO,EACL,SAAS,EACT,SAAS,IAAI,OAAO,GACrB,MAAM,cAAc,CAAC;AAKtB,OAAO,EACL,yBAAyB,GAC1B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,4BAA4B,GAC7B,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,gBAAgB,EACjB,MAAM,2BAA2B,CAAA;AAElC,OAAO,EACL,sBAAsB,GACvB,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,EAC1B,gCAAgC,GACjC,MAAM,kCAAkC,CAAC;AAG1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAE9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAErE,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,sCAAsC,CAAC;AAG9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAGlE,OAAO,EACL,SAAS,EACT,SAAS,IAAI,OAAO,GACrB,MAAM,cAAc,CAAC;AAKtB,OAAO,EACL,yBAAyB,GAC1B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,4BAA4B,GAC7B,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,gBAAgB,EACjB,MAAM,2BAA2B,CAAA;AAElC,OAAO,EACL,sBAAsB,GACvB,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC"}
@@ -21,6 +21,12 @@ export declare function createSendEmailRequestBodySchema(allow_mailing_list_ids_
21
21
  bcc: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
22
22
  dryRun: z.ZodOptional<z.ZodBoolean>;
23
23
  transport: z.ZodOptional<z.ZodString>;
24
+ attachments: z.ZodOptional<z.ZodArray<z.ZodObject<{
25
+ filename: z.ZodNonOptional<z.ZodString>;
26
+ content: z.ZodNonOptional<z.ZodBase64>;
27
+ contentType: z.ZodOptional<z.ZodString>;
28
+ contentId: z.ZodOptional<z.ZodString>;
29
+ }, z.core.$strict>>>;
24
30
  }, z.core.$strict>;
25
31
  /**
26
32
  * Default send email request body schema
@@ -45,6 +51,13 @@ export declare const sendEmailRequestBodySchema: z.ZodObject<{
45
51
  bcc: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
46
52
  dryRun: z.ZodOptional<z.ZodBoolean>;
47
53
  transport: z.ZodOptional<z.ZodString>;
54
+ attachments: z.ZodOptional<z.ZodArray<z.ZodObject<{
55
+ filename: z.ZodNonOptional<z.ZodString>;
56
+ content: z.ZodNonOptional<z.ZodBase64>;
57
+ contentType: z.ZodOptional<z.ZodString>;
58
+ contentId: z.ZodOptional<z.ZodString>;
59
+ }, z.core.$strict>>>;
48
60
  }, z.core.$strict>;
49
61
  export type SendEmailRequestBody = z.infer<typeof sendEmailRequestBodySchema>;
62
+ export type { EmailAttachment } from "./validators/email-attachment-schema.js";
50
63
  export default createSendEmailRequestBodySchema;
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { emailTemplateIdSchema } from "./validators/email-template-id-schema.js";
3
3
  import { transportIdSchema } from "./validators/transport-id-schema.js";
4
+ import { emailAttachmentsSchema } from "./validators/email-attachment-schema.js";
4
5
  const sendEmailTemplateOptions = z
5
6
  .object({
6
7
  template_id: emailTemplateIdSchema,
@@ -21,6 +22,16 @@ const sendRawEmailOptions = z
21
22
  })
22
23
  .strict();
23
24
  const MAX_RECIPIENTS = 50;
25
+ // C0 controls except HTAB, plus DEL: a line break in a header value can
26
+ // start a new header (e.g. `Bcc:`).
27
+ function hasHeaderUnsafeCharacters(value) {
28
+ for (let i = 0; i < value.length; i++) {
29
+ const code = value.charCodeAt(i);
30
+ if ((code < 0x20 && code !== 0x09) || code === 0x7f)
31
+ return true;
32
+ }
33
+ return false;
34
+ }
24
35
  export function createRecipientSchema(allow_mailing_list_ids_as_recipients = false) {
25
36
  if (allow_mailing_list_ids_as_recipients) {
26
37
  return z.union([
@@ -46,7 +57,12 @@ export function createSendEmailRequestBodySchema(allow_mailing_list_ids_as_recip
46
57
  .object({
47
58
  to: createRecipientSchema(allow_mailing_list_ids_as_recipients),
48
59
  from: z.string().email().optional(),
49
- subject: z.string().nonempty(),
60
+ subject: z
61
+ .string()
62
+ .nonempty()
63
+ .refine((subject) => !hasHeaderUnsafeCharacters(subject), {
64
+ message: "Subject must not contain line breaks or control characters!",
65
+ }),
50
66
  message: z.union([sendEmailTemplateOptions, sendRawEmailOptions]),
51
67
  replyTo: z.string().email().optional(),
52
68
  cc: z
@@ -69,6 +85,14 @@ export function createSendEmailRequestBodySchema(allow_mailing_list_ids_as_recip
69
85
  * mail-server picks its own default transport.
70
86
  */
71
87
  transport: transportIdSchema.optional(),
88
+ /**
89
+ * Files to attach to the email. Each attachment carries its bytes as a
90
+ * base64 string (request bodies are JSON); see `createEmailAttachment`
91
+ * for encoding binary or plain-text content. Between 1 and
92
+ * `MAX_ATTACHMENTS_PER_EMAIL` files, totalling at most
93
+ * `MAX_TOTAL_ATTACHMENT_BYTES` once decoded. Omit when there are none.
94
+ */
95
+ attachments: emailAttachmentsSchema.optional(),
72
96
  })
73
97
  .required({
74
98
  to: true,
@@ -1 +1 @@
1
- {"version":3,"file":"send-email-request-body-schema.js","sourceRoot":"","sources":["../src/send-email-request-body-schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"send-email-request-body-schema.js","sourceRoot":"","sources":["../src/send-email-request-body-schema.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { type SendEmailRequestBody } from "./send-email-request-body-schema.js";
2
+ import { type EmailAttachmentInput } from "./create-email-attachment.js";
2
3
  import getSchemaVaultsMailApiKey from "./env/get-api-key.js";
3
4
  export interface ISendEmailOpts {
4
5
  body: SendEmailRequestBody;
@@ -10,6 +11,14 @@ export interface ISendEmailOpts {
10
11
  * mail-server's configured transports should deliver this email.
11
12
  */
12
13
  transport?: string;
14
+ /**
15
+ * Convenience for `body.attachments` -- files to attach to the email.
16
+ * Unlike `body.attachments`, binary content (`Uint8Array`, `Buffer`,
17
+ * `ArrayBuffer`) and plain text (`encoding: "utf8"`) are accepted here
18
+ * and base64-encoded for you; see `createEmailAttachment`. Appended after
19
+ * any attachments already present on `body`.
20
+ */
21
+ attachments?: EmailAttachmentInput[];
13
22
  }
14
23
  export { getSchemaVaultsMailApiKey };
15
24
  export declare function sendEmail({ body, ...opts }: ISendEmailOpts): Promise<void>;
@@ -1,14 +1,21 @@
1
1
  // send-email.ts
2
2
  import { createSendEmailRequestBodySchema, } from "./send-email-request-body-schema.js";
3
+ import { createEmailAttachment, } from "./create-email-attachment.js";
3
4
  import getSchemaVaultsMailApiKey from "./env/get-api-key.js";
4
5
  import parseMailServerUrlWithEnvFallback from "./parse-mail-server-url-with-env-fallback.js";
5
6
  const body_schema = createSendEmailRequestBodySchema(true);
6
7
  export { getSchemaVaultsMailApiKey };
7
8
  export async function sendEmail({ body, ...opts }) {
9
+ const { attachments: bodyAttachments, ...bodyWithoutAttachments } = body;
10
+ const attachments = [
11
+ ...(bodyAttachments ?? []),
12
+ ...(opts.attachments ?? []).map(createEmailAttachment),
13
+ ];
8
14
  const effectiveBody = {
9
- ...body,
15
+ ...bodyWithoutAttachments,
10
16
  ...(typeof opts.dryRun === "boolean" ? { dryRun: opts.dryRun } : {}),
11
17
  ...(typeof opts.transport === "string" ? { transport: opts.transport } : {}),
18
+ ...(attachments.length > 0 ? { attachments } : {}),
12
19
  };
13
20
  const parsed = await body_schema.safeParseAsync(effectiveBody);
14
21
  if (!parsed.success) {
@@ -1 +1 @@
1
- {"version":3,"file":"send-email.js","sourceRoot":"","sources":["../src/send-email.ts"],"names":[],"mappings":"AAAA,gBAAgB;AAEhB,OAAO,EAEL,gCAAgC,GACjC,MAAM,kCAAkC,CAAC;AAC1C,OAAO,yBAAyB,MAAM,mBAAmB,CAAC;AAC1D,OAAO,iCAAiC,MAAM,2CAA2C,CAAC;AAE1F,MAAM,WAAW,GAAG,gCAAgC,CAAC,IAAI,CAAC,CAAC;AAc3D,OAAO,EAAE,yBAAyB,EAAE,CAAC;AAErC,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,EAC9B,IAAI,EACJ,GAAG,IAAI,EACQ;IACf,MAAM,aAAa,GAAyB;QAC1C,GAAG,IAAI;QACP,GAAG,CAAC,OAAO,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpE,GAAG,CAAC,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC7E,CAAC;IAEF,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;IAC/D,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO,CAAC,KAAK,CAAC,oBAAoB,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QAClD,MAAM,IAAI,SAAS,CAAC,sCAAsC,CAAC,CAAC;IAC9D,CAAC;IAED,IAAI,WAAmB,CAAC;IACxB,IAAI,IAAI,CAAC,WAAW,IAAI,OAAO,IAAI,CAAC,WAAW,KAAK,QAAQ,EAAE,CAAC;QAC7D,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;IACjC,CAAC;SAAM,CAAC;QACN,WAAW,GAAG,yBAAyB,EAAE,CAAC;IAC5C,CAAC;IAED,MAAM,eAAe,GAAW,iCAAiC,CAC/D,IAAI,CAAC,aAAa,CACnB,CAAC;IAEF,MAAM,QAAQ,GAAQ,IAAI,GAAG,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;IAC5D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,QAAQ,EAAE;QACrC,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAmC,CAAC;QAChE,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,UAAU,WAAW,EAAE;SACvC;KACF,CAAC,CAAC;IACH,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC5C,IAAI,YAAY,GAAG,4CAA4C,CAAC;QAChE,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACtC,IACE,OAAO,OAAO,KAAK,QAAQ;gBAC3B,CAAC,CAAC,OAAO;gBACT,SAAS,IAAI,OAAO;gBACpB,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ,EACnC,CAAC;gBACD,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC;YACjC,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,kDAAkD;QACpD,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC;IAChC,CAAC;IACD,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC3C,IAAI,OAAO,YAAY,KAAK,QAAQ,IAAI,CAAC,YAAY,EAAE,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,CAAC,CAAC,SAAS,IAAI,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;QAC1D,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACzD,CAAC;IAED,OAAO;AACT,CAAC;AAED,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"send-email.js","sourceRoot":"","sources":["../src/send-email.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Maximum number of attachments accepted on a single email.
4
+ */
5
+ export declare const MAX_ATTACHMENTS_PER_EMAIL: number;
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 declare const MAX_TOTAL_ATTACHMENT_BYTES: number;
14
+ /**
15
+ * Number of bytes a (padded, whitespace-free) base64 string decodes to,
16
+ * computed without actually decoding it.
17
+ */
18
+ export declare function base64DecodedByteLength(base64: string): number;
19
+ /**
20
+ * Name the recipient's mail client shows for the attached file. Unicode is
21
+ * allowed; path separators, control characters and bidirectional formatting
22
+ * characters are not.
23
+ */
24
+ export declare const attachmentFilenameSchema: z.ZodString;
25
+ /**
26
+ * The attached file's bytes, base64-encoded (standard alphabet, padded, no
27
+ * line breaks). Request bodies are JSON, so binary content has to travel
28
+ * as text.
29
+ */
30
+ export declare const attachmentContentSchema: z.ZodBase64;
31
+ /**
32
+ * MIME type of the attachment, e.g. "application/pdf" or
33
+ * "text/csv; charset=utf-8". Printable ASCII only. When omitted, the
34
+ * mail-server's transport derives it from the filename's extension.
35
+ */
36
+ export declare const attachmentContentTypeSchema: z.ZodString;
37
+ /**
38
+ * Content-ID for inline attachments. Setting it marks the attachment as
39
+ * inline so the HTML body can reference it as `<img src="cid:<contentId>">`.
40
+ * Pass the bare identifier -- without the angle brackets. Content-ID is an
41
+ * ASCII header; anything else is emitted as an encoded-word that no longer
42
+ * matches the HTML's `cid:` reference.
43
+ */
44
+ export declare const attachmentContentIdSchema: z.ZodString;
45
+ /**
46
+ * A single file attached to an email, in the shape accepted by the
47
+ * `@schemavaults/mail-server` `/api/send` route.
48
+ */
49
+ export declare const emailAttachmentSchema: z.ZodObject<{
50
+ filename: z.ZodNonOptional<z.ZodString>;
51
+ content: z.ZodNonOptional<z.ZodBase64>;
52
+ contentType: z.ZodOptional<z.ZodString>;
53
+ contentId: z.ZodOptional<z.ZodString>;
54
+ }, z.core.$strict>;
55
+ export type EmailAttachment = z.infer<typeof emailAttachmentSchema>;
56
+ /**
57
+ * The full list of attachments on one email: between 1 and
58
+ * {@link MAX_ATTACHMENTS_PER_EMAIL} files whose decoded sizes add up to at
59
+ * most {@link MAX_TOTAL_ATTACHMENT_BYTES}.
60
+ */
61
+ export declare const emailAttachmentsSchema: z.ZodArray<z.ZodObject<{
62
+ filename: z.ZodNonOptional<z.ZodString>;
63
+ content: z.ZodNonOptional<z.ZodBase64>;
64
+ contentType: z.ZodOptional<z.ZodString>;
65
+ contentId: z.ZodOptional<z.ZodString>;
66
+ }, z.core.$strict>>;
67
+ export default emailAttachmentSchema;
@@ -0,0 +1,148 @@
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
+ // C0, DEL and C1 controls.
29
+ function hasControlCharacters(value) {
30
+ for (let i = 0; i < value.length; i++) {
31
+ const code = value.charCodeAt(i);
32
+ if (code < 0x20 || (code >= 0x7f && code <= 0x9f))
33
+ return true;
34
+ }
35
+ return false;
36
+ }
37
+ // Bidirectional formatting characters (ALM, LRM, RLM, LRE through RLO, LRI
38
+ // through PDI) let a filename like "invoice<U+202E>fdp.exe" display as
39
+ // "invoiceexe.pdf". Listed as code points because the characters are
40
+ // invisible in source.
41
+ const BIDI_FORMATTING = new Set([
42
+ 0x061c, 0x200e, 0x200f, 0x202a, 0x202b, 0x202c, 0x202d, 0x202e, 0x2066,
43
+ 0x2067, 0x2068, 0x2069,
44
+ ]);
45
+ function hasBidiFormatting(value) {
46
+ for (let i = 0; i < value.length; i++) {
47
+ if (BIDI_FORMATTING.has(value.charCodeAt(i)))
48
+ return true;
49
+ }
50
+ return false;
51
+ }
52
+ /**
53
+ * Name the recipient's mail client shows for the attached file. Unicode is
54
+ * allowed; path separators, control characters and bidirectional formatting
55
+ * characters are not.
56
+ */
57
+ export const attachmentFilenameSchema = z
58
+ .string()
59
+ .min(1, "Attachment filename must be non-empty!")
60
+ .max(255, "Attachment filename must be at most 255 characters!")
61
+ .refine((name) => name !== "." && name !== "..", {
62
+ message: "Attachment filename must not be '.' or '..'!",
63
+ })
64
+ .refine((name) => !name.includes("/") && !name.includes("\\"), {
65
+ message: "Attachment filename must not contain path separators!",
66
+ })
67
+ .refine((name) => !hasControlCharacters(name), {
68
+ message: "Attachment filename must not contain control characters!",
69
+ })
70
+ .refine((name) => !hasBidiFormatting(name), {
71
+ message: "Attachment filename must not contain bidirectional formatting characters!",
72
+ });
73
+ /**
74
+ * The attached file's bytes, base64-encoded (standard alphabet, padded, no
75
+ * line breaks). Request bodies are JSON, so binary content has to travel
76
+ * as text.
77
+ */
78
+ export const attachmentContentSchema = z
79
+ .base64("Attachment content must be base64-encoded!")
80
+ .min(1, "Attachment content must not be empty!");
81
+ const MIME_TOKEN = "[A-Za-z0-9!#$&^_.+-]+";
82
+ // Printable ASCII only, so no value can carry CR/LF or other controls out of
83
+ // its header. Quoted: anything printable but `"`. Unquoted: printable minus
84
+ // space, `"` and `;`.
85
+ const MIME_PARAM_VALUE = '(?:"[\\x20\\x21\\x23-\\x7E]*"|[\\x21\\x23-\\x3A\\x3C-\\x7E]+)';
86
+ /**
87
+ * MIME type of the attachment, e.g. "application/pdf" or
88
+ * "text/csv; charset=utf-8". Printable ASCII only. When omitted, the
89
+ * mail-server's transport derives it from the filename's extension.
90
+ */
91
+ export const attachmentContentTypeSchema = z
92
+ .string()
93
+ .min(1, "Attachment content type must be non-empty!")
94
+ .max(255, "Attachment content type must be at most 255 characters!")
95
+ .regex(
96
+ // `[ \t]*`, not `\s*`: `\s` matches CR/LF.
97
+ new RegExp(`^${MIME_TOKEN}\\/${MIME_TOKEN}(?:[ \\t]*;[ \\t]*${MIME_TOKEN}=${MIME_PARAM_VALUE})*$`), "Attachment content type must be a MIME type such as 'application/pdf'!");
98
+ /**
99
+ * Content-ID for inline attachments. Setting it marks the attachment as
100
+ * inline so the HTML body can reference it as `<img src="cid:<contentId>">`.
101
+ * Pass the bare identifier -- without the angle brackets. Content-ID is an
102
+ * ASCII header; anything else is emitted as an encoded-word that no longer
103
+ * matches the HTML's `cid:` reference.
104
+ */
105
+ export const attachmentContentIdSchema = z
106
+ .string()
107
+ .min(1, "Attachment content ID must be non-empty!")
108
+ .max(255, "Attachment content ID must be at most 255 characters!")
109
+ .regex(/^[\x21-\x3B\x3D\x3F-\x7E]+$/, "Attachment content ID must be printable ASCII without spaces or angle brackets!");
110
+ /**
111
+ * A single file attached to an email, in the shape accepted by the
112
+ * `@schemavaults/mail-server` `/api/send` route.
113
+ */
114
+ export const emailAttachmentSchema = z
115
+ .object({
116
+ filename: attachmentFilenameSchema,
117
+ content: attachmentContentSchema,
118
+ contentType: attachmentContentTypeSchema.optional(),
119
+ contentId: attachmentContentIdSchema.optional(),
120
+ })
121
+ .required({
122
+ filename: true,
123
+ content: true,
124
+ })
125
+ .strict();
126
+ /**
127
+ * The full list of attachments on one email: between 1 and
128
+ * {@link MAX_ATTACHMENTS_PER_EMAIL} files whose decoded sizes add up to at
129
+ * most {@link MAX_TOTAL_ATTACHMENT_BYTES}.
130
+ */
131
+ export const emailAttachmentsSchema = z
132
+ .array(emailAttachmentSchema)
133
+ .min(1, "Provide at least one attachment, or omit 'attachments' entirely!")
134
+ .max(MAX_ATTACHMENTS_PER_EMAIL, `An email may have at most ${MAX_ATTACHMENTS_PER_EMAIL} attachments!`)
135
+ .superRefine((attachments, ctx) => {
136
+ let totalBytes = 0;
137
+ for (const attachment of attachments) {
138
+ totalBytes += base64DecodedByteLength(attachment.content);
139
+ }
140
+ if (totalBytes > MAX_TOTAL_ATTACHMENT_BYTES) {
141
+ ctx.addIssue({
142
+ code: "custom",
143
+ message: `Attachments must total at most ${MAX_TOTAL_ATTACHMENT_BYTES} bytes (${MAX_TOTAL_ATTACHMENT_BYTES / (1024 * 1024)} MiB) once decoded; received ${totalBytes} bytes.`,
144
+ });
145
+ }
146
+ });
147
+ export default emailAttachmentSchema;
148
+ //# 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.3.1",
4
+ "version": "0.4.1",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
7
7
  "repository": {
@@ -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/*",