@venizia/ignis 0.4.6 → 0.4.7-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/LICENSE.md +1 -4
- package/README.md +2 -2
- package/dist/esm/base/applications/base.d.ts.map +1 -1
- package/dist/esm/base/applications/base.js +19 -15
- package/dist/esm/base/applications/base.js.map +1 -1
- package/dist/esm/base/applications/server.d.ts +14 -9
- package/dist/esm/base/applications/server.d.ts.map +1 -1
- package/dist/esm/base/applications/server.js +36 -29
- package/dist/esm/base/applications/server.js.map +1 -1
- package/dist/esm/base/controllers/grpc/adapter.d.ts.map +1 -1
- package/dist/esm/base/controllers/grpc/adapter.js +5 -1
- package/dist/esm/base/controllers/grpc/adapter.js.map +1 -1
- package/dist/esm/base/middlewares/request-spy/request-spy.middleware.d.ts.map +1 -1
- package/dist/esm/base/middlewares/request-spy/request-spy.middleware.js +9 -3
- package/dist/esm/base/middlewares/request-spy/request-spy.middleware.js.map +1 -1
- package/dist/esm/common/constants.d.ts.map +1 -1
- package/dist/esm/common/constants.js +2 -2
- package/dist/esm/common/constants.js.map +1 -1
- package/dist/esm/components/api-reference/ui/scalar.d.ts.map +1 -1
- package/dist/esm/components/api-reference/ui/scalar.js +2 -1
- package/dist/esm/components/api-reference/ui/scalar.js.map +1 -1
- package/dist/esm/components/api-reference/ui/swagger.d.ts.map +1 -1
- package/dist/esm/components/api-reference/ui/swagger.js +2 -1
- package/dist/esm/components/api-reference/ui/swagger.js.map +1 -1
- package/dist/esm/components/auth/authenticate/component.js +2 -2
- package/dist/esm/components/auth/authenticate/component.js.map +1 -1
- package/dist/esm/components/auth/authenticate/controllers/factory.d.ts +139 -136
- package/dist/esm/components/auth/authenticate/controllers/factory.d.ts.map +1 -1
- package/dist/esm/components/auth/authenticate/controllers/factory.js +175 -172
- package/dist/esm/components/auth/authenticate/controllers/factory.js.map +1 -1
- package/dist/esm/components/auth/authenticate/controllers/jwks/definitions.js +2 -2
- package/dist/esm/components/auth/authenticate/controllers/jwks/definitions.js.map +1 -1
- package/dist/esm/components/auth/authenticate/controllers/service-certs/definitions.js +2 -2
- package/dist/esm/components/auth/authenticate/controllers/service-certs/definitions.js.map +1 -1
- package/dist/esm/components/auth/authenticate/services/bearer/abstract.service.d.ts.map +1 -1
- package/dist/esm/components/auth/authenticate/services/bearer/abstract.service.js +4 -2
- package/dist/esm/components/auth/authenticate/services/bearer/abstract.service.js.map +1 -1
- package/dist/esm/components/auth/authenticate/services/service/signer.service.d.ts.map +1 -1
- package/dist/esm/components/auth/authenticate/services/service/signer.service.js +6 -4
- package/dist/esm/components/auth/authenticate/services/service/signer.service.js.map +1 -1
- package/dist/esm/components/auth/authorize/adapters/base-filtered.js +2 -2
- package/dist/esm/components/auth/authorize/adapters/base-filtered.js.map +1 -1
- package/dist/esm/components/auth/authorize/adapters/custom-grant-expander.d.ts.map +1 -1
- package/dist/esm/components/auth/authorize/adapters/custom-grant-expander.js +2 -2
- package/dist/esm/components/auth/authorize/adapters/custom-grant-expander.js.map +1 -1
- package/dist/esm/components/auth/authorize/adapters/scoped-casbin/adapter.d.ts.map +1 -1
- package/dist/esm/components/auth/authorize/adapters/scoped-casbin/adapter.js +6 -3
- package/dist/esm/components/auth/authorize/adapters/scoped-casbin/adapter.js.map +1 -1
- package/dist/esm/components/auth/authorize/enforcers/casbin-module.d.ts.map +1 -1
- package/dist/esm/components/auth/authorize/enforcers/casbin-module.js +2 -1
- package/dist/esm/components/auth/authorize/enforcers/casbin-module.js.map +1 -1
- package/dist/esm/components/auth/authorize/enforcers/casbin.enforcer.d.ts.map +1 -1
- package/dist/esm/components/auth/authorize/enforcers/casbin.enforcer.js +9 -3
- package/dist/esm/components/auth/authorize/enforcers/casbin.enforcer.js.map +1 -1
- package/dist/esm/components/auth/models/entities/columns.d.ts +99 -0
- package/dist/esm/components/auth/models/entities/columns.d.ts.map +1 -0
- package/dist/esm/components/auth/models/entities/columns.js +133 -0
- package/dist/esm/components/auth/models/entities/columns.js.map +1 -0
- package/dist/esm/components/auth/models/entities/index.d.ts +1 -2
- package/dist/esm/components/auth/models/entities/index.d.ts.map +1 -1
- package/dist/esm/components/auth/models/entities/index.js +1 -2
- package/dist/esm/components/auth/models/entities/index.js.map +1 -1
- package/dist/esm/components/auth/models/entities/permission.model.d.ts +1 -12
- package/dist/esm/components/auth/models/entities/permission.model.d.ts.map +1 -1
- package/dist/esm/components/auth/models/entities/permission.model.js +1 -36
- package/dist/esm/components/auth/models/entities/permission.model.js.map +1 -1
- package/dist/esm/components/auth/models/entities/policy-definition.model.d.ts +1 -49
- package/dist/esm/components/auth/models/entities/policy-definition.model.d.ts.map +1 -1
- package/dist/esm/components/auth/models/entities/policy-definition.model.js +1 -69
- package/dist/esm/components/auth/models/entities/policy-definition.model.js.map +1 -1
- package/dist/esm/components/controller/grpc/grpc.component.js +2 -2
- package/dist/esm/components/controller/grpc/grpc.component.js.map +1 -1
- package/dist/esm/components/health-check/component.d.ts.map +1 -1
- package/dist/esm/components/health-check/component.js +5 -3
- package/dist/esm/components/health-check/component.js.map +1 -1
- package/dist/esm/components/health-check/controller.js +5 -5
- package/dist/esm/components/health-check/controller.js.map +1 -1
- package/dist/esm/components/index.d.ts +0 -1
- package/dist/esm/components/index.d.ts.map +1 -1
- package/dist/esm/components/index.js +0 -1
- package/dist/esm/components/index.js.map +1 -1
- package/dist/esm/components/mail/helpers/executors/bull-mq-executor.helper.d.ts.map +1 -1
- package/dist/esm/components/mail/helpers/executors/bull-mq-executor.helper.js +2 -1
- package/dist/esm/components/mail/helpers/executors/bull-mq-executor.helper.js.map +1 -1
- package/dist/esm/components/mail/helpers/transporters/amazon-ses-transporter.helper.d.ts.map +1 -1
- package/dist/esm/components/mail/helpers/transporters/amazon-ses-transporter.helper.js +13 -9
- package/dist/esm/components/mail/helpers/transporters/amazon-ses-transporter.helper.js.map +1 -1
- package/dist/esm/components/mail/helpers/transporters/mailgun-transporter.helper.d.ts.map +1 -1
- package/dist/esm/components/mail/helpers/transporters/mailgun-transporter.helper.js +5 -3
- package/dist/esm/components/mail/helpers/transporters/mailgun-transporter.helper.js.map +1 -1
- package/dist/esm/components/mail/helpers/transporters/nodemail-transporter.helper.d.ts.map +1 -1
- package/dist/esm/components/mail/helpers/transporters/nodemail-transporter.helper.js +9 -7
- package/dist/esm/components/mail/helpers/transporters/nodemail-transporter.helper.js.map +1 -1
- package/dist/esm/components/mail/services/generators/verification-data.d.ts.map +1 -1
- package/dist/esm/components/mail/services/generators/verification-data.js +7 -3
- package/dist/esm/components/mail/services/generators/verification-data.js.map +1 -1
- package/dist/esm/components/mail/services/mail.service.d.ts.map +1 -1
- package/dist/esm/components/mail/services/mail.service.js +12 -9
- package/dist/esm/components/mail/services/mail.service.js.map +1 -1
- package/dist/esm/components/mail/utilities/address.utility.d.ts +12 -8
- package/dist/esm/components/mail/utilities/address.utility.d.ts.map +1 -1
- package/dist/esm/components/mail/utilities/address.utility.js +30 -27
- package/dist/esm/components/mail/utilities/address.utility.js.map +1 -1
- package/dist/esm/components/mail/utilities/attachment.utility.d.ts +31 -29
- package/dist/esm/components/mail/utilities/attachment.utility.d.ts.map +1 -1
- package/dist/esm/components/mail/utilities/attachment.utility.js +94 -86
- package/dist/esm/components/mail/utilities/attachment.utility.js.map +1 -1
- package/dist/esm/components/mail/utilities/body.utility.d.ts +6 -4
- package/dist/esm/components/mail/utilities/body.utility.d.ts.map +1 -1
- package/dist/esm/components/mail/utilities/body.utility.js +14 -12
- package/dist/esm/components/mail/utilities/body.utility.js.map +1 -1
- package/dist/esm/components/mail/utilities/mime.utility.d.ts +23 -17
- package/dist/esm/components/mail/utilities/mime.utility.d.ts.map +1 -1
- package/dist/esm/components/mail/utilities/mime.utility.js +109 -98
- package/dist/esm/components/mail/utilities/mime.utility.js.map +1 -1
- package/dist/esm/components/mail/utilities/verification.utility.d.ts +8 -2
- package/dist/esm/components/mail/utilities/verification.utility.d.ts.map +1 -1
- package/dist/esm/components/mail/utilities/verification.utility.js +7 -5
- package/dist/esm/components/mail/utilities/verification.utility.js.map +1 -1
- package/dist/esm/components/socket-io/component.js +3 -3
- package/dist/esm/components/socket-io/component.js.map +1 -1
- package/dist/esm/components/socket-io/handlers/bun.handler.d.ts +13 -11
- package/dist/esm/components/socket-io/handlers/bun.handler.d.ts.map +1 -1
- package/dist/esm/components/socket-io/handlers/bun.handler.js +32 -30
- package/dist/esm/components/socket-io/handlers/bun.handler.js.map +1 -1
- package/dist/esm/components/static-asset/common/commit-token.d.ts +21 -16
- package/dist/esm/components/static-asset/common/commit-token.d.ts.map +1 -1
- package/dist/esm/components/static-asset/common/commit-token.js +35 -28
- package/dist/esm/components/static-asset/common/commit-token.js.map +1 -1
- package/dist/esm/components/static-asset/common/constants.d.ts +0 -2
- package/dist/esm/components/static-asset/common/constants.d.ts.map +1 -1
- package/dist/esm/components/static-asset/common/constants.js +1 -3
- package/dist/esm/components/static-asset/common/constants.js.map +1 -1
- package/dist/esm/components/static-asset/common/types.d.ts +0 -4
- package/dist/esm/components/static-asset/common/types.d.ts.map +1 -1
- package/dist/esm/components/static-asset/common/types.js.map +1 -1
- package/dist/esm/components/static-asset/component.js +2 -2
- package/dist/esm/components/static-asset/component.js.map +1 -1
- package/dist/esm/components/static-asset/controller/base.definition.d.ts +6 -6
- package/dist/esm/components/static-asset/controller/base.definition.d.ts.map +1 -1
- package/dist/esm/components/static-asset/controller/base.definition.js +224 -224
- package/dist/esm/components/static-asset/controller/base.definition.js.map +1 -1
- package/dist/esm/components/static-asset/controller/factory.d.ts +23 -23
- package/dist/esm/components/static-asset/controller/factory.d.ts.map +1 -1
- package/dist/esm/components/static-asset/controller/factory.js +104 -74
- package/dist/esm/components/static-asset/controller/factory.js.map +1 -1
- package/dist/esm/components/static-asset/controller/key-scope.d.ts +28 -26
- package/dist/esm/components/static-asset/controller/key-scope.d.ts.map +1 -1
- package/dist/esm/components/static-asset/controller/key-scope.js +34 -30
- package/dist/esm/components/static-asset/controller/key-scope.js.map +1 -1
- package/dist/esm/components/static-asset/controller/meta-link.d.ts +37 -33
- package/dist/esm/components/static-asset/controller/meta-link.d.ts.map +1 -1
- package/dist/esm/components/static-asset/controller/meta-link.js +86 -84
- package/dist/esm/components/static-asset/controller/meta-link.js.map +1 -1
- package/dist/esm/components/static-asset/controller/route-options.d.ts +18 -16
- package/dist/esm/components/static-asset/controller/route-options.d.ts.map +1 -1
- package/dist/esm/components/static-asset/controller/route-options.js +28 -26
- package/dist/esm/components/static-asset/controller/route-options.js.map +1 -1
- package/dist/esm/components/static-asset/models/base.model.js +3 -3
- package/dist/esm/components/static-asset/models/base.model.js.map +1 -1
- package/dist/esm/components/websocket/component.js +2 -2
- package/dist/esm/components/websocket/component.js.map +1 -1
- package/dist/esm/components/websocket/handlers/bun.handler.d.ts +6 -4
- package/dist/esm/components/websocket/handlers/bun.handler.d.ts.map +1 -1
- package/dist/esm/components/websocket/handlers/bun.handler.js +21 -19
- package/dist/esm/components/websocket/handlers/bun.handler.js.map +1 -1
- package/dist/esm/testing/assert-application-boots.d.ts +6 -1
- package/dist/esm/testing/assert-application-boots.d.ts.map +1 -1
- package/dist/esm/testing/assert-application-boots.js +139 -129
- package/dist/esm/testing/assert-application-boots.js.map +1 -1
- package/dist/esm/utilities/drizzle-result.utility.d.ts +6 -8
- package/dist/esm/utilities/drizzle-result.utility.d.ts.map +1 -1
- package/dist/esm/utilities/drizzle-result.utility.js +23 -49
- package/dist/esm/utilities/drizzle-result.utility.js.map +1 -1
- package/dist/esm/utilities/network.utility.d.ts.map +1 -1
- package/dist/esm/utilities/network.utility.js +1 -1
- package/dist/esm/utilities/network.utility.js.map +1 -1
- package/dist/presets/app/files/src/boot.test.ts +2 -2
- package/dist/presets/app/files/src/controllers/hello.controller.ts +2 -2
- package/dist/presets/app/preset.json +4 -4
- package/dist/presets/lib/preset.json +4 -4
- package/dist/presets/shared/files/.configs/formatters/.ignore +4 -0
- package/dist/presets/shared/files/.configs/lints/.ignore +4 -0
- package/dist/presets/shared/files/.configs/lints/eslint.config.mjs +4 -0
- package/dist/presets/shared/files/.configs/lints/oxlint.config.mts +4 -0
- package/dist/presets/shared/files/README.md +15 -0
- package/dist/presets/shared/files/src/index.test.ts +8 -0
- package/dist/presets/shared/files/src/index.ts +8 -0
- package/dist/presets/shared/files/tsconfig.json +20 -0
- package/dist/presets/shared/preset.json +35 -0
- package/package.json +11 -11
- package/dist/esm/components/auth/models/entities/role.model.d.ts +0 -10
- package/dist/esm/components/auth/models/entities/role.model.d.ts.map +0 -1
- package/dist/esm/components/auth/models/entities/role.model.js +0 -15
- package/dist/esm/components/auth/models/entities/role.model.js.map +0 -1
- package/dist/esm/components/auth/models/entities/user.model.d.ts +0 -28
- package/dist/esm/components/auth/models/entities/user.model.d.ts.map +0 -1
- package/dist/esm/components/auth/models/entities/user.model.js +0 -17
- package/dist/esm/components/auth/models/entities/user.model.js.map +0 -1
- package/dist/esm/components/controller/index.d.ts +0 -2
- package/dist/esm/components/controller/index.d.ts.map +0 -1
- package/dist/esm/components/controller/index.js +0 -7
- package/dist/esm/components/controller/index.js.map +0 -1
|
@@ -2,8 +2,8 @@ import { HTTP } from '@venizia/ignis-helpers/common';
|
|
|
2
2
|
import { randomBytes } from 'node:crypto';
|
|
3
3
|
import { getError } from '@venizia/ignis-helpers/core';
|
|
4
4
|
import { MailErrorCodes } from '../common/index.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { MailAddressUtility } from './address.utility.js';
|
|
6
|
+
import { MailAttachmentUtility } from './attachment.utility.js';
|
|
7
7
|
const CRLF = '\r\n';
|
|
8
8
|
const BASE64_LINE_LENGTH = 76;
|
|
9
9
|
/** RFC 5322 recommends folding well under its own 998-byte hard limit; 78 matches common MTA
|
|
@@ -78,11 +78,9 @@ function encodeAddress(address) {
|
|
|
78
78
|
}
|
|
79
79
|
return `${encodeWord(name)} <${addr}>`;
|
|
80
80
|
}
|
|
81
|
-
/**
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
export function formatAddressHeader(value) {
|
|
85
|
-
return splitAddressList(value).map(encodeAddress).join(', ');
|
|
81
|
+
/** The body of `MailMimeUtility.formatAddressHeader`, callable from the header builder. */
|
|
82
|
+
function formatAddressList(value) {
|
|
83
|
+
return MailAddressUtility.splitList({ value }).map(encodeAddress).join(', ');
|
|
86
84
|
}
|
|
87
85
|
/**
|
|
88
86
|
* RFC 5322 §2.2.3 header folding: inserting CRLF immediately before an existing whitespace
|
|
@@ -191,19 +189,19 @@ function buildHeaders(message) {
|
|
|
191
189
|
const headers = [];
|
|
192
190
|
if (message.from) {
|
|
193
191
|
assertNoAddressListInjection({ field: 'from', value: message.from });
|
|
194
|
-
headers.push(foldHeaderLine(`From: ${
|
|
192
|
+
headers.push(foldHeaderLine(`From: ${formatAddressList(message.from)}`));
|
|
195
193
|
}
|
|
196
194
|
if (message.to) {
|
|
197
195
|
assertNoAddressListInjection({ field: 'to', value: message.to });
|
|
198
|
-
headers.push(foldHeaderLine(`To: ${
|
|
196
|
+
headers.push(foldHeaderLine(`To: ${formatAddressList(message.to)}`));
|
|
199
197
|
}
|
|
200
198
|
if (message.cc) {
|
|
201
199
|
assertNoAddressListInjection({ field: 'cc', value: message.cc });
|
|
202
|
-
headers.push(foldHeaderLine(`Cc: ${
|
|
200
|
+
headers.push(foldHeaderLine(`Cc: ${formatAddressList(message.cc)}`));
|
|
203
201
|
}
|
|
204
202
|
if (message.replyTo) {
|
|
205
203
|
assertNoAddressListInjection({ field: 'replyTo', value: message.replyTo });
|
|
206
|
-
headers.push(foldHeaderLine(`Reply-To: ${
|
|
204
|
+
headers.push(foldHeaderLine(`Reply-To: ${formatAddressList(message.replyTo)}`));
|
|
207
205
|
}
|
|
208
206
|
// Checked, never emitted: a `Bcc:` header would disclose the blind recipients, so they travel as
|
|
209
207
|
// `Destination.BccAddresses`. Without this, `bcc` is the one address field that fails at AWS.
|
|
@@ -261,8 +259,8 @@ function attachmentNameParams(attribute, value) {
|
|
|
261
259
|
`; ${attribute}*=UTF-8''${encodeRfc2231(value)}`);
|
|
262
260
|
}
|
|
263
261
|
async function attachmentPart(attachment, boundary) {
|
|
264
|
-
// No root here: a `path` that did not go through `
|
|
265
|
-
const content = await
|
|
262
|
+
// No root here: a `path` that did not go through `MailAttachmentUtility.resolve` is refused, never read.
|
|
263
|
+
const content = await MailAttachmentUtility.readContent({ attachment });
|
|
266
264
|
const contentType = attachment.contentType ?? 'application/octet-stream';
|
|
267
265
|
assertNoHeaderInjection({ field: 'attachment.contentType', value: contentType });
|
|
268
266
|
if (attachment.filename) {
|
|
@@ -293,94 +291,107 @@ async function attachmentPart(attachment, boundary) {
|
|
|
293
291
|
function newBoundary(label) {
|
|
294
292
|
return `----=_${label}_${randomBytes(12).toString('hex')}`;
|
|
295
293
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
* Inline (`cid`) attachments nest under `multipart/related` next to the body - a mail client
|
|
303
|
-
* only renders `cid:` references inline when they share a `multipart/related` parent with the
|
|
304
|
-
* body; under `multipart/mixed` they show up as ordinary downloadable attachments instead. Plain
|
|
305
|
-
* file attachments (no `cid`) stay under `multipart/mixed`, alongside the body or the
|
|
306
|
-
* `multipart/related` group when both kinds are present.
|
|
307
|
-
*/
|
|
308
|
-
export async function buildRawMimeMessage(message) {
|
|
309
|
-
const headers = buildHeaders(message);
|
|
310
|
-
const attachments = message.attachments ?? [];
|
|
311
|
-
const inlineAttachments = attachments.filter((attachment) => Boolean(attachment.cid));
|
|
312
|
-
const fileAttachments = attachments.filter((attachment) => !attachment.cid);
|
|
313
|
-
const hasBothBodies = Boolean(message.text) && Boolean(message.html);
|
|
314
|
-
const singleContentType = message.html ? 'text/html' : 'text/plain';
|
|
315
|
-
const singleContent = message.html ?? message.text ?? '';
|
|
316
|
-
const bodyPart = (boundary) => {
|
|
317
|
-
if (!hasBothBodies) {
|
|
318
|
-
return textPart(boundary, singleContentType, singleContent);
|
|
319
|
-
}
|
|
320
|
-
const altBoundary = newBoundary('Alt');
|
|
321
|
-
return [
|
|
322
|
-
`--${boundary}`,
|
|
323
|
-
`Content-Type: multipart/alternative; boundary="${altBoundary}"`,
|
|
324
|
-
'',
|
|
325
|
-
[
|
|
326
|
-
textPart(altBoundary, 'text/plain', message.text ?? ''),
|
|
327
|
-
textPart(altBoundary, 'text/html', message.html ?? ''),
|
|
328
|
-
`--${altBoundary}--`,
|
|
329
|
-
].join(CRLF),
|
|
330
|
-
].join(CRLF);
|
|
331
|
-
};
|
|
332
|
-
if (attachments.length === 0 && !hasBothBodies) {
|
|
333
|
-
headers.push(`Content-Type: ${singleContentType}; charset=UTF-8`);
|
|
334
|
-
headers.push('Content-Transfer-Encoding: base64');
|
|
335
|
-
return Buffer.from([...headers, '', encodeTextBase64(singleContent)].join(CRLF), 'utf-8');
|
|
336
|
-
}
|
|
337
|
-
if (attachments.length === 0) {
|
|
338
|
-
const boundary = newBoundary('Alt');
|
|
339
|
-
headers.push(`Content-Type: multipart/alternative; boundary="${boundary}"`);
|
|
340
|
-
const parts = [
|
|
341
|
-
textPart(boundary, 'text/plain', message.text ?? ''),
|
|
342
|
-
textPart(boundary, 'text/html', message.html ?? ''),
|
|
343
|
-
`--${boundary}--`,
|
|
344
|
-
];
|
|
345
|
-
return Buffer.from([...headers, '', parts.join(CRLF)].join(CRLF), 'utf-8');
|
|
294
|
+
export class MailMimeUtility {
|
|
295
|
+
/** Formats a `to`/`cc`/`from`/`replyTo` field as one RFC 5322 address list: splits a
|
|
296
|
+
* comma-separated string or array into individual mailboxes, RFC 2047-encodes each display name,
|
|
297
|
+
* and rejoins with `, ` - the same shape whether the caller passed a string or an array. */
|
|
298
|
+
static formatAddressHeader(opts) {
|
|
299
|
+
return formatAddressList(opts.value);
|
|
346
300
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
301
|
+
/**
|
|
302
|
+
* Builds a raw RFC 822 message with zero external dependencies - Amazon SES's `SendEmailCommand`
|
|
303
|
+
* `Content.Simple` shape has no attachment field, so attachments require a raw MIME body. Written
|
|
304
|
+
* by hand instead of depending on `nodemailer`'s `MailComposer` so the `ses` provider never needs
|
|
305
|
+
* the `nodemailer` peer installed at all.
|
|
306
|
+
*
|
|
307
|
+
* Inline (`cid`) attachments nest under `multipart/related` next to the body - a mail client
|
|
308
|
+
* only renders `cid:` references inline when they share a `multipart/related` parent with the
|
|
309
|
+
* body; under `multipart/mixed` they show up as ordinary downloadable attachments instead. Plain
|
|
310
|
+
* file attachments (no `cid`) stay under `multipart/mixed`, alongside the body or the
|
|
311
|
+
* `multipart/related` group when both kinds are present.
|
|
312
|
+
*/
|
|
313
|
+
static async buildRawMessage(opts) {
|
|
314
|
+
const { message } = opts;
|
|
315
|
+
const headers = buildHeaders(message);
|
|
316
|
+
const attachments = message.attachments ?? [];
|
|
317
|
+
const inlineAttachments = attachments.filter((attachment) => Boolean(attachment.cid));
|
|
318
|
+
const fileAttachments = attachments.filter((attachment) => !attachment.cid);
|
|
319
|
+
const hasBothBodies = Boolean(message.text) && Boolean(message.html);
|
|
320
|
+
const singleContentType = message.html ? 'text/html' : 'text/plain';
|
|
321
|
+
const singleContent = message.html ?? message.text ?? '';
|
|
322
|
+
const bodyPart = (boundary) => {
|
|
323
|
+
if (!hasBothBodies) {
|
|
324
|
+
return textPart(boundary, singleContentType, singleContent);
|
|
325
|
+
}
|
|
326
|
+
const altBoundary = newBoundary('Alt');
|
|
327
|
+
return [
|
|
328
|
+
`--${boundary}`,
|
|
329
|
+
`Content-Type: multipart/alternative; boundary="${altBoundary}"`,
|
|
330
|
+
'',
|
|
331
|
+
[
|
|
332
|
+
textPart(altBoundary, 'text/plain', message.text ?? ''),
|
|
333
|
+
textPart(altBoundary, 'text/html', message.html ?? ''),
|
|
334
|
+
`--${altBoundary}--`,
|
|
335
|
+
].join(CRLF),
|
|
336
|
+
].join(CRLF);
|
|
337
|
+
};
|
|
338
|
+
if (attachments.length === 0 && !hasBothBodies) {
|
|
339
|
+
headers.push(`Content-Type: ${singleContentType}; charset=UTF-8`);
|
|
340
|
+
headers.push('Content-Transfer-Encoding: base64');
|
|
341
|
+
return Buffer.from([...headers, '', encodeTextBase64(singleContent)].join(CRLF), 'utf-8');
|
|
352
342
|
}
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
const relatedBoundary = newBoundary('Related');
|
|
363
|
-
const parts = [bodyPart(relatedBoundary)];
|
|
364
|
-
for (const attachment of inlineAttachments) {
|
|
365
|
-
parts.push(await attachmentPart(attachment, relatedBoundary));
|
|
343
|
+
if (attachments.length === 0) {
|
|
344
|
+
const boundary = newBoundary('Alt');
|
|
345
|
+
headers.push(`Content-Type: multipart/alternative; boundary="${boundary}"`);
|
|
346
|
+
const parts = [
|
|
347
|
+
textPart(boundary, 'text/plain', message.text ?? ''),
|
|
348
|
+
textPart(boundary, 'text/html', message.html ?? ''),
|
|
349
|
+
`--${boundary}--`,
|
|
350
|
+
];
|
|
351
|
+
return Buffer.from([...headers, '', parts.join(CRLF)].join(CRLF), 'utf-8');
|
|
366
352
|
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
353
|
+
const buildRelatedPart = async (parentBoundary) => {
|
|
354
|
+
const relatedBoundary = newBoundary('Related');
|
|
355
|
+
const parts = [bodyPart(relatedBoundary)];
|
|
356
|
+
for (const attachment of inlineAttachments) {
|
|
357
|
+
const result = await attachmentPart(attachment, relatedBoundary);
|
|
358
|
+
parts.push(result);
|
|
359
|
+
}
|
|
360
|
+
parts.push(`--${relatedBoundary}--`);
|
|
361
|
+
return [
|
|
362
|
+
`--${parentBoundary}`,
|
|
363
|
+
`Content-Type: multipart/related; boundary="${relatedBoundary}"`,
|
|
364
|
+
'',
|
|
365
|
+
parts.join(CRLF),
|
|
366
|
+
].join(CRLF);
|
|
367
|
+
};
|
|
368
|
+
if (inlineAttachments.length > 0 && fileAttachments.length === 0) {
|
|
369
|
+
const relatedBoundary = newBoundary('Related');
|
|
370
|
+
const parts = [bodyPart(relatedBoundary)];
|
|
371
|
+
for (const attachment of inlineAttachments) {
|
|
372
|
+
const result = await attachmentPart(attachment, relatedBoundary);
|
|
373
|
+
parts.push(result);
|
|
374
|
+
}
|
|
375
|
+
parts.push(`--${relatedBoundary}--`);
|
|
376
|
+
headers.push(`Content-Type: multipart/related; boundary="${relatedBoundary}"`);
|
|
377
|
+
return Buffer.from([...headers, '', parts.join(CRLF)].join(CRLF), 'utf-8');
|
|
378
|
+
}
|
|
379
|
+
const mixedBoundary = newBoundary('Mixed');
|
|
380
|
+
headers.push(`Content-Type: multipart/mixed; boundary="${mixedBoundary}"`);
|
|
381
|
+
const bodyParts = [];
|
|
382
|
+
if (inlineAttachments.length > 0) {
|
|
383
|
+
const result2 = await buildRelatedPart(mixedBoundary);
|
|
384
|
+
bodyParts.push(result2);
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
bodyParts.push(bodyPart(mixedBoundary));
|
|
388
|
+
}
|
|
389
|
+
for (const attachment of fileAttachments) {
|
|
390
|
+
const result3 = await attachmentPart(attachment, mixedBoundary);
|
|
391
|
+
bodyParts.push(result3);
|
|
392
|
+
}
|
|
393
|
+
bodyParts.push(`--${mixedBoundary}--`);
|
|
394
|
+
return Buffer.from([...headers, '', bodyParts.join(CRLF)].join(CRLF), 'utf-8');
|
|
382
395
|
}
|
|
383
|
-
bodyParts.push(`--${mixedBoundary}--`);
|
|
384
|
-
return Buffer.from([...headers, '', bodyParts.join(CRLF)].join(CRLF), 'utf-8');
|
|
385
396
|
}
|
|
386
397
|
//# sourceMappingURL=mime.utility.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mime.utility.js","sourceRoot":"","sources":["../../../../../src/components/mail/utilities/mime.utility.ts"],"names":[],"mappings":"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","sourcesContent":["import { HTTP } from '@venizia/ignis-helpers/common';\nimport { randomBytes } from 'node:crypto';\nimport { getError } from '@venizia/ignis-helpers/core';\nimport { MailErrorCodes, type IMailAttachment, type IMailMessage } from '../common';\nimport { splitAddressList } from './address.utility';\nimport { readAttachmentContent } from './attachment.utility';\n\nconst CRLF = '\\r\\n';\nconst BASE64_LINE_LENGTH = 76;\n/** RFC 5322 recommends folding well under its own 998-byte hard limit; 78 matches common MTA\n * practice. This is a *soft* preferred width, not a hard requirement - {@link foldHeaderLine}\n * only forces a line break past it when a natural whitespace break point exists nearby. */\nconst MAX_HEADER_LINE_LENGTH = 78;\n/** The real hard requirement: SES rejects any raw-message physical line at or over 1000 bytes.\n * A 10-byte margin below that, and comfortably under RFC 5322's own 998-octet hard limit. Only\n * an unbroken token that would itself cross this line forces {@link foldHeaderLine}'s\n * content-altering hard-wrap fallback - below it, one long line is safer than corrupting an\n * atomic value (a single very long address, an unbroken encoded-word) by splitting it. */\nconst MAX_HEADER_HARD_LIMIT = 990;\n/** RFC 2047 caps one encoded-word (`=?charset?B?...?=` included) at 75 characters. */\nconst ENCODED_WORD_MAX_LENGTH = 75;\n\nfunction isAscii(value: string): boolean {\n for (let index = 0; index < value.length; index++) {\n if (value.charCodeAt(index) > 127) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * RFC 2047 §5(3) requires each encoded-word to carry whole characters - a multi-byte UTF-8\n * sequence split across two encoded-words decodes to mojibake or invalid UTF-8 on either side of\n * the cut. `bytesPerChunk` is a fixed byte budget, not a character-aligned one, so after slicing\n * at that budget we walk the boundary back over any UTF-8 continuation bytes (`10xxxxxx`, i.e.\n * `byte & 0xC0 === 0x80`) until it lands on the leading byte of a character. `bytesPerChunk` is\n * always far larger than the longest UTF-8 sequence (4 bytes), so this walk-back can never\n * consume the whole chunk. Words are joined with a single space - the required separator between\n * adjacent encoded-words, and also a valid header-folding point.\n */\nfunction encodeWord(text: string): string {\n const prefix = '=?UTF-8?B?';\n const suffix = '?=';\n const maxPayloadChars = ENCODED_WORD_MAX_LENGTH - prefix.length - suffix.length;\n const bytesPerChunk = Math.floor(maxPayloadChars / 4) * 3;\n const bytes = Buffer.from(text, 'utf-8');\n const words: string[] = [];\n let offset = 0;\n\n while (offset < bytes.length) {\n let end = Math.min(offset + bytesPerChunk, bytes.length);\n\n while (end < bytes.length && (bytes[end] & 0xc0) === 0x80) {\n end--;\n }\n\n const chunk = bytes.subarray(offset, end);\n words.push(`${prefix}${chunk.toString('base64')}${suffix}`);\n offset = end;\n }\n\n return words.join(' ');\n}\n\n/** Only non-ASCII values pay for RFC 2047 encoding, so a plain-English subject or custom header\n * stays byte-for-byte readable. */\nfunction encodeHeaderValue(value: string): string {\n return isAscii(value) ? value : encodeWord(value);\n}\n\n/**\n * Encodes only the *display-name* portion of `\"Name\" <addr@x.com>` (or bare `Name <addr@x.com>`).\n * The `<addr-spec>` must stay 7-bit ASCII per RFC 5321/5322 and per the SES raw-message\n * requirements - only the free-text name may become an RFC 2047 encoded-word. A bare address with\n * no display name is returned unchanged.\n */\nfunction encodeAddress(address: string): string {\n const match = /^(.*)<([^<>]+)>\\s*$/.exec(address);\n\n if (!match) {\n return address;\n }\n\n const [, rawName, addr] = match;\n const name = rawName.trim().replace(/^\"|\"$/g, '');\n\n if (name.length === 0 || isAscii(name)) {\n return rawName.trim().length > 0 ? `${rawName.trim()} <${addr}>` : `<${addr}>`;\n }\n\n return `${encodeWord(name)} <${addr}>`;\n}\n\n/** Formats a `to`/`cc`/`from`/`replyTo` field as one RFC 5322 address list: splits a\n * comma-separated string or array into individual mailboxes, RFC 2047-encodes each display name,\n * and rejoins with `, ` - the same shape whether the caller passed a string or an array. */\nexport function formatAddressHeader(value: string | string[]): string {\n return splitAddressList(value).map(encodeAddress).join(', ');\n}\n\n/**\n * RFC 5322 §2.2.3 header folding: inserting CRLF immediately before an existing whitespace\n * character splits one logical header value across physical lines without changing its content\n * (the folded whitespace is still there, just after a line break). SES enforces a 1000-byte\n * ceiling per raw-message line; folding at {@link MAX_HEADER_LINE_LENGTH} keeps arbitrarily long\n * recipient lists, subjects, or encoded display names deliverable regardless of how many\n * addresses or how much non-ASCII text a caller supplies - purely by adding more physical\n * lines, never by dropping or truncating anything (verified: rejoining every folded line always\n * reproduces the original value exactly).\n *\n * The one value-altering fallback is deliberately gated on the *hard* limit, not the soft one:\n * an unbroken token (a single very long address, an encoded-word with no adjacent break) that\n * fits under {@link MAX_HEADER_HARD_LIMIT} is left on one long physical line rather than\n * force-broken at the soft width - forcing a break through the middle of an atomic token (an\n * email address has no internal whitespace) would insert a space that was never there,\n * corrupting it. Only a token that would itself violate the real transport limit gets the\n * content-altering hard-wrap - a case that essentially never occurs with real-world input.\n */\nfunction foldHeaderLine(line: string): string {\n const lines: string[] = [];\n let remaining = line;\n\n while (remaining.length > MAX_HEADER_LINE_LENGTH) {\n let breakAt = remaining.lastIndexOf(' ', MAX_HEADER_LINE_LENGTH);\n\n if (breakAt <= 0) {\n // No break within the soft width - look further ahead instead of forcing one through an\n // atomic token. Still bounded by the hard limit, so the physical line this produces can\n // never violate the real transport constraint.\n const nextBreak = remaining.indexOf(' ', MAX_HEADER_LINE_LENGTH);\n breakAt = nextBreak > 0 && nextBreak <= MAX_HEADER_HARD_LIMIT ? nextBreak : -1;\n }\n\n if (breakAt > 0) {\n lines.push(remaining.slice(0, breakAt));\n remaining = remaining.slice(breakAt);\n continue;\n }\n\n if (remaining.length <= MAX_HEADER_HARD_LIMIT) {\n break;\n }\n\n // Truly no natural break within the hard limit - an unbroken token that would itself\n // violate the transport line cap. This is the only path that can alter the value.\n lines.push(remaining.slice(0, MAX_HEADER_HARD_LIMIT));\n remaining = ` ${remaining.slice(MAX_HEADER_HARD_LIMIT)}`;\n }\n\n lines.push(remaining);\n return lines.join(CRLF);\n}\n\n/** MIME requires encoded body lines capped well under the RFC 5322 998-byte hard limit. */\nfunction foldBase64(base64: string): string {\n const lines: string[] = [];\n\n for (let index = 0; index < base64.length; index += BASE64_LINE_LENGTH) {\n lines.push(base64.slice(index, index + BASE64_LINE_LENGTH));\n }\n\n return lines.join(CRLF);\n}\n\n/** UTF-8 text (`text`/`html` bodies) must be encoded before folding - unlike {@link encodeBufferBase64}, which folds bytes that are already final. Two names instead of one so the caller can never feed raw bytes through the wrong path and silently double-encode them. */\nfunction encodeTextBase64(text: string): string {\n return foldBase64(Buffer.from(text, 'utf-8').toString('base64'));\n}\n\n/** Attachment bytes are already final binary content - only folding applies, no UTF-8 re-encode. */\nfunction encodeBufferBase64(buffer: Buffer): string {\n return foldBase64(buffer.toString('base64'));\n}\n\n/** A literal CR/LF/NUL inside a header-bound value would let it break out of its own header line and forge additional headers or smuggle body content - classic email header injection. Every value spliced into a raw header line is checked here first, so an attempt fails loudly instead of silently reaching the wire.\n *\n * `value` is `unknown`, not `string`: `IMailMessage`/`IMailAttachment` both end in\n * `[key: string]: any`, so nothing at the type layer stops a caller (e.g. a controller\n * forwarding a request body) from putting a non-string - notably an array - where a string is\n * declared. `Array.prototype.includes('\\r')` checks element *equality*, not substring, so\n * `['x\\r\\nBcc: evil'].includes('\\r')` is `false` and a naive string-only check would wave a\n * forged header straight through once it is later stringified by a template literal. Requiring\n * `typeof value === 'string'` up front closes that gap for every field that flows through here. */\nfunction assertNoHeaderInjection(opts: { field: string; value: unknown }): void {\n const { field, value } = opts;\n\n if (typeof value !== 'string') {\n throw getError({\n statusCode: HTTP.ResultCodes.RS_5.InternalServerError,\n messageCode: MailErrorCodes.INVALID_CONFIGURATION,\n message: `Invalid Amazon SES message | ${field} must be a string`,\n });\n }\n\n if (!value.includes('\\r') && !value.includes('\\n') && !value.includes('\\0')) {\n return;\n }\n\n throw getError({\n statusCode: HTTP.ResultCodes.RS_5.InternalServerError,\n messageCode: MailErrorCodes.INVALID_CONFIGURATION,\n message: `Invalid Amazon SES message | ${field} contains illegal control characters`,\n });\n}\n\n/** Same guard as {@link assertNoHeaderInjection}, applied to every address in a `to`/`cc`/`bcc`\n * field individually - a string field only, not `[key: string]: any`; validating the raw\n * caller-provided value (before splitting/joining) both types and content-checks each entry. */\nfunction assertNoAddressListInjection(opts: { field: string; value: string | string[] }): void {\n const { field, value } = opts;\n const list = Array.isArray(value) ? value : [value];\n\n for (const item of list) {\n assertNoHeaderInjection({ field, value: item });\n }\n}\n\nfunction extractDomain(address: string | undefined): string {\n const match = address ? /@([^@>\\s]+)>?\\s*$/.exec(address) : null;\n return match ? match[1] : 'localhost';\n}\n\nfunction buildHeaders(message: IMailMessage): string[] {\n const headers: string[] = [];\n\n if (message.from) {\n assertNoAddressListInjection({ field: 'from', value: message.from });\n headers.push(foldHeaderLine(`From: ${formatAddressHeader(message.from)}`));\n }\n\n if (message.to) {\n assertNoAddressListInjection({ field: 'to', value: message.to });\n headers.push(foldHeaderLine(`To: ${formatAddressHeader(message.to)}`));\n }\n\n if (message.cc) {\n assertNoAddressListInjection({ field: 'cc', value: message.cc });\n headers.push(foldHeaderLine(`Cc: ${formatAddressHeader(message.cc)}`));\n }\n\n if (message.replyTo) {\n assertNoAddressListInjection({ field: 'replyTo', value: message.replyTo });\n headers.push(foldHeaderLine(`Reply-To: ${formatAddressHeader(message.replyTo)}`));\n }\n\n // Checked, never emitted: a `Bcc:` header would disclose the blind recipients, so they travel as\n // `Destination.BccAddresses`. Without this, `bcc` is the one address field that fails at AWS.\n if (message.bcc) {\n assertNoAddressListInjection({ field: 'bcc', value: message.bcc });\n }\n\n assertNoHeaderInjection({ field: 'subject', value: message.subject });\n headers.push(foldHeaderLine(`Subject: ${encodeHeaderValue(message.subject)}`));\n // RFC 5322 requires both on every message; SES's raw-message parser does not synthesize them,\n // so an omission here is an omission on the wire (some receiving MTAs reject a `Date`-less\n // message outright, and a missing `Message-ID` breaks threading and dedup).\n headers.push(`Date: ${new Date().toUTCString()}`);\n headers.push(`Message-ID: <${randomBytes(16).toString('hex')}@${extractDomain(message.from)}>`);\n headers.push('MIME-Version: 1.0');\n\n if (message.headers) {\n const messageHeadersEntries = Object.entries(message.headers);\n for (const [key, value] of messageHeadersEntries) {\n assertNoHeaderInjection({ field: `headers.${key}`, value: key });\n assertNoHeaderInjection({ field: `headers.${key}`, value });\n headers.push(foldHeaderLine(`${key}: ${encodeHeaderValue(value)}`));\n }\n }\n\n return headers;\n}\n\n/** One text/plain or text/html MIME part. `boundary` omitted only for the single-part, no-attachment case, where the content type belongs on the top-level headers instead of a nested part. */\nfunction textPart(\n boundary: string,\n contentType: 'text/plain' | 'text/html',\n content: string,\n): string {\n return [\n `--${boundary}`,\n `Content-Type: ${contentType}; charset=UTF-8`,\n 'Content-Transfer-Encoding: base64',\n '',\n encodeTextBase64(content),\n ].join(CRLF);\n}\n\nfunction escapeQuotedString(value: string): string {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n}\n\n/** RFC 2231 percent-encoding for the `filename*=`/`name*=` extended parameter form. */\nfunction encodeRfc2231(value: string): string {\n return encodeURIComponent(value).replace(\n /[!'()*]/g,\n (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`,\n );\n}\n\n/**\n * Builds the `; name=\"...\"`/`; filename=\"...\"` (and, for non-ASCII names, the RFC 2231\n * `; filename*=UTF-8''...` extended form) suffix for a Content-Type/Content-Disposition line.\n * ASCII names are still quoted-string escaped - an unescaped `\"` or `\\` in a filename would\n * terminate the quoted string early and let the rest of the value merge into a sibling MIME\n * parameter, corrupting the part header for every downstream reader.\n */\nfunction attachmentNameParams(attribute: 'name' | 'filename', value: string): string {\n if (isAscii(value)) {\n return `; ${attribute}=\"${escapeQuotedString(value)}\"`;\n }\n\n const asciiFallback = value.replace(/[^\\x20-\\x7E]/g, '_');\n return (\n `; ${attribute}=\"${escapeQuotedString(asciiFallback)}\"` +\n `; ${attribute}*=UTF-8''${encodeRfc2231(value)}`\n );\n}\n\nasync function attachmentPart(attachment: IMailAttachment, boundary: string): Promise<string> {\n // No root here: a `path` that did not go through `resolveMailAttachments` is refused, never read.\n const content = await readAttachmentContent({ attachment });\n const contentType = attachment.contentType ?? 'application/octet-stream';\n assertNoHeaderInjection({ field: 'attachment.contentType', value: contentType });\n\n if (attachment.filename) {\n assertNoHeaderInjection({ field: 'attachment.filename', value: attachment.filename });\n }\n\n if (attachment.cid) {\n assertNoHeaderInjection({ field: 'attachment.cid', value: attachment.cid });\n }\n\n const nameAttribute = attachment.filename\n ? attachmentNameParams('name', attachment.filename)\n : '';\n const filenameAttribute = attachment.filename\n ? attachmentNameParams('filename', attachment.filename)\n : '';\n const disposition = attachment.cid ? 'inline' : 'attachment';\n\n const headerLines = [\n `--${boundary}`,\n foldHeaderLine(`Content-Type: ${contentType}${nameAttribute}`),\n 'Content-Transfer-Encoding: base64',\n foldHeaderLine(`Content-Disposition: ${disposition}${filenameAttribute}`),\n ];\n\n if (attachment.cid) {\n headerLines.push(`Content-ID: <${attachment.cid}>`);\n }\n\n return [...headerLines, '', encodeBufferBase64(content)].join(CRLF);\n}\n\n/** Every boundary is generated the same way - a fixed prefix plus random hex - so no two nested multipart sections in one message can ever collide. */\nfunction newBoundary(label: string): string {\n return `----=_${label}_${randomBytes(12).toString('hex')}`;\n}\n\n/**\n * Builds a raw RFC 822 message with zero external dependencies - Amazon SES's `SendEmailCommand`\n * `Content.Simple` shape has no attachment field, so attachments require a raw MIME body. Written\n * by hand instead of depending on `nodemailer`'s `MailComposer` so the `ses` provider never needs\n * the `nodemailer` peer installed at all.\n *\n * Inline (`cid`) attachments nest under `multipart/related` next to the body - a mail client\n * only renders `cid:` references inline when they share a `multipart/related` parent with the\n * body; under `multipart/mixed` they show up as ordinary downloadable attachments instead. Plain\n * file attachments (no `cid`) stay under `multipart/mixed`, alongside the body or the\n * `multipart/related` group when both kinds are present.\n */\nexport async function buildRawMimeMessage(message: IMailMessage): Promise<Buffer> {\n const headers = buildHeaders(message);\n const attachments = message.attachments ?? [];\n const inlineAttachments = attachments.filter((attachment) => Boolean(attachment.cid));\n const fileAttachments = attachments.filter((attachment) => !attachment.cid);\n const hasBothBodies = Boolean(message.text) && Boolean(message.html);\n const singleContentType = message.html ? ('text/html' as const) : ('text/plain' as const);\n const singleContent = message.html ?? message.text ?? '';\n\n const bodyPart = (boundary: string): string => {\n if (!hasBothBodies) {\n return textPart(boundary, singleContentType, singleContent);\n }\n\n const altBoundary = newBoundary('Alt');\n return [\n `--${boundary}`,\n `Content-Type: multipart/alternative; boundary=\"${altBoundary}\"`,\n '',\n [\n textPart(altBoundary, 'text/plain', message.text ?? ''),\n textPart(altBoundary, 'text/html', message.html ?? ''),\n `--${altBoundary}--`,\n ].join(CRLF),\n ].join(CRLF);\n };\n\n if (attachments.length === 0 && !hasBothBodies) {\n headers.push(`Content-Type: ${singleContentType}; charset=UTF-8`);\n headers.push('Content-Transfer-Encoding: base64');\n return Buffer.from([...headers, '', encodeTextBase64(singleContent)].join(CRLF), 'utf-8');\n }\n\n if (attachments.length === 0) {\n const boundary = newBoundary('Alt');\n headers.push(`Content-Type: multipart/alternative; boundary=\"${boundary}\"`);\n const parts = [\n textPart(boundary, 'text/plain', message.text ?? ''),\n textPart(boundary, 'text/html', message.html ?? ''),\n `--${boundary}--`,\n ];\n return Buffer.from([...headers, '', parts.join(CRLF)].join(CRLF), 'utf-8');\n }\n\n const buildRelatedPart = async (parentBoundary: string): Promise<string> => {\n const relatedBoundary = newBoundary('Related');\n const parts = [bodyPart(relatedBoundary)];\n\n for (const attachment of inlineAttachments) {\n parts.push(await attachmentPart(attachment, relatedBoundary));\n }\n\n parts.push(`--${relatedBoundary}--`);\n\n return [\n `--${parentBoundary}`,\n `Content-Type: multipart/related; boundary=\"${relatedBoundary}\"`,\n '',\n parts.join(CRLF),\n ].join(CRLF);\n };\n\n if (inlineAttachments.length > 0 && fileAttachments.length === 0) {\n const relatedBoundary = newBoundary('Related');\n const parts = [bodyPart(relatedBoundary)];\n\n for (const attachment of inlineAttachments) {\n parts.push(await attachmentPart(attachment, relatedBoundary));\n }\n\n parts.push(`--${relatedBoundary}--`);\n headers.push(`Content-Type: multipart/related; boundary=\"${relatedBoundary}\"`);\n return Buffer.from([...headers, '', parts.join(CRLF)].join(CRLF), 'utf-8');\n }\n\n const mixedBoundary = newBoundary('Mixed');\n headers.push(`Content-Type: multipart/mixed; boundary=\"${mixedBoundary}\"`);\n\n const bodyParts: string[] = [];\n\n if (inlineAttachments.length > 0) {\n bodyParts.push(await buildRelatedPart(mixedBoundary));\n } else {\n bodyParts.push(bodyPart(mixedBoundary));\n }\n\n for (const attachment of fileAttachments) {\n bodyParts.push(await attachmentPart(attachment, mixedBoundary));\n }\n\n bodyParts.push(`--${mixedBoundary}--`);\n\n return Buffer.from([...headers, '', bodyParts.join(CRLF)].join(CRLF), 'utf-8');\n}\n"]}
|
|
1
|
+
{"version":3,"file":"mime.utility.js","sourceRoot":"","sources":["../../../../../src/components/mail/utilities/mime.utility.ts"],"names":[],"mappings":"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","sourcesContent":["import { HTTP } from '@venizia/ignis-helpers/common';\nimport { randomBytes } from 'node:crypto';\nimport { getError } from '@venizia/ignis-helpers/core';\nimport { MailErrorCodes, type IMailAttachment, type IMailMessage } from '../common';\nimport { MailAddressUtility } from './address.utility';\nimport { MailAttachmentUtility } from './attachment.utility';\n\nconst CRLF = '\\r\\n';\nconst BASE64_LINE_LENGTH = 76;\n/** RFC 5322 recommends folding well under its own 998-byte hard limit; 78 matches common MTA\n * practice. This is a *soft* preferred width, not a hard requirement - {@link foldHeaderLine}\n * only forces a line break past it when a natural whitespace break point exists nearby. */\nconst MAX_HEADER_LINE_LENGTH = 78;\n/** The real hard requirement: SES rejects any raw-message physical line at or over 1000 bytes.\n * A 10-byte margin below that, and comfortably under RFC 5322's own 998-octet hard limit. Only\n * an unbroken token that would itself cross this line forces {@link foldHeaderLine}'s\n * content-altering hard-wrap fallback - below it, one long line is safer than corrupting an\n * atomic value (a single very long address, an unbroken encoded-word) by splitting it. */\nconst MAX_HEADER_HARD_LIMIT = 990;\n/** RFC 2047 caps one encoded-word (`=?charset?B?...?=` included) at 75 characters. */\nconst ENCODED_WORD_MAX_LENGTH = 75;\n\nfunction isAscii(value: string): boolean {\n for (let index = 0; index < value.length; index++) {\n if (value.charCodeAt(index) > 127) {\n return false;\n }\n }\n\n return true;\n}\n\n/**\n * RFC 2047 §5(3) requires each encoded-word to carry whole characters - a multi-byte UTF-8\n * sequence split across two encoded-words decodes to mojibake or invalid UTF-8 on either side of\n * the cut. `bytesPerChunk` is a fixed byte budget, not a character-aligned one, so after slicing\n * at that budget we walk the boundary back over any UTF-8 continuation bytes (`10xxxxxx`, i.e.\n * `byte & 0xC0 === 0x80`) until it lands on the leading byte of a character. `bytesPerChunk` is\n * always far larger than the longest UTF-8 sequence (4 bytes), so this walk-back can never\n * consume the whole chunk. Words are joined with a single space - the required separator between\n * adjacent encoded-words, and also a valid header-folding point.\n */\nfunction encodeWord(text: string): string {\n const prefix = '=?UTF-8?B?';\n const suffix = '?=';\n const maxPayloadChars = ENCODED_WORD_MAX_LENGTH - prefix.length - suffix.length;\n const bytesPerChunk = Math.floor(maxPayloadChars / 4) * 3;\n const bytes = Buffer.from(text, 'utf-8');\n const words: string[] = [];\n let offset = 0;\n\n while (offset < bytes.length) {\n let end = Math.min(offset + bytesPerChunk, bytes.length);\n\n while (end < bytes.length && (bytes[end] & 0xc0) === 0x80) {\n end--;\n }\n\n const chunk = bytes.subarray(offset, end);\n words.push(`${prefix}${chunk.toString('base64')}${suffix}`);\n offset = end;\n }\n\n return words.join(' ');\n}\n\n/** Only non-ASCII values pay for RFC 2047 encoding, so a plain-English subject or custom header\n * stays byte-for-byte readable. */\nfunction encodeHeaderValue(value: string): string {\n return isAscii(value) ? value : encodeWord(value);\n}\n\n/**\n * Encodes only the *display-name* portion of `\"Name\" <addr@x.com>` (or bare `Name <addr@x.com>`).\n * The `<addr-spec>` must stay 7-bit ASCII per RFC 5321/5322 and per the SES raw-message\n * requirements - only the free-text name may become an RFC 2047 encoded-word. A bare address with\n * no display name is returned unchanged.\n */\nfunction encodeAddress(address: string): string {\n const match = /^(.*)<([^<>]+)>\\s*$/.exec(address);\n\n if (!match) {\n return address;\n }\n\n const [, rawName, addr] = match;\n const name = rawName.trim().replace(/^\"|\"$/g, '');\n\n if (name.length === 0 || isAscii(name)) {\n return rawName.trim().length > 0 ? `${rawName.trim()} <${addr}>` : `<${addr}>`;\n }\n\n return `${encodeWord(name)} <${addr}>`;\n}\n\n/** The body of `MailMimeUtility.formatAddressHeader`, callable from the header builder. */\nfunction formatAddressList(value: string | string[]): string {\n return MailAddressUtility.splitList({ value }).map(encodeAddress).join(', ');\n}\n\n/**\n * RFC 5322 §2.2.3 header folding: inserting CRLF immediately before an existing whitespace\n * character splits one logical header value across physical lines without changing its content\n * (the folded whitespace is still there, just after a line break). SES enforces a 1000-byte\n * ceiling per raw-message line; folding at {@link MAX_HEADER_LINE_LENGTH} keeps arbitrarily long\n * recipient lists, subjects, or encoded display names deliverable regardless of how many\n * addresses or how much non-ASCII text a caller supplies - purely by adding more physical\n * lines, never by dropping or truncating anything (verified: rejoining every folded line always\n * reproduces the original value exactly).\n *\n * The one value-altering fallback is deliberately gated on the *hard* limit, not the soft one:\n * an unbroken token (a single very long address, an encoded-word with no adjacent break) that\n * fits under {@link MAX_HEADER_HARD_LIMIT} is left on one long physical line rather than\n * force-broken at the soft width - forcing a break through the middle of an atomic token (an\n * email address has no internal whitespace) would insert a space that was never there,\n * corrupting it. Only a token that would itself violate the real transport limit gets the\n * content-altering hard-wrap - a case that essentially never occurs with real-world input.\n */\nfunction foldHeaderLine(line: string): string {\n const lines: string[] = [];\n let remaining = line;\n\n while (remaining.length > MAX_HEADER_LINE_LENGTH) {\n let breakAt = remaining.lastIndexOf(' ', MAX_HEADER_LINE_LENGTH);\n\n if (breakAt <= 0) {\n // No break within the soft width - look further ahead instead of forcing one through an\n // atomic token. Still bounded by the hard limit, so the physical line this produces can\n // never violate the real transport constraint.\n const nextBreak = remaining.indexOf(' ', MAX_HEADER_LINE_LENGTH);\n breakAt = nextBreak > 0 && nextBreak <= MAX_HEADER_HARD_LIMIT ? nextBreak : -1;\n }\n\n if (breakAt > 0) {\n lines.push(remaining.slice(0, breakAt));\n remaining = remaining.slice(breakAt);\n continue;\n }\n\n if (remaining.length <= MAX_HEADER_HARD_LIMIT) {\n break;\n }\n\n // Truly no natural break within the hard limit - an unbroken token that would itself\n // violate the transport line cap. This is the only path that can alter the value.\n lines.push(remaining.slice(0, MAX_HEADER_HARD_LIMIT));\n remaining = ` ${remaining.slice(MAX_HEADER_HARD_LIMIT)}`;\n }\n\n lines.push(remaining);\n return lines.join(CRLF);\n}\n\n/** MIME requires encoded body lines capped well under the RFC 5322 998-byte hard limit. */\nfunction foldBase64(base64: string): string {\n const lines: string[] = [];\n\n for (let index = 0; index < base64.length; index += BASE64_LINE_LENGTH) {\n lines.push(base64.slice(index, index + BASE64_LINE_LENGTH));\n }\n\n return lines.join(CRLF);\n}\n\n/** UTF-8 text (`text`/`html` bodies) must be encoded before folding - unlike {@link encodeBufferBase64}, which folds bytes that are already final. Two names instead of one so the caller can never feed raw bytes through the wrong path and silently double-encode them. */\nfunction encodeTextBase64(text: string): string {\n return foldBase64(Buffer.from(text, 'utf-8').toString('base64'));\n}\n\n/** Attachment bytes are already final binary content - only folding applies, no UTF-8 re-encode. */\nfunction encodeBufferBase64(buffer: Buffer): string {\n return foldBase64(buffer.toString('base64'));\n}\n\n/** A literal CR/LF/NUL inside a header-bound value would let it break out of its own header line and forge additional headers or smuggle body content - classic email header injection. Every value spliced into a raw header line is checked here first, so an attempt fails loudly instead of silently reaching the wire.\n *\n * `value` is `unknown`, not `string`: `IMailMessage`/`IMailAttachment` both end in\n * `[key: string]: any`, so nothing at the type layer stops a caller (e.g. a controller\n * forwarding a request body) from putting a non-string - notably an array - where a string is\n * declared. `Array.prototype.includes('\\r')` checks element *equality*, not substring, so\n * `['x\\r\\nBcc: evil'].includes('\\r')` is `false` and a naive string-only check would wave a\n * forged header straight through once it is later stringified by a template literal. Requiring\n * `typeof value === 'string'` up front closes that gap for every field that flows through here. */\nfunction assertNoHeaderInjection(opts: { field: string; value: unknown }): void {\n const { field, value } = opts;\n\n if (typeof value !== 'string') {\n throw getError({\n statusCode: HTTP.ResultCodes.RS_5.InternalServerError,\n messageCode: MailErrorCodes.INVALID_CONFIGURATION,\n message: `Invalid Amazon SES message | ${field} must be a string`,\n });\n }\n\n if (!value.includes('\\r') && !value.includes('\\n') && !value.includes('\\0')) {\n return;\n }\n\n throw getError({\n statusCode: HTTP.ResultCodes.RS_5.InternalServerError,\n messageCode: MailErrorCodes.INVALID_CONFIGURATION,\n message: `Invalid Amazon SES message | ${field} contains illegal control characters`,\n });\n}\n\n/** Same guard as {@link assertNoHeaderInjection}, applied to every address in a `to`/`cc`/`bcc`\n * field individually - a string field only, not `[key: string]: any`; validating the raw\n * caller-provided value (before splitting/joining) both types and content-checks each entry. */\nfunction assertNoAddressListInjection(opts: { field: string; value: string | string[] }): void {\n const { field, value } = opts;\n const list = Array.isArray(value) ? value : [value];\n\n for (const item of list) {\n assertNoHeaderInjection({ field, value: item });\n }\n}\n\nfunction extractDomain(address: string | undefined): string {\n const match = address ? /@([^@>\\s]+)>?\\s*$/.exec(address) : null;\n return match ? match[1] : 'localhost';\n}\n\nfunction buildHeaders(message: IMailMessage): string[] {\n const headers: string[] = [];\n\n if (message.from) {\n assertNoAddressListInjection({ field: 'from', value: message.from });\n headers.push(foldHeaderLine(`From: ${formatAddressList(message.from)}`));\n }\n\n if (message.to) {\n assertNoAddressListInjection({ field: 'to', value: message.to });\n headers.push(foldHeaderLine(`To: ${formatAddressList(message.to)}`));\n }\n\n if (message.cc) {\n assertNoAddressListInjection({ field: 'cc', value: message.cc });\n headers.push(foldHeaderLine(`Cc: ${formatAddressList(message.cc)}`));\n }\n\n if (message.replyTo) {\n assertNoAddressListInjection({ field: 'replyTo', value: message.replyTo });\n headers.push(foldHeaderLine(`Reply-To: ${formatAddressList(message.replyTo)}`));\n }\n\n // Checked, never emitted: a `Bcc:` header would disclose the blind recipients, so they travel as\n // `Destination.BccAddresses`. Without this, `bcc` is the one address field that fails at AWS.\n if (message.bcc) {\n assertNoAddressListInjection({ field: 'bcc', value: message.bcc });\n }\n\n assertNoHeaderInjection({ field: 'subject', value: message.subject });\n headers.push(foldHeaderLine(`Subject: ${encodeHeaderValue(message.subject)}`));\n // RFC 5322 requires both on every message; SES's raw-message parser does not synthesize them,\n // so an omission here is an omission on the wire (some receiving MTAs reject a `Date`-less\n // message outright, and a missing `Message-ID` breaks threading and dedup).\n headers.push(`Date: ${new Date().toUTCString()}`);\n headers.push(`Message-ID: <${randomBytes(16).toString('hex')}@${extractDomain(message.from)}>`);\n headers.push('MIME-Version: 1.0');\n\n if (message.headers) {\n const messageHeadersEntries = Object.entries(message.headers);\n for (const [key, value] of messageHeadersEntries) {\n assertNoHeaderInjection({ field: `headers.${key}`, value: key });\n assertNoHeaderInjection({ field: `headers.${key}`, value });\n headers.push(foldHeaderLine(`${key}: ${encodeHeaderValue(value)}`));\n }\n }\n\n return headers;\n}\n\n/** One text/plain or text/html MIME part. `boundary` omitted only for the single-part, no-attachment case, where the content type belongs on the top-level headers instead of a nested part. */\nfunction textPart(\n boundary: string,\n contentType: 'text/plain' | 'text/html',\n content: string,\n): string {\n return [\n `--${boundary}`,\n `Content-Type: ${contentType}; charset=UTF-8`,\n 'Content-Transfer-Encoding: base64',\n '',\n encodeTextBase64(content),\n ].join(CRLF);\n}\n\nfunction escapeQuotedString(value: string): string {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n}\n\n/** RFC 2231 percent-encoding for the `filename*=`/`name*=` extended parameter form. */\nfunction encodeRfc2231(value: string): string {\n return encodeURIComponent(value).replace(\n /[!'()*]/g,\n (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`,\n );\n}\n\n/**\n * Builds the `; name=\"...\"`/`; filename=\"...\"` (and, for non-ASCII names, the RFC 2231\n * `; filename*=UTF-8''...` extended form) suffix for a Content-Type/Content-Disposition line.\n * ASCII names are still quoted-string escaped - an unescaped `\"` or `\\` in a filename would\n * terminate the quoted string early and let the rest of the value merge into a sibling MIME\n * parameter, corrupting the part header for every downstream reader.\n */\nfunction attachmentNameParams(attribute: 'name' | 'filename', value: string): string {\n if (isAscii(value)) {\n return `; ${attribute}=\"${escapeQuotedString(value)}\"`;\n }\n\n const asciiFallback = value.replace(/[^\\x20-\\x7E]/g, '_');\n return (\n `; ${attribute}=\"${escapeQuotedString(asciiFallback)}\"` +\n `; ${attribute}*=UTF-8''${encodeRfc2231(value)}`\n );\n}\n\nasync function attachmentPart(attachment: IMailAttachment, boundary: string): Promise<string> {\n // No root here: a `path` that did not go through `MailAttachmentUtility.resolve` is refused, never read.\n const content = await MailAttachmentUtility.readContent({ attachment });\n const contentType = attachment.contentType ?? 'application/octet-stream';\n assertNoHeaderInjection({ field: 'attachment.contentType', value: contentType });\n\n if (attachment.filename) {\n assertNoHeaderInjection({ field: 'attachment.filename', value: attachment.filename });\n }\n\n if (attachment.cid) {\n assertNoHeaderInjection({ field: 'attachment.cid', value: attachment.cid });\n }\n\n const nameAttribute = attachment.filename\n ? attachmentNameParams('name', attachment.filename)\n : '';\n const filenameAttribute = attachment.filename\n ? attachmentNameParams('filename', attachment.filename)\n : '';\n const disposition = attachment.cid ? 'inline' : 'attachment';\n\n const headerLines = [\n `--${boundary}`,\n foldHeaderLine(`Content-Type: ${contentType}${nameAttribute}`),\n 'Content-Transfer-Encoding: base64',\n foldHeaderLine(`Content-Disposition: ${disposition}${filenameAttribute}`),\n ];\n\n if (attachment.cid) {\n headerLines.push(`Content-ID: <${attachment.cid}>`);\n }\n\n return [...headerLines, '', encodeBufferBase64(content)].join(CRLF);\n}\n\n/** Every boundary is generated the same way - a fixed prefix plus random hex - so no two nested multipart sections in one message can ever collide. */\nfunction newBoundary(label: string): string {\n return `----=_${label}_${randomBytes(12).toString('hex')}`;\n}\n\nexport class MailMimeUtility {\n /** Formats a `to`/`cc`/`from`/`replyTo` field as one RFC 5322 address list: splits a\n * comma-separated string or array into individual mailboxes, RFC 2047-encodes each display name,\n * and rejoins with `, ` - the same shape whether the caller passed a string or an array. */\n static formatAddressHeader(opts: { value: string | string[] }): string {\n return formatAddressList(opts.value);\n }\n\n /**\n * Builds a raw RFC 822 message with zero external dependencies - Amazon SES's `SendEmailCommand`\n * `Content.Simple` shape has no attachment field, so attachments require a raw MIME body. Written\n * by hand instead of depending on `nodemailer`'s `MailComposer` so the `ses` provider never needs\n * the `nodemailer` peer installed at all.\n *\n * Inline (`cid`) attachments nest under `multipart/related` next to the body - a mail client\n * only renders `cid:` references inline when they share a `multipart/related` parent with the\n * body; under `multipart/mixed` they show up as ordinary downloadable attachments instead. Plain\n * file attachments (no `cid`) stay under `multipart/mixed`, alongside the body or the\n * `multipart/related` group when both kinds are present.\n */\n static async buildRawMessage(opts: { message: IMailMessage }): Promise<Buffer> {\n const { message } = opts;\n const headers = buildHeaders(message);\n const attachments = message.attachments ?? [];\n const inlineAttachments = attachments.filter((attachment) => Boolean(attachment.cid));\n const fileAttachments = attachments.filter((attachment) => !attachment.cid);\n const hasBothBodies = Boolean(message.text) && Boolean(message.html);\n const singleContentType = message.html ? ('text/html' as const) : ('text/plain' as const);\n const singleContent = message.html ?? message.text ?? '';\n\n const bodyPart = (boundary: string): string => {\n if (!hasBothBodies) {\n return textPart(boundary, singleContentType, singleContent);\n }\n\n const altBoundary = newBoundary('Alt');\n return [\n `--${boundary}`,\n `Content-Type: multipart/alternative; boundary=\"${altBoundary}\"`,\n '',\n [\n textPart(altBoundary, 'text/plain', message.text ?? ''),\n textPart(altBoundary, 'text/html', message.html ?? ''),\n `--${altBoundary}--`,\n ].join(CRLF),\n ].join(CRLF);\n };\n\n if (attachments.length === 0 && !hasBothBodies) {\n headers.push(`Content-Type: ${singleContentType}; charset=UTF-8`);\n headers.push('Content-Transfer-Encoding: base64');\n return Buffer.from([...headers, '', encodeTextBase64(singleContent)].join(CRLF), 'utf-8');\n }\n\n if (attachments.length === 0) {\n const boundary = newBoundary('Alt');\n headers.push(`Content-Type: multipart/alternative; boundary=\"${boundary}\"`);\n const parts = [\n textPart(boundary, 'text/plain', message.text ?? ''),\n textPart(boundary, 'text/html', message.html ?? ''),\n `--${boundary}--`,\n ];\n return Buffer.from([...headers, '', parts.join(CRLF)].join(CRLF), 'utf-8');\n }\n\n const buildRelatedPart = async (parentBoundary: string): Promise<string> => {\n const relatedBoundary = newBoundary('Related');\n const parts = [bodyPart(relatedBoundary)];\n\n for (const attachment of inlineAttachments) {\n const result = await attachmentPart(attachment, relatedBoundary);\n parts.push(result);\n }\n\n parts.push(`--${relatedBoundary}--`);\n\n return [\n `--${parentBoundary}`,\n `Content-Type: multipart/related; boundary=\"${relatedBoundary}\"`,\n '',\n parts.join(CRLF),\n ].join(CRLF);\n };\n\n if (inlineAttachments.length > 0 && fileAttachments.length === 0) {\n const relatedBoundary = newBoundary('Related');\n const parts = [bodyPart(relatedBoundary)];\n\n for (const attachment of inlineAttachments) {\n const result = await attachmentPart(attachment, relatedBoundary);\n parts.push(result);\n }\n\n parts.push(`--${relatedBoundary}--`);\n headers.push(`Content-Type: multipart/related; boundary=\"${relatedBoundary}\"`);\n return Buffer.from([...headers, '', parts.join(CRLF)].join(CRLF), 'utf-8');\n }\n\n const mixedBoundary = newBoundary('Mixed');\n headers.push(`Content-Type: multipart/mixed; boundary=\"${mixedBoundary}\"`);\n\n const bodyParts: string[] = [];\n\n if (inlineAttachments.length > 0) {\n const result2 = await buildRelatedPart(mixedBoundary);\n bodyParts.push(result2);\n } else {\n bodyParts.push(bodyPart(mixedBoundary));\n }\n\n for (const attachment of fileAttachments) {\n const result3 = await attachmentPart(attachment, mixedBoundary);\n bodyParts.push(result3);\n }\n\n bodyParts.push(`--${mixedBoundary}--`);\n\n return Buffer.from([...headers, '', bodyParts.join(CRLF)].join(CRLF), 'utf-8');\n }\n}\n"]}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
-
export declare
|
|
2
|
-
|
|
1
|
+
export declare class MailVerificationUtility {
|
|
2
|
+
static getExpiryTime(opts: {
|
|
3
|
+
minutes: number;
|
|
4
|
+
}): Date;
|
|
5
|
+
static getExpiryTimeInHours(opts: {
|
|
6
|
+
hours: number;
|
|
7
|
+
}): Date;
|
|
8
|
+
}
|
|
3
9
|
//# sourceMappingURL=verification.utility.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verification.utility.d.ts","sourceRoot":"","sources":["../../../../../src/components/mail/utilities/verification.utility.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"verification.utility.d.ts","sourceRoot":"","sources":["../../../../../src/components/mail/utilities/verification.utility.ts"],"names":[],"mappings":"AAAA,qBAAa,uBAAuB;IAClC,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAIrD,MAAM,CAAC,oBAAoB,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;CAG3D"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
export class MailVerificationUtility {
|
|
2
|
+
static getExpiryTime(opts) {
|
|
3
|
+
return new Date(Date.now() + opts.minutes * 60 * 1000);
|
|
4
|
+
}
|
|
5
|
+
static getExpiryTimeInHours(opts) {
|
|
6
|
+
return new Date(Date.now() + opts.hours * 60 * 60 * 1000);
|
|
7
|
+
}
|
|
6
8
|
}
|
|
7
9
|
//# sourceMappingURL=verification.utility.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verification.utility.js","sourceRoot":"","sources":["../../../../../src/components/mail/utilities/verification.utility.ts"],"names":[],"mappings":"AAAA,MAAM,
|
|
1
|
+
{"version":3,"file":"verification.utility.js","sourceRoot":"","sources":["../../../../../src/components/mail/utilities/verification.utility.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,uBAAuB;IAClC,MAAM,CAAC,aAAa,CAAC,IAAyB;QAC5C,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,CAAC,oBAAoB,CAAC,IAAuB;QACjD,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAC5D,CAAC;CACF","sourcesContent":["export class MailVerificationUtility {\n static getExpiryTime(opts: { minutes: number }): Date {\n return new Date(Date.now() + opts.minutes * 60 * 1000);\n }\n\n static getExpiryTimeInHours(opts: { hours: number }): Date {\n return new Date(Date.now() + opts.hours * 60 * 60 * 1000);\n }\n}\n"]}
|
|
@@ -20,7 +20,7 @@ import { HTTP, RuntimeModules } from '@venizia/ignis-helpers/common';
|
|
|
20
20
|
import { isRedisHelper } from '@venizia/ignis-helpers';
|
|
21
21
|
import { SocketIOServerHelper, } from '@venizia/ignis-helpers/socket-io';
|
|
22
22
|
import { SocketIOBindingKeys, DEFAULT_SERVER_OPTIONS, } from './common/index.js';
|
|
23
|
-
import {
|
|
23
|
+
import { SocketIOBunHandler } from './handlers/index.js';
|
|
24
24
|
let SocketIOComponent = SocketIOComponent_1 = class SocketIOComponent extends BaseComponent {
|
|
25
25
|
constructor(application) {
|
|
26
26
|
super({
|
|
@@ -83,7 +83,7 @@ let SocketIOComponent = SocketIOComponent_1 = class SocketIOComponent extends Ba
|
|
|
83
83
|
this.application.registerPostStartHook({
|
|
84
84
|
identifier: 'socket-io-initialize',
|
|
85
85
|
hook: async () => {
|
|
86
|
-
const { engine, engineHandler } = await
|
|
86
|
+
const { engine, engineHandler } = await SocketIOBunHandler.createEngine({ serverOptions });
|
|
87
87
|
const socketIOHelper = new SocketIOServerHelper({
|
|
88
88
|
runtime: RuntimeModules.BUN,
|
|
89
89
|
identifier: serverOptions.identifier,
|
|
@@ -102,7 +102,7 @@ let SocketIOComponent = SocketIOComponent_1 = class SocketIOComponent extends Ba
|
|
|
102
102
|
const honoServer = this.application.getServer();
|
|
103
103
|
const enginePath = serverOptions.path ?? '/socket.io/';
|
|
104
104
|
serverInstance.reload({
|
|
105
|
-
fetch:
|
|
105
|
+
fetch: SocketIOBunHandler.createFetchHandler({ engine, enginePath, honoServer }),
|
|
106
106
|
websocket: engineHandler.websocket,
|
|
107
107
|
});
|
|
108
108
|
logger.info('SocketIO initialized for Bun runtime');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component.js","sourceRoot":"","sources":["../../../../src/components/socket-io/component.ts"],"names":[],"mappings":"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","sourcesContent":["import { type BaseApplication, type TBunServerInstance } from '@/base/applications';\nimport { BaseComponent } from '@venizia/ignis-kernel';\nimport { inject } from '@/base/metadata';\nimport { CoreBindings } from '@venizia/ignis-kernel';\nimport { Binding } from '@venizia/ignis-kernel';\nimport { getError } from '@venizia/ignis-helpers/core';\nimport { HTTP, RuntimeModules, type ValueOrPromise } from '@venizia/ignis-helpers/common';\nimport { type AbstractRedisHelper, isRedisHelper } from '@venizia/ignis-helpers';\nimport {\n SocketIOServerHelper,\n type TSocketIOAuthenticateFn,\n type TSocketIOClientConnectedFn,\n type TSocketIOValidateRoomFn,\n} from '@venizia/ignis-helpers/socket-io';\nimport type { ServerOptions } from 'socket.io';\nimport {\n SocketIOBindingKeys,\n DEFAULT_SERVER_OPTIONS,\n type IResolvedBindings,\n type IServerOptions,\n} from './common';\nimport { createBunEngine, createBunFetchHandler } from './handlers';\n\nexport class SocketIOComponent extends BaseComponent {\n protected serverOptions: Partial<IServerOptions>;\n\n constructor(\n @inject({ key: CoreBindings.APPLICATION_INSTANCE }) private application: BaseApplication,\n ) {\n super({\n scope: SocketIOComponent.name,\n initDefault: { enable: true, container: application },\n bindings: {\n [SocketIOBindingKeys.SERVER_OPTIONS]: Binding.bind<Partial<ServerOptions>>({\n key: SocketIOBindingKeys.SERVER_OPTIONS,\n }).toValue(DEFAULT_SERVER_OPTIONS),\n\n [SocketIOBindingKeys.REDIS_CONNECTION]: Binding.bind<AbstractRedisHelper | null>({\n key: SocketIOBindingKeys.REDIS_CONNECTION,\n }).toValue(null),\n\n [SocketIOBindingKeys.AUTHENTICATE_HANDLER]: Binding.bind<TSocketIOAuthenticateFn | null>({\n key: SocketIOBindingKeys.AUTHENTICATE_HANDLER,\n }).toValue(null),\n\n [SocketIOBindingKeys.VALIDATE_ROOM_HANDLER]: Binding.bind<TSocketIOValidateRoomFn | null>({\n key: SocketIOBindingKeys.VALIDATE_ROOM_HANDLER,\n }).toValue(null),\n\n [SocketIOBindingKeys.CLIENT_CONNECTED_HANDLER]:\n Binding.bind<TSocketIOClientConnectedFn | null>({\n key: SocketIOBindingKeys.CLIENT_CONNECTED_HANDLER,\n }).toValue(null),\n },\n });\n }\n\n private resolveBindings(): IResolvedBindings {\n const extraServerOptions =\n this.application.get<Partial<ServerOptions>>({\n key: SocketIOBindingKeys.SERVER_OPTIONS,\n isOptional: true,\n }) ?? {};\n this.serverOptions = Object.assign({}, DEFAULT_SERVER_OPTIONS, extraServerOptions);\n\n const redisConnection = this.application.get<AbstractRedisHelper>({\n key: SocketIOBindingKeys.REDIS_CONNECTION,\n });\n if (!isRedisHelper(redisConnection)) {\n throw getError({\n message:\n '[SocketIOComponent][resolveBindings] Invalid instance of redisConnection | Please init connection with RedisSingleHelper (single), RedisClusterHelper (cluster), or RedisSentinelHelper (sentinel)!',\n });\n }\n\n const authenticateFn = this.application.get<TSocketIOAuthenticateFn>({\n key: SocketIOBindingKeys.AUTHENTICATE_HANDLER,\n });\n if (!authenticateFn) {\n throw getError({\n message: '[DANGER][SocketIOComponent] Invalid authenticateFn to setup io socket server!',\n });\n }\n\n const validateRoomFn =\n this.application.get<TSocketIOValidateRoomFn | null>({\n key: SocketIOBindingKeys.VALIDATE_ROOM_HANDLER,\n }) ?? undefined;\n\n const clientConnectedFn =\n this.application.get<TSocketIOClientConnectedFn | null>({\n key: SocketIOBindingKeys.CLIENT_CONNECTED_HANDLER,\n }) ?? undefined;\n\n return { redisConnection, authenticateFn, validateRoomFn, clientConnectedFn };\n }\n\n private registerBunHook(opts: IResolvedBindings) {\n const { redisConnection, authenticateFn, validateRoomFn, clientConnectedFn } = opts;\n const serverOptions = this.serverOptions;\n const logger = this.logger.for(this.registerBunHook.name);\n\n this.application.registerPostStartHook({\n identifier: 'socket-io-initialize',\n hook: async () => {\n const { engine, engineHandler } = await createBunEngine({ serverOptions });\n\n const socketIOHelper = new SocketIOServerHelper({\n runtime: RuntimeModules.BUN,\n identifier: serverOptions.identifier!,\n engine,\n serverOptions,\n redisConnection,\n authenticateFn,\n validateRoomFn,\n clientConnectedFn,\n });\n await socketIOHelper.configure();\n\n this.application\n .bind({ key: SocketIOBindingKeys.SOCKET_IO_INSTANCE })\n .toValue(socketIOHelper);\n\n const serverInstance = this.application.getServerInstance<TBunServerInstance>();\n const honoServer = this.application.getServer();\n const enginePath = serverOptions.path ?? '/socket.io/';\n\n serverInstance!.reload({\n fetch: createBunFetchHandler({ engine, enginePath, honoServer }),\n websocket: engineHandler.websocket,\n });\n\n logger.info('SocketIO initialized for Bun runtime');\n },\n });\n }\n\n override binding(): ValueOrPromise<void> {\n const logger = this.logger.for(this.binding.name);\n\n if (!this.application) {\n throw getError({\n statusCode: HTTP.ResultCodes.RS_5.InternalServerError,\n message: '[binding] Invalid application to bind SocketIOComponent',\n });\n }\n\n logger.info('Binding SocketIO for application...');\n\n const resolved = this.resolveBindings();\n logger.debug('Socket.IO Server Options: %j', this.serverOptions);\n\n this.registerBunHook(resolved);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"component.js","sourceRoot":"","sources":["../../../../src/components/socket-io/component.ts"],"names":[],"mappings":"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","sourcesContent":["import { type BaseApplication, type TBunServerInstance } from '@/base/applications';\nimport { BaseComponent } from '@venizia/ignis-kernel';\nimport { inject } from '@/base/metadata';\nimport { CoreBindings } from '@venizia/ignis-kernel';\nimport { Binding } from '@venizia/ignis-kernel';\nimport { getError } from '@venizia/ignis-helpers/core';\nimport { HTTP, RuntimeModules, type ValueOrPromise } from '@venizia/ignis-helpers/common';\nimport { type AbstractRedisHelper, isRedisHelper } from '@venizia/ignis-helpers';\nimport {\n SocketIOServerHelper,\n type TSocketIOAuthenticateFn,\n type TSocketIOClientConnectedFn,\n type TSocketIOValidateRoomFn,\n} from '@venizia/ignis-helpers/socket-io';\nimport type { ServerOptions } from 'socket.io';\nimport {\n SocketIOBindingKeys,\n DEFAULT_SERVER_OPTIONS,\n type IResolvedBindings,\n type IServerOptions,\n} from './common';\nimport { SocketIOBunHandler } from './handlers';\n\nexport class SocketIOComponent extends BaseComponent {\n protected serverOptions: Partial<IServerOptions>;\n\n constructor(\n @inject({ key: CoreBindings.APPLICATION_INSTANCE }) private application: BaseApplication,\n ) {\n super({\n scope: SocketIOComponent.name,\n initDefault: { enable: true, container: application },\n bindings: {\n [SocketIOBindingKeys.SERVER_OPTIONS]: Binding.bind<Partial<ServerOptions>>({\n key: SocketIOBindingKeys.SERVER_OPTIONS,\n }).toValue(DEFAULT_SERVER_OPTIONS),\n\n [SocketIOBindingKeys.REDIS_CONNECTION]: Binding.bind<AbstractRedisHelper | null>({\n key: SocketIOBindingKeys.REDIS_CONNECTION,\n }).toValue(null),\n\n [SocketIOBindingKeys.AUTHENTICATE_HANDLER]: Binding.bind<TSocketIOAuthenticateFn | null>({\n key: SocketIOBindingKeys.AUTHENTICATE_HANDLER,\n }).toValue(null),\n\n [SocketIOBindingKeys.VALIDATE_ROOM_HANDLER]: Binding.bind<TSocketIOValidateRoomFn | null>({\n key: SocketIOBindingKeys.VALIDATE_ROOM_HANDLER,\n }).toValue(null),\n\n [SocketIOBindingKeys.CLIENT_CONNECTED_HANDLER]:\n Binding.bind<TSocketIOClientConnectedFn | null>({\n key: SocketIOBindingKeys.CLIENT_CONNECTED_HANDLER,\n }).toValue(null),\n },\n });\n }\n\n private resolveBindings(): IResolvedBindings {\n const extraServerOptions =\n this.application.get<Partial<ServerOptions>>({\n key: SocketIOBindingKeys.SERVER_OPTIONS,\n isOptional: true,\n }) ?? {};\n this.serverOptions = Object.assign({}, DEFAULT_SERVER_OPTIONS, extraServerOptions);\n\n const redisConnection = this.application.get<AbstractRedisHelper>({\n key: SocketIOBindingKeys.REDIS_CONNECTION,\n });\n if (!isRedisHelper(redisConnection)) {\n throw getError({\n message:\n '[SocketIOComponent][resolveBindings] Invalid instance of redisConnection | Please init connection with RedisSingleHelper (single), RedisClusterHelper (cluster), or RedisSentinelHelper (sentinel)!',\n });\n }\n\n const authenticateFn = this.application.get<TSocketIOAuthenticateFn>({\n key: SocketIOBindingKeys.AUTHENTICATE_HANDLER,\n });\n if (!authenticateFn) {\n throw getError({\n message: '[DANGER][SocketIOComponent] Invalid authenticateFn to setup io socket server!',\n });\n }\n\n const validateRoomFn =\n this.application.get<TSocketIOValidateRoomFn | null>({\n key: SocketIOBindingKeys.VALIDATE_ROOM_HANDLER,\n }) ?? undefined;\n\n const clientConnectedFn =\n this.application.get<TSocketIOClientConnectedFn | null>({\n key: SocketIOBindingKeys.CLIENT_CONNECTED_HANDLER,\n }) ?? undefined;\n\n return { redisConnection, authenticateFn, validateRoomFn, clientConnectedFn };\n }\n\n private registerBunHook(opts: IResolvedBindings) {\n const { redisConnection, authenticateFn, validateRoomFn, clientConnectedFn } = opts;\n const serverOptions = this.serverOptions;\n const logger = this.logger.for(this.registerBunHook.name);\n\n this.application.registerPostStartHook({\n identifier: 'socket-io-initialize',\n hook: async () => {\n const { engine, engineHandler } = await SocketIOBunHandler.createEngine({ serverOptions });\n\n const socketIOHelper = new SocketIOServerHelper({\n runtime: RuntimeModules.BUN,\n identifier: serverOptions.identifier!,\n engine,\n serverOptions,\n redisConnection,\n authenticateFn,\n validateRoomFn,\n clientConnectedFn,\n });\n await socketIOHelper.configure();\n\n this.application\n .bind({ key: SocketIOBindingKeys.SOCKET_IO_INSTANCE })\n .toValue(socketIOHelper);\n\n const serverInstance = this.application.getServerInstance<TBunServerInstance>();\n const honoServer = this.application.getServer();\n const enginePath = serverOptions.path ?? '/socket.io/';\n\n serverInstance!.reload({\n fetch: SocketIOBunHandler.createFetchHandler({ engine, enginePath, honoServer }),\n websocket: engineHandler.websocket,\n });\n\n logger.info('SocketIO initialized for Bun runtime');\n },\n });\n }\n\n override binding(): ValueOrPromise<void> {\n const logger = this.logger.for(this.binding.name);\n\n if (!this.application) {\n throw getError({\n statusCode: HTTP.ResultCodes.RS_5.InternalServerError,\n message: '[binding] Invalid application to bind SocketIOComponent',\n });\n }\n\n logger.info('Binding SocketIO for application...');\n\n const resolved = this.resolveBindings();\n logger.debug('Socket.IO Server Options: %j', this.serverOptions);\n\n this.registerBunHook(resolved);\n }\n}\n"]}
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import type { OpenAPIHono } from '@hono/zod-openapi';
|
|
2
2
|
import type { TBunServerInstance } from '../../../base/applications/index.js';
|
|
3
3
|
import type { ServerOptions } from 'socket.io';
|
|
4
|
-
export declare
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
4
|
+
export declare class SocketIOBunHandler {
|
|
5
|
+
static createEngine(opts: {
|
|
6
|
+
serverOptions: Partial<ServerOptions>;
|
|
7
|
+
}): Promise<{
|
|
8
|
+
engine: any;
|
|
9
|
+
engineHandler: any;
|
|
10
|
+
}>;
|
|
11
|
+
static createFetchHandler(opts: {
|
|
12
|
+
engine: any;
|
|
13
|
+
enginePath: string;
|
|
14
|
+
honoServer: OpenAPIHono;
|
|
15
|
+
}): (req: Request, server: TBunServerInstance) => Response | Promise<Response>;
|
|
16
|
+
}
|
|
15
17
|
//# sourceMappingURL=bun.handler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bun.handler.d.ts","sourceRoot":"","sources":["../../../../../src/components/socket-io/handlers/bun.handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C,
|
|
1
|
+
{"version":3,"file":"bun.handler.d.ts","sourceRoot":"","sources":["../../../../../src/components/socket-io/handlers/bun.handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C,qBAAa,kBAAkB;WAChB,YAAY,CAAC,IAAI,EAAE;QAC9B,aAAa,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;KACvC,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,GAAG,CAAC;QAAC,aAAa,EAAE,GAAG,CAAA;KAAE,CAAC;IAyBhD,MAAM,CAAC,kBAAkB,CAAC,IAAI,EAAE;QAC9B,MAAM,EAAE,GAAG,CAAC;QACZ,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,WAAW,CAAC;KACzB,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,kBAAkB,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;CAa/E"}
|