@venizia/ignis 0.4.6 → 0.4.7-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/esm/base/applications/base.d.ts.map +1 -1
  2. package/dist/esm/base/applications/base.js +4 -2
  3. package/dist/esm/base/applications/base.js.map +1 -1
  4. package/dist/esm/base/controllers/grpc/adapter.d.ts.map +1 -1
  5. package/dist/esm/base/controllers/grpc/adapter.js +5 -1
  6. package/dist/esm/base/controllers/grpc/adapter.js.map +1 -1
  7. package/dist/esm/base/middlewares/request-spy/request-spy.middleware.d.ts.map +1 -1
  8. package/dist/esm/base/middlewares/request-spy/request-spy.middleware.js +9 -3
  9. package/dist/esm/base/middlewares/request-spy/request-spy.middleware.js.map +1 -1
  10. package/dist/esm/components/api-reference/ui/scalar.d.ts.map +1 -1
  11. package/dist/esm/components/api-reference/ui/scalar.js +2 -1
  12. package/dist/esm/components/api-reference/ui/scalar.js.map +1 -1
  13. package/dist/esm/components/api-reference/ui/swagger.d.ts.map +1 -1
  14. package/dist/esm/components/api-reference/ui/swagger.js +2 -1
  15. package/dist/esm/components/api-reference/ui/swagger.js.map +1 -1
  16. package/dist/esm/components/auth/authenticate/services/bearer/abstract.service.d.ts.map +1 -1
  17. package/dist/esm/components/auth/authenticate/services/bearer/abstract.service.js +4 -2
  18. package/dist/esm/components/auth/authenticate/services/bearer/abstract.service.js.map +1 -1
  19. package/dist/esm/components/auth/authenticate/services/service/signer.service.d.ts.map +1 -1
  20. package/dist/esm/components/auth/authenticate/services/service/signer.service.js +6 -4
  21. package/dist/esm/components/auth/authenticate/services/service/signer.service.js.map +1 -1
  22. package/dist/esm/components/auth/authorize/adapters/scoped-casbin/adapter.d.ts.map +1 -1
  23. package/dist/esm/components/auth/authorize/adapters/scoped-casbin/adapter.js +6 -3
  24. package/dist/esm/components/auth/authorize/adapters/scoped-casbin/adapter.js.map +1 -1
  25. package/dist/esm/components/auth/authorize/enforcers/casbin-module.d.ts.map +1 -1
  26. package/dist/esm/components/auth/authorize/enforcers/casbin-module.js +2 -1
  27. package/dist/esm/components/auth/authorize/enforcers/casbin-module.js.map +1 -1
  28. package/dist/esm/components/auth/authorize/enforcers/casbin.enforcer.d.ts.map +1 -1
  29. package/dist/esm/components/auth/authorize/enforcers/casbin.enforcer.js +9 -3
  30. package/dist/esm/components/auth/authorize/enforcers/casbin.enforcer.js.map +1 -1
  31. package/dist/esm/components/health-check/component.d.ts.map +1 -1
  32. package/dist/esm/components/health-check/component.js +5 -3
  33. package/dist/esm/components/health-check/component.js.map +1 -1
  34. package/dist/esm/components/mail/helpers/executors/bull-mq-executor.helper.d.ts.map +1 -1
  35. package/dist/esm/components/mail/helpers/executors/bull-mq-executor.helper.js +2 -1
  36. package/dist/esm/components/mail/helpers/executors/bull-mq-executor.helper.js.map +1 -1
  37. package/dist/esm/components/mail/helpers/transporters/nodemail-transporter.helper.d.ts.map +1 -1
  38. package/dist/esm/components/mail/helpers/transporters/nodemail-transporter.helper.js +5 -3
  39. package/dist/esm/components/mail/helpers/transporters/nodemail-transporter.helper.js.map +1 -1
  40. package/dist/esm/components/mail/services/mail.service.d.ts.map +1 -1
  41. package/dist/esm/components/mail/services/mail.service.js +6 -3
  42. package/dist/esm/components/mail/services/mail.service.js.map +1 -1
  43. package/dist/esm/components/mail/utilities/attachment.utility.d.ts.map +1 -1
  44. package/dist/esm/components/mail/utilities/attachment.utility.js +4 -2
  45. package/dist/esm/components/mail/utilities/attachment.utility.js.map +1 -1
  46. package/dist/esm/components/mail/utilities/mime.utility.d.ts.map +1 -1
  47. package/dist/esm/components/mail/utilities/mime.utility.js +8 -4
  48. package/dist/esm/components/mail/utilities/mime.utility.js.map +1 -1
  49. package/dist/esm/components/static-asset/controller/factory.d.ts.map +1 -1
  50. package/dist/esm/components/static-asset/controller/factory.js +4 -2
  51. package/dist/esm/components/static-asset/controller/factory.js.map +1 -1
  52. package/dist/esm/testing/assert-application-boots.d.ts.map +1 -1
  53. package/dist/esm/testing/assert-application-boots.js +8 -1
  54. package/dist/esm/testing/assert-application-boots.js.map +1 -1
  55. package/dist/presets/app/preset.json +4 -4
  56. package/dist/presets/lib/preset.json +3 -3
  57. package/package.json +8 -8
@@ -1 +1 @@
1
- {"version":3,"file":"bull-mq-executor.helper.js","sourceRoot":"","sources":["../../../../../../src/components/mail/helpers/executors/bull-mq-executor.helper.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseHelper, getError } from '@venizia/ignis-helpers/core';\nimport { RedisSingleHelper } from '@venizia/ignis-helpers';\nimport type { TConstValue } from '@/helpers';\nimport { BullMQHelper } from '@venizia/ignis-helpers/bullmq';\nimport type {\n IBullMQMailExecutorOpts,\n IMailProcessorResult,\n IMailQueueExecutor,\n IMailQueueOptions,\n IMailQueueResult,\n IQueueJobPayload,\n} from '../../common';\nimport {\n BullMQExecutorModes,\n MailBackoffTypes,\n MailErrorCodes,\n MailExecutorErrors,\n} from '../../common';\nimport type { Job } from 'bullmq';\n\nexport class BullMQMailExecutorHelper extends BaseHelper implements IMailQueueExecutor {\n private queueIdentifier: string;\n private queueName: string;\n private mode: TConstValue<typeof BullMQExecutorModes>;\n\n private queueHelper?: BullMQHelper<IQueueJobPayload, IMailQueueResult>;\n private workerHelpers: BullMQHelper<IQueueJobPayload, IMailProcessorResult>[] = [];\n private redisConnection: RedisSingleHelper;\n private readonly bullmqModule?: typeof import('bullmq');\n private jobIdCounter = 0;\n\n private processor?: (email: string) => Promise<IMailProcessorResult>;\n\n constructor(opts: IBullMQMailExecutorOpts) {\n super({ scope: BullMQMailExecutorHelper.name });\n\n this.mode = opts.mode;\n\n this.redisConnection = new RedisSingleHelper({\n ...opts.redis,\n maxRetry: undefined,\n });\n\n this.bullmqModule = opts.module;\n this.queueIdentifier = opts.queue.identifier;\n this.queueName = opts.queue.name;\n\n this.initQueue(opts.queue);\n }\n\n initQueue(queue: { identifier: string; name: string }) {\n switch (this.mode) {\n case BullMQExecutorModes.QUEUE_ONLY:\n case BullMQExecutorModes.BOTH: {\n this.queueHelper = new BullMQHelper<IQueueJobPayload, IMailQueueResult>({\n identifier: queue.identifier,\n queueName: queue.name,\n redisConnection: this.redisConnection,\n role: 'queue',\n module: this.bullmqModule,\n });\n\n this.logger\n .for(this.constructor.name)\n .info(\n 'BullMQ queue executor initialized (queue enabled) | Identifier: %s | Mode: %s',\n this.queueIdentifier,\n this.mode,\n );\n break;\n }\n default: {\n this.logger\n .for(this.constructor.name)\n .info(\n 'BullMQ queue executor initialized (worker-only mode) | Identifier: %s | Mode: %s',\n this.queueIdentifier,\n this.mode,\n );\n\n break;\n }\n }\n }\n\n async setProcessor(\n processor: (email: string) => Promise<IMailProcessorResult>,\n opts?: {\n numberOfWorkers?: number;\n concurrencyPerWorker?: number;\n lockDuration?: number;\n },\n ): Promise<void> {\n this.processor = processor;\n\n if (this.mode === BullMQExecutorModes.QUEUE_ONLY) {\n this.logger\n .for(this.setProcessor.name)\n .warn('Skipping worker creation in queue-only mode. Workers will not be started.');\n return;\n }\n\n const numberOfWorkers = opts?.numberOfWorkers ?? 1;\n const concurrencyPerWorker = opts?.concurrencyPerWorker ?? 5;\n const lockDuration = opts?.lockDuration ?? 30000;\n\n await this.clearWorkers();\n\n for (let i = 0; i < numberOfWorkers; i++) {\n this.addWorker({\n workerIdentifier: `${this.queueIdentifier}-worker-${i}`,\n concurrency: concurrencyPerWorker,\n lockDuration,\n });\n }\n\n this.logger\n .for(this.setProcessor.name)\n .info(\n 'Processor registered | Mode: %s | Workers: %d | Concurrency per worker: %d',\n this.mode,\n numberOfWorkers,\n concurrencyPerWorker,\n );\n }\n\n addWorker(opts: { workerIdentifier: string; concurrency?: number; lockDuration?: number }): void {\n if (!this.processor) {\n throw getError({ message: MailExecutorErrors.PROCESSOR_NOT_SET });\n }\n\n const workerIdentifier = opts.workerIdentifier;\n const concurrency = opts?.concurrency ?? 5;\n const lockDuration = opts?.lockDuration ?? 30000;\n\n const workerHelper = new BullMQHelper<IQueueJobPayload, IMailProcessorResult>({\n identifier: workerIdentifier,\n queueName: this.queueName,\n redisConnection: this.redisConnection,\n role: 'worker',\n module: this.bullmqModule,\n numberOfWorker: concurrency,\n lockDuration,\n onWorkerData: async (job: Job<IQueueJobPayload, IMailProcessorResult>) => {\n const maxAttempts = job.data.options?.attempts ?? 3;\n\n this.logger\n .for('onWorkerData')\n .info(\n 'Processing job | Worker: %s | jobId: %s | email: %s | attempt: %d/%d',\n workerIdentifier,\n job.data.id,\n job.data.email,\n job.attemptsMade + 1,\n maxAttempts,\n );\n\n if (!this.processor) {\n // Returning undefined would let BullMQ mark the job COMPLETED and (with removeOnComplete) delete it - mail silently dropped.\n throw getError({\n messageCode: MailErrorCodes.SEND_FAILED,\n message: `[onWorkerData] ${MailExecutorErrors.PROCESSOR_NOT_SET} | jobId: ${job.data.id}`,\n });\n }\n\n const result = await this.processor(job.data.email);\n\n // A processor reports SMTP rejection by RETURNING `{ success: false }`; BullMQ only treats a REJECTION as failure, so resolving completes the job - no retry, no DLQ.\n if (result?.success === false) {\n throw getError({\n messageCode: MailErrorCodes.SEND_FAILED,\n message: `[onWorkerData] Processor reported failure | jobId: ${job.data.id} | reason: ${result.message}`,\n });\n }\n\n return result;\n },\n onWorkerDataCompleted: async (job: Job<IQueueJobPayload, IMailProcessorResult>) => {\n this.logger\n .for('onWorkerDataCompleted')\n .info(\n 'Job completed | Worker: %s | jobId: %s | email: %s',\n workerIdentifier,\n job.data.id,\n job.data.email,\n );\n },\n onWorkerDataFail: async (\n job: Job<IQueueJobPayload, IMailProcessorResult> | undefined,\n error: Error,\n ) => {\n if (job) {\n const maxAttempts = job.data.options?.attempts ?? 3;\n this.logger\n .for('onWorkerDataFail')\n .error(\n 'Job failed | Worker: %s | jobId: %s | email: %s | attempt: %d/%d | error: %s',\n workerIdentifier,\n job.data.id,\n job.data.email,\n job.attemptsMade,\n maxAttempts,\n error.message,\n );\n } else {\n this.logger\n .for('onWorkerDataFail')\n .error(\n 'Worker error (no job) | Worker: %s | error: %s',\n workerIdentifier,\n error.message,\n );\n }\n },\n });\n\n this.workerHelpers.push(workerHelper);\n\n this.logger\n .for(this.addWorker.name)\n .info(\n 'Worker added | Identifier: %s | Concurrency: %d | Total workers: %d',\n workerIdentifier,\n concurrency,\n this.workerHelpers.length,\n );\n }\n\n async removeWorker(index: number): Promise<boolean> {\n if (index < 0 || index >= this.workerHelpers.length) {\n this.logger.for(this.removeWorker.name).warn('Invalid worker index: %d', index);\n return false;\n }\n\n // Dropped from the list BEFORE the close is awaited: a rejecting close must not leave a half-closed worker still counted and still handed jobs.\n const [workerHelper] = this.workerHelpers.splice(index, 1);\n await workerHelper.worker.close();\n\n this.logger\n .for(this.removeWorker.name)\n .info('Worker removed | Index: %d | Remaining workers: %d', index, this.workerHelpers.length);\n\n return true;\n }\n\n /** Closes every worker without stopping at the first failure, always empties the list, and returns failures instead of throwing so teardown can continue. */\n private async closeWorkers(): Promise<Error[]> {\n const workerHelpers = this.workerHelpers;\n this.workerHelpers = [];\n\n const results = await Promise.allSettled(\n workerHelpers.map((workerHelper) => workerHelper.worker.close()),\n );\n\n const failures: Error[] = [];\n\n for (const result of results) {\n if (result.status !== 'rejected') {\n continue;\n }\n\n const error =\n result.reason instanceof Error\n ? result.reason\n : getError({ message: `Worker close failed | reason: ${String(result.reason)}` });\n\n failures.push(error);\n this.logger.for('closeWorkers').error('Failed to close worker | error: %s', error.message);\n }\n\n return failures;\n }\n\n async clearWorkers(): Promise<void> {\n const count = this.workerHelpers.length;\n await this.closeWorkers();\n\n this.logger.for(this.clearWorkers.name).info('All workers cleared | Count: %d', count);\n }\n\n /** Full teardown: workers -> queue -> Redis. Every step runs even if an earlier one failed (skipping leaks a queue client + Redis socket); failures are reported once, at the end. */\n async close(): Promise<void> {\n const failures: Error[] = [...(await this.closeWorkers())];\n\n if (this.queueHelper) {\n try {\n await this.queueHelper.queue.close();\n } catch (error) {\n const closeError = error instanceof Error ? error : getError({ message: String(error) });\n failures.push(closeError);\n this.logger.for(this.close.name).error('Failed to close queue | error: %s', closeError);\n }\n }\n\n try {\n await this.redisConnection.disconnect();\n } catch (error) {\n const disconnectError = error instanceof Error ? error : getError({ message: String(error) });\n failures.push(disconnectError);\n this.logger\n .for(this.close.name)\n .error('Failed to disconnect redis | error: %s', disconnectError);\n }\n\n if (failures.length > 0) {\n throw getError({\n message: `[close] Incomplete BullMQ mail executor teardown | failures: ${failures\n .map((failure) => failure.message)\n .join(' | ')}`,\n });\n }\n\n this.logger.for(this.close.name).info('BullMQ mail executor closed | Mode: %s', this.mode);\n }\n\n getWorkerCount(): number {\n return this.workerHelpers.length;\n }\n\n getMode(): TConstValue<typeof BullMQExecutorModes> {\n return this.mode;\n }\n\n async enqueueVerificationEmail(\n email: string,\n options?: IMailQueueOptions,\n ): Promise<IMailQueueResult> {\n if (this.mode === BullMQExecutorModes.WORKER_ONLY) {\n throw getError({\n message: 'Cannot enqueue jobs in worker-only mode. Set mode to \"queue-only\" or \"both\".',\n });\n }\n\n if (!this.queueHelper) {\n throw getError({\n message: 'Queue helper not initialized. This should not happen in queue-enabled mode.',\n });\n }\n\n if (!this.processor && this.mode !== BullMQExecutorModes.QUEUE_ONLY) {\n throw getError({ message: MailExecutorErrors.PROCESSOR_NOT_SET });\n }\n\n const jobId = `job_${++this.jobIdCounter}_${Date.now()}`;\n const job: IQueueJobPayload = {\n id: jobId,\n email,\n options,\n attempts: 0,\n scheduledAt: Date.now() + (options?.delay ?? 0),\n };\n\n this.logger\n .for(this.enqueueVerificationEmail.name)\n .info(\n 'Queuing email to BullMQ/Redis | jobId: %s | email: %s | mode: %s',\n jobId,\n email,\n this.mode,\n );\n\n const bullJob = await this.queueHelper.queue.add('send-verification-email', job, {\n priority: options?.priority,\n delay: options?.delay,\n attempts: options?.attempts ?? 3,\n backoff: {\n type: options?.backoff?.type ?? MailBackoffTypes.EXPONENTIAL,\n delay: options?.backoff?.delay ?? 1000,\n },\n removeOnComplete: true,\n removeOnFail: false,\n });\n\n return {\n jobId: bullJob.id ?? jobId,\n queued: true,\n message: 'Email queued successfully (BullMQ/Redis)',\n };\n }\n}\n"]}
1
+ {"version":3,"file":"bull-mq-executor.helper.js","sourceRoot":"","sources":["../../../../../../src/components/mail/helpers/executors/bull-mq-executor.helper.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseHelper, getError } from '@venizia/ignis-helpers/core';\nimport { RedisSingleHelper } from '@venizia/ignis-helpers';\nimport type { TConstValue } from '@/helpers';\nimport { BullMQHelper } from '@venizia/ignis-helpers/bullmq';\nimport type {\n IBullMQMailExecutorOpts,\n IMailProcessorResult,\n IMailQueueExecutor,\n IMailQueueOptions,\n IMailQueueResult,\n IQueueJobPayload,\n} from '../../common';\nimport {\n BullMQExecutorModes,\n MailBackoffTypes,\n MailErrorCodes,\n MailExecutorErrors,\n} from '../../common';\nimport type { Job } from 'bullmq';\n\nexport class BullMQMailExecutorHelper extends BaseHelper implements IMailQueueExecutor {\n private queueIdentifier: string;\n private queueName: string;\n private mode: TConstValue<typeof BullMQExecutorModes>;\n\n private queueHelper?: BullMQHelper<IQueueJobPayload, IMailQueueResult>;\n private workerHelpers: BullMQHelper<IQueueJobPayload, IMailProcessorResult>[] = [];\n private redisConnection: RedisSingleHelper;\n private readonly bullmqModule?: typeof import('bullmq');\n private jobIdCounter = 0;\n\n private processor?: (email: string) => Promise<IMailProcessorResult>;\n\n constructor(opts: IBullMQMailExecutorOpts) {\n super({ scope: BullMQMailExecutorHelper.name });\n\n this.mode = opts.mode;\n\n this.redisConnection = new RedisSingleHelper({\n ...opts.redis,\n maxRetry: undefined,\n });\n\n this.bullmqModule = opts.module;\n this.queueIdentifier = opts.queue.identifier;\n this.queueName = opts.queue.name;\n\n this.initQueue(opts.queue);\n }\n\n initQueue(queue: { identifier: string; name: string }) {\n switch (this.mode) {\n case BullMQExecutorModes.QUEUE_ONLY:\n case BullMQExecutorModes.BOTH: {\n this.queueHelper = new BullMQHelper<IQueueJobPayload, IMailQueueResult>({\n identifier: queue.identifier,\n queueName: queue.name,\n redisConnection: this.redisConnection,\n role: 'queue',\n module: this.bullmqModule,\n });\n\n this.logger\n .for(this.constructor.name)\n .info(\n 'BullMQ queue executor initialized (queue enabled) | Identifier: %s | Mode: %s',\n this.queueIdentifier,\n this.mode,\n );\n break;\n }\n default: {\n this.logger\n .for(this.constructor.name)\n .info(\n 'BullMQ queue executor initialized (worker-only mode) | Identifier: %s | Mode: %s',\n this.queueIdentifier,\n this.mode,\n );\n\n break;\n }\n }\n }\n\n async setProcessor(\n processor: (email: string) => Promise<IMailProcessorResult>,\n opts?: {\n numberOfWorkers?: number;\n concurrencyPerWorker?: number;\n lockDuration?: number;\n },\n ): Promise<void> {\n this.processor = processor;\n\n if (this.mode === BullMQExecutorModes.QUEUE_ONLY) {\n this.logger\n .for(this.setProcessor.name)\n .warn('Skipping worker creation in queue-only mode. Workers will not be started.');\n return;\n }\n\n const numberOfWorkers = opts?.numberOfWorkers ?? 1;\n const concurrencyPerWorker = opts?.concurrencyPerWorker ?? 5;\n const lockDuration = opts?.lockDuration ?? 30000;\n\n await this.clearWorkers();\n\n for (let i = 0; i < numberOfWorkers; i++) {\n this.addWorker({\n workerIdentifier: `${this.queueIdentifier}-worker-${i}`,\n concurrency: concurrencyPerWorker,\n lockDuration,\n });\n }\n\n this.logger\n .for(this.setProcessor.name)\n .info(\n 'Processor registered | Mode: %s | Workers: %d | Concurrency per worker: %d',\n this.mode,\n numberOfWorkers,\n concurrencyPerWorker,\n );\n }\n\n addWorker(opts: { workerIdentifier: string; concurrency?: number; lockDuration?: number }): void {\n if (!this.processor) {\n throw getError({ message: MailExecutorErrors.PROCESSOR_NOT_SET });\n }\n\n const workerIdentifier = opts.workerIdentifier;\n const concurrency = opts?.concurrency ?? 5;\n const lockDuration = opts?.lockDuration ?? 30000;\n\n const workerHelper = new BullMQHelper<IQueueJobPayload, IMailProcessorResult>({\n identifier: workerIdentifier,\n queueName: this.queueName,\n redisConnection: this.redisConnection,\n role: 'worker',\n module: this.bullmqModule,\n numberOfWorker: concurrency,\n lockDuration,\n onWorkerData: async (job: Job<IQueueJobPayload, IMailProcessorResult>) => {\n const maxAttempts = job.data.options?.attempts ?? 3;\n\n this.logger\n .for('onWorkerData')\n .info(\n 'Processing job | Worker: %s | jobId: %s | email: %s | attempt: %d/%d',\n workerIdentifier,\n job.data.id,\n job.data.email,\n job.attemptsMade + 1,\n maxAttempts,\n );\n\n if (!this.processor) {\n // Returning undefined would let BullMQ mark the job COMPLETED and (with removeOnComplete) delete it - mail silently dropped.\n throw getError({\n messageCode: MailErrorCodes.SEND_FAILED,\n message: `[onWorkerData] ${MailExecutorErrors.PROCESSOR_NOT_SET} | jobId: ${job.data.id}`,\n });\n }\n\n const result = await this.processor(job.data.email);\n\n // A processor reports SMTP rejection by RETURNING `{ success: false }`; BullMQ only treats a REJECTION as failure, so resolving completes the job - no retry, no DLQ.\n if (result?.success === false) {\n throw getError({\n messageCode: MailErrorCodes.SEND_FAILED,\n message: `[onWorkerData] Processor reported failure | jobId: ${job.data.id} | reason: ${result.message}`,\n });\n }\n\n return result;\n },\n onWorkerDataCompleted: async (job: Job<IQueueJobPayload, IMailProcessorResult>) => {\n this.logger\n .for('onWorkerDataCompleted')\n .info(\n 'Job completed | Worker: %s | jobId: %s | email: %s',\n workerIdentifier,\n job.data.id,\n job.data.email,\n );\n },\n onWorkerDataFail: async (\n job: Job<IQueueJobPayload, IMailProcessorResult> | undefined,\n error: Error,\n ) => {\n if (job) {\n const maxAttempts = job.data.options?.attempts ?? 3;\n this.logger\n .for('onWorkerDataFail')\n .error(\n 'Job failed | Worker: %s | jobId: %s | email: %s | attempt: %d/%d | error: %s',\n workerIdentifier,\n job.data.id,\n job.data.email,\n job.attemptsMade,\n maxAttempts,\n error.message,\n );\n } else {\n this.logger\n .for('onWorkerDataFail')\n .error(\n 'Worker error (no job) | Worker: %s | error: %s',\n workerIdentifier,\n error.message,\n );\n }\n },\n });\n\n this.workerHelpers.push(workerHelper);\n\n this.logger\n .for(this.addWorker.name)\n .info(\n 'Worker added | Identifier: %s | Concurrency: %d | Total workers: %d',\n workerIdentifier,\n concurrency,\n this.workerHelpers.length,\n );\n }\n\n async removeWorker(index: number): Promise<boolean> {\n if (index < 0 || index >= this.workerHelpers.length) {\n this.logger.for(this.removeWorker.name).warn('Invalid worker index: %d', index);\n return false;\n }\n\n // Dropped from the list BEFORE the close is awaited: a rejecting close must not leave a half-closed worker still counted and still handed jobs.\n const [workerHelper] = this.workerHelpers.splice(index, 1);\n await workerHelper.worker.close();\n\n this.logger\n .for(this.removeWorker.name)\n .info('Worker removed | Index: %d | Remaining workers: %d', index, this.workerHelpers.length);\n\n return true;\n }\n\n /** Closes every worker without stopping at the first failure, always empties the list, and returns failures instead of throwing so teardown can continue. */\n private async closeWorkers(): Promise<Error[]> {\n const workerHelpers = this.workerHelpers;\n this.workerHelpers = [];\n\n const results = await Promise.allSettled(\n workerHelpers.map((workerHelper) => workerHelper.worker.close()),\n );\n\n const failures: Error[] = [];\n\n for (const result of results) {\n if (result.status !== 'rejected') {\n continue;\n }\n\n const error =\n result.reason instanceof Error\n ? result.reason\n : getError({ message: `Worker close failed | reason: ${String(result.reason)}` });\n\n failures.push(error);\n this.logger.for('closeWorkers').error('Failed to close worker | error: %s', error.message);\n }\n\n return failures;\n }\n\n async clearWorkers(): Promise<void> {\n const count = this.workerHelpers.length;\n await this.closeWorkers();\n\n this.logger.for(this.clearWorkers.name).info('All workers cleared | Count: %d', count);\n }\n\n /** Full teardown: workers -> queue -> Redis. Every step runs even if an earlier one failed (skipping leaks a queue client + Redis socket); failures are reported once, at the end. */\n async close(): Promise<void> {\n const result = await this.closeWorkers();\n const failures: Error[] = [...result];\n\n if (this.queueHelper) {\n try {\n await this.queueHelper.queue.close();\n } catch (error) {\n const closeError = error instanceof Error ? error : getError({ message: String(error) });\n failures.push(closeError);\n this.logger.for(this.close.name).error('Failed to close queue | error: %s', closeError);\n }\n }\n\n try {\n await this.redisConnection.disconnect();\n } catch (error) {\n const disconnectError = error instanceof Error ? error : getError({ message: String(error) });\n failures.push(disconnectError);\n this.logger\n .for(this.close.name)\n .error('Failed to disconnect redis | error: %s', disconnectError);\n }\n\n if (failures.length > 0) {\n throw getError({\n message: `[close] Incomplete BullMQ mail executor teardown | failures: ${failures\n .map((failure) => failure.message)\n .join(' | ')}`,\n });\n }\n\n this.logger.for(this.close.name).info('BullMQ mail executor closed | Mode: %s', this.mode);\n }\n\n getWorkerCount(): number {\n return this.workerHelpers.length;\n }\n\n getMode(): TConstValue<typeof BullMQExecutorModes> {\n return this.mode;\n }\n\n async enqueueVerificationEmail(\n email: string,\n options?: IMailQueueOptions,\n ): Promise<IMailQueueResult> {\n if (this.mode === BullMQExecutorModes.WORKER_ONLY) {\n throw getError({\n message: 'Cannot enqueue jobs in worker-only mode. Set mode to \"queue-only\" or \"both\".',\n });\n }\n\n if (!this.queueHelper) {\n throw getError({\n message: 'Queue helper not initialized. This should not happen in queue-enabled mode.',\n });\n }\n\n if (!this.processor && this.mode !== BullMQExecutorModes.QUEUE_ONLY) {\n throw getError({ message: MailExecutorErrors.PROCESSOR_NOT_SET });\n }\n\n const jobId = `job_${++this.jobIdCounter}_${Date.now()}`;\n const job: IQueueJobPayload = {\n id: jobId,\n email,\n options,\n attempts: 0,\n scheduledAt: Date.now() + (options?.delay ?? 0),\n };\n\n this.logger\n .for(this.enqueueVerificationEmail.name)\n .info(\n 'Queuing email to BullMQ/Redis | jobId: %s | email: %s | mode: %s',\n jobId,\n email,\n this.mode,\n );\n\n const bullJob = await this.queueHelper.queue.add('send-verification-email', job, {\n priority: options?.priority,\n delay: options?.delay,\n attempts: options?.attempts ?? 3,\n backoff: {\n type: options?.backoff?.type ?? MailBackoffTypes.EXPONENTIAL,\n delay: options?.backoff?.delay ?? 1000,\n },\n removeOnComplete: true,\n removeOnFail: false,\n });\n\n return {\n jobId: bullJob.id ?? jobId,\n queued: true,\n message: 'Email queued successfully (BullMQ/Redis)',\n };\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"nodemail-transporter.helper.d.ts","sourceRoot":"","sources":["../../../../../../src/components/mail/helpers/transporters/nodemail-transporter.helper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAEzD,OAAO,KAAK,EACV,YAAY,EACZ,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,cAAc,CAAC;AAOtB,qBAAa,yBAA0B,SAAQ,UAAW,YAAW,cAAc;IACjF,OAAO,CAAC,WAAW,CAAU;IAC7B,OAAO,CAAC,MAAM,CAAC,CAAoB;gBAEvB,IAAI,EAAE;QAAE,MAAM,EAAE,iBAAiB,CAAC;QAAC,MAAM,CAAC,EAAE,iBAAiB,CAAA;KAAE;IAO3E,SAAS,CAAC,MAAM,EAAE,iBAAiB;IAInC,8FAA8F;IAC9F,SAAS,CAAC,gBAAgB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO;IAKxD,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC;IA2CrD,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAY1B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAI7B"}
1
+ {"version":3,"file":"nodemail-transporter.helper.d.ts","sourceRoot":"","sources":["../../../../../../src/components/mail/helpers/transporters/nodemail-transporter.helper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAEzD,OAAO,KAAK,EACV,YAAY,EACZ,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,cAAc,CAAC;AAOtB,qBAAa,yBAA0B,SAAQ,UAAW,YAAW,cAAc;IACjF,OAAO,CAAC,WAAW,CAAU;IAC7B,OAAO,CAAC,MAAM,CAAC,CAAoB;gBAEvB,IAAI,EAAE;QAAE,MAAM,EAAE,iBAAiB,CAAC;QAAC,MAAM,CAAC,EAAE,iBAAiB,CAAA;KAAE;IAO3E,SAAS,CAAC,MAAM,EAAE,iBAAiB;IAInC,8FAA8F;IAC9F,SAAS,CAAC,gBAAgB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO;IAKxD,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC;IA6CrD,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAY1B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAI7B"}
@@ -20,9 +20,11 @@ export class NodemailerTransportHelper extends BaseHelper {
20
20
  try {
21
21
  assertMailBodyIsText({ message });
22
22
  // No root: MailService resolved paths already; one that reaches here is refused, not read.
23
- const attachments = message.attachments
24
- ? await resolveMailAttachments({ attachments: message.attachments })
25
- : undefined;
23
+ let attachments;
24
+ if (message.attachments) {
25
+ const resolved = await resolveMailAttachments({ attachments: message.attachments });
26
+ attachments = resolved;
27
+ }
26
28
  const mailOptions = {
27
29
  from: message.from,
28
30
  to: Array.isArray(message.to) ? message.to.join(', ') : message.to,
@@ -1 +1 @@
1
- {"version":3,"file":"nodemail-transporter.helper.js","sourceRoot":"","sources":["../../../../../../src/components/mail/helpers/transporters/nodemail-transporter.helper.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AnyType } from '@venizia/ignis-helpers/common';\nimport { BaseHelper } from '@venizia/ignis-helpers/core';\nimport { ModuleUtility } from '@venizia/ignis-helpers';\nimport type {\n IMailMessage,\n IMailSendResult,\n IMailTransport,\n TNodemailerConfig,\n TNodemailerModule,\n} from '../../common';\nimport {\n releaseAttachmentStreams,\n resolveMailAttachments,\n} from '../../utilities/attachment.utility';\nimport { assertMailBodyIsText } from '../../utilities/body.utility';\n\nexport class NodemailerTransportHelper extends BaseHelper implements IMailTransport {\n private transporter: AnyType;\n private module?: TNodemailerModule;\n\n constructor(opts: { config: TNodemailerConfig; module?: TNodemailerModule }) {\n super({ scope: NodemailerTransportHelper.name });\n\n this.module = opts.module;\n this.configure(opts.config);\n }\n\n configure(config: TNodemailerConfig) {\n this.transporter = this.buildTransporter(config);\n }\n\n /** Client factory seam - overridden in tests to run the helper without a real SMTP client. */\n protected buildTransporter(config: TNodemailerConfig): AnyType {\n const nodemailer = this.module ?? ModuleUtility.loadSync<AnyType>({ module: 'nodemailer' });\n return nodemailer.createTransport(config);\n }\n\n async send(message: IMailMessage): Promise<IMailSendResult> {\n try {\n assertMailBodyIsText({ message });\n\n // No root: MailService resolved paths already; one that reaches here is refused, not read.\n const attachments = message.attachments\n ? await resolveMailAttachments({ attachments: message.attachments })\n : undefined;\n\n const mailOptions = {\n from: message.from,\n to: Array.isArray(message.to) ? message.to.join(', ') : message.to,\n cc: message.cc,\n bcc: message.bcc,\n replyTo: message.replyTo,\n subject: message.subject,\n text: message.text,\n html: message.html,\n attachments,\n headers: message.headers,\n // Nodemailer's own switches: no field it is handed can make it open a file or fetch a URL.\n disableFileAccess: true,\n disableUrlAccess: true,\n };\n\n this.logger.for(this.send.name).debug('Sending email with nodemailer to: %s', mailOptions.to);\n const info = await this.transporter.sendMail(mailOptions);\n\n return {\n success: true,\n messageId: info.messageId,\n response: info.response,\n };\n } catch (error) {\n this.logger.for(this.send.name).error('Nodemailer send failed: %s', error);\n releaseAttachmentStreams({ attachments: message.attachments });\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n\n async verify(): Promise<boolean> {\n try {\n this.logger.for(this.verify.name).info('Verifying SMTP connection');\n await this.transporter.verify();\n this.logger.for(this.verify.name).info('SMTP connection verified successfully');\n return true;\n } catch (error) {\n this.logger.for(this.verify.name).error('SMTP verification failed: %s', error);\n return false;\n }\n }\n\n async close(): Promise<void> {\n this.logger.for(this.close.name).info('Closing nodemailer transport');\n this.transporter.close();\n }\n}\n"]}
1
+ {"version":3,"file":"nodemail-transporter.helper.js","sourceRoot":"","sources":["../../../../../../src/components/mail/helpers/transporters/nodemail-transporter.helper.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AnyType } from '@venizia/ignis-helpers/common';\nimport { BaseHelper } from '@venizia/ignis-helpers/core';\nimport { ModuleUtility } from '@venizia/ignis-helpers';\nimport type {\n IMailMessage,\n IMailSendResult,\n IMailTransport,\n TNodemailerConfig,\n TNodemailerModule,\n} from '../../common';\nimport {\n releaseAttachmentStreams,\n resolveMailAttachments,\n} from '../../utilities/attachment.utility';\nimport { assertMailBodyIsText } from '../../utilities/body.utility';\n\nexport class NodemailerTransportHelper extends BaseHelper implements IMailTransport {\n private transporter: AnyType;\n private module?: TNodemailerModule;\n\n constructor(opts: { config: TNodemailerConfig; module?: TNodemailerModule }) {\n super({ scope: NodemailerTransportHelper.name });\n\n this.module = opts.module;\n this.configure(opts.config);\n }\n\n configure(config: TNodemailerConfig) {\n this.transporter = this.buildTransporter(config);\n }\n\n /** Client factory seam - overridden in tests to run the helper without a real SMTP client. */\n protected buildTransporter(config: TNodemailerConfig): AnyType {\n const nodemailer = this.module ?? ModuleUtility.loadSync<AnyType>({ module: 'nodemailer' });\n return nodemailer.createTransport(config);\n }\n\n async send(message: IMailMessage): Promise<IMailSendResult> {\n try {\n assertMailBodyIsText({ message });\n\n // No root: MailService resolved paths already; one that reaches here is refused, not read.\n let attachments: Awaited<ReturnType<typeof resolveMailAttachments>> | undefined;\n if (message.attachments) {\n const resolved = await resolveMailAttachments({ attachments: message.attachments });\n attachments = resolved;\n }\n\n const mailOptions = {\n from: message.from,\n to: Array.isArray(message.to) ? message.to.join(', ') : message.to,\n cc: message.cc,\n bcc: message.bcc,\n replyTo: message.replyTo,\n subject: message.subject,\n text: message.text,\n html: message.html,\n attachments,\n headers: message.headers,\n // Nodemailer's own switches: no field it is handed can make it open a file or fetch a URL.\n disableFileAccess: true,\n disableUrlAccess: true,\n };\n\n this.logger.for(this.send.name).debug('Sending email with nodemailer to: %s', mailOptions.to);\n const info = await this.transporter.sendMail(mailOptions);\n\n return {\n success: true,\n messageId: info.messageId,\n response: info.response,\n };\n } catch (error) {\n this.logger.for(this.send.name).error('Nodemailer send failed: %s', error);\n releaseAttachmentStreams({ attachments: message.attachments });\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n }\n\n async verify(): Promise<boolean> {\n try {\n this.logger.for(this.verify.name).info('Verifying SMTP connection');\n await this.transporter.verify();\n this.logger.for(this.verify.name).info('SMTP connection verified successfully');\n return true;\n } catch (error) {\n this.logger.for(this.verify.name).error('SMTP verification failed: %s', error);\n return false;\n }\n }\n\n async close(): Promise<void> {\n this.logger.for(this.close.name).info('Closing nodemailer transport');\n this.transporter.close();\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"mail.service.d.ts","sourceRoot":"","sources":["../../../../../src/components/mail/services/mail.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,KAAK,EACV,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,cAAc,EACd,YAAY,EACb,MAAM,WAAW,CAAC;AAInB,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAK7D,qBAAa,WAAY,SAAQ,WAAY,YAAW,YAAY;IAGhE,SAAS,CAAC,OAAO,EAAE,YAAY;IAE/B,SAAS,CAAC,SAAS,EAAE,cAAc;IAEnC,SAAS,CAAC,cAAc,CAAC,EAAE,mBAAmB;gBAJpC,OAAO,EAAE,YAAY,EAErB,SAAS,EAAE,cAAc,EAEzB,cAAc,CAAC,EAAE,mBAAmB,YAAA;IAQ1C,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC;IAiDrD,SAAS,CACb,QAAQ,EAAE,YAAY,EAAE,EACxB,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GACjC,OAAO,CAAC,eAAe,EAAE,CAAC;IA4CvB,YAAY,CAAC,IAAI,EAAE;QACvB,YAAY,EAAE,MAAM,CAAC;QACrB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC9B,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAC9B,OAAO,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;KACjC,GAAG,OAAO,CAAC,eAAe,CAAC;IA2CtB,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAiBhC,SAAS,CAAC,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI;IAyBtD,SAAS,CAAC,cAAc,IAAI,MAAM;CAcnC"}
1
+ {"version":3,"file":"mail.service.d.ts","sourceRoot":"","sources":["../../../../../src/components/mail/services/mail.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,KAAK,EACV,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,cAAc,EACd,YAAY,EACb,MAAM,WAAW,CAAC;AAInB,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAK7D,qBAAa,WAAY,SAAQ,WAAY,YAAW,YAAY;IAGhE,SAAS,CAAC,OAAO,EAAE,YAAY;IAE/B,SAAS,CAAC,SAAS,EAAE,cAAc;IAEnC,SAAS,CAAC,cAAc,CAAC,EAAE,mBAAmB;gBAJpC,OAAO,EAAE,YAAY,EAErB,SAAS,EAAE,cAAc,EAEzB,cAAc,CAAC,EAAE,mBAAmB,YAAA;IAQ1C,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,eAAe,CAAC;IAkDrD,SAAS,CACb,QAAQ,EAAE,YAAY,EAAE,EACxB,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GACjC,OAAO,CAAC,eAAe,EAAE,CAAC;IA6CvB,YAAY,CAAC,IAAI,EAAE;QACvB,YAAY,EAAE,MAAM,CAAC;QACrB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC9B,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAC9B,OAAO,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;KACjC,GAAG,OAAO,CAAC,eAAe,CAAC;IA4CtB,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAiBhC,SAAS,CAAC,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI;IAyBtD,SAAS,CAAC,cAAc,IAAI,MAAM;CAcnC"}
@@ -38,11 +38,12 @@ let MailService = MailService_1 = class MailService extends BaseService {
38
38
  };
39
39
  // The one place the attachment policy applies: every transport receives bytes, never a path.
40
40
  if (message.attachments) {
41
- emailMessage.attachments = await resolveMailAttachments({
41
+ const result2 = await resolveMailAttachments({
42
42
  attachments: message.attachments,
43
43
  attachmentRoot: this.options.attachmentRoot,
44
44
  maxBytes: this.options.maxAttachmentBytes ?? MailDefaults.MAX_ATTACHMENT_BYTES,
45
45
  });
46
+ emailMessage.attachments = result2;
46
47
  }
47
48
  this.logger.for(this.send.name).debug('Sending email to: %s', emailMessage.to);
48
49
  const result = await this.transport.send(emailMessage);
@@ -80,7 +81,8 @@ let MailService = MailService_1 = class MailService extends BaseService {
80
81
  .info('Sending batch of %d emails with concurrency: %d', messages.length, concurrency);
81
82
  const tasks = messages.map((message) => async () => {
82
83
  try {
83
- return await this.send(message);
84
+ const result = await this.send(message);
85
+ return result;
84
86
  }
85
87
  catch (error) {
86
88
  return {
@@ -139,7 +141,8 @@ let MailService = MailService_1 = class MailService extends BaseService {
139
141
  html,
140
142
  ...options,
141
143
  };
142
- return await this.send(message);
144
+ const result = await this.send(message);
145
+ return result;
143
146
  }
144
147
  catch (error) {
145
148
  this.logger.for(this.sendTemplate.name).error('Error sending template email: %s', error);
@@ -1 +1 @@
1
- {"version":3,"file":"mail.service.js","sourceRoot":"","sources":["../../../../../src/components/mail/services/mail.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { HTTP } from '@venizia/ignis-helpers/common';\nimport { BaseService } from '@venizia/ignis-kernel';\nimport type {\n IMailMessage,\n IMailSendResult,\n IMailService,\n IMailTemplateEngine,\n IMailTransport,\n TMailOptions,\n} from '../common';\nimport { MailDefaults, MailErrorCodes, MailErrors, MailKeys } from '../common';\nimport { inject } from '@/base/metadata';\nimport { getError, isApplicationError } from '@venizia/ignis-helpers/core';\nimport { type AnyType } from '@venizia/ignis-helpers/common';\nimport { executePromiseWithLimit } from '@venizia/ignis-helpers';\nimport { releaseAttachmentStreams, resolveMailAttachments } from '../utilities/attachment.utility';\nimport { assertMailBodyIsText } from '../utilities/body.utility';\n\nexport class MailService extends BaseService implements IMailService {\n constructor(\n @inject({ key: MailKeys.MAIL_OPTIONS })\n protected options: TMailOptions,\n @inject({ key: MailKeys.MAIL_TRANSPORT_INSTANCE })\n protected transport: IMailTransport,\n @inject({ key: MailKeys.MAIL_TEMPLATE_ENGINE, isOptional: true })\n protected templateEngine?: IMailTemplateEngine,\n ) {\n super({ scope: MailService.name });\n this.logger\n .for(this.constructor.name)\n .info('Mail service initialized with provider: %s', this.options.provider);\n }\n\n async send(message: IMailMessage): Promise<IMailSendResult> {\n try {\n this.validateMessage(message);\n\n const emailMessage: IMailMessage = {\n ...message,\n from: message.from ?? this.getDefaultFrom(),\n };\n\n // The one place the attachment policy applies: every transport receives bytes, never a path.\n if (message.attachments) {\n emailMessage.attachments = await resolveMailAttachments({\n attachments: message.attachments,\n attachmentRoot: this.options.attachmentRoot,\n maxBytes: this.options.maxAttachmentBytes ?? MailDefaults.MAX_ATTACHMENT_BYTES,\n });\n }\n\n this.logger.for(this.send.name).debug('Sending email to: %s', emailMessage.to);\n const result = await this.transport.send(emailMessage);\n\n if (result.success) {\n this.logger\n .for(this.send.name)\n .debug('Email sent successfully. MessageId: %s', result.messageId);\n } else {\n this.logger.for(this.send.name).debug('Email send failed: %s', result.error);\n }\n\n return result;\n } catch (error) {\n this.logger.for(this.send.name).error('Error sending email: %s', error);\n releaseAttachmentStreams({ attachments: message.attachments });\n\n // An error the framework already shaped carries its own status and message code; re-wrapping as 500 would report a caller mistake as a server fault.\n if (isApplicationError(error)) {\n throw error;\n }\n\n throw getError({\n statusCode: HTTP.ResultCodes.RS_5.InternalServerError,\n messageCode: MailErrorCodes.SEND_FAILED,\n // The raw text can carry a file path or a provider detail; it rides in `cause`, which only development responses show.\n message: 'Failed to send email',\n cause: error,\n });\n }\n }\n\n async sendBatch(\n messages: IMailMessage[],\n options?: { concurrency?: number },\n ): Promise<IMailSendResult[]> {\n try {\n const concurrency = options?.concurrency ?? MailDefaults.BATCH_CONCURRENCY;\n this.logger\n .for(this.sendBatch.name)\n .info('Sending batch of %d emails with concurrency: %d', messages.length, concurrency);\n\n const tasks = messages.map((message) => async () => {\n try {\n return await this.send(message);\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n });\n\n const results = await executePromiseWithLimit<IMailSendResult>({\n tasks,\n limit: concurrency,\n });\n\n const successCount = results.filter((r) => r.success).length;\n this.logger\n .for(this.sendBatch.name)\n .info(\n 'Batch send completed. Success: %d, Failed: %d',\n successCount,\n results.length - successCount,\n );\n\n return results;\n } catch (error) {\n this.logger.for(this.sendBatch.name).error('Error sending batch emails: %s', error);\n throw getError({\n statusCode: HTTP.ResultCodes.RS_5.InternalServerError,\n messageCode: MailErrorCodes.BATCH_SEND_FAILED,\n message: 'Failed to send batch emails',\n cause: error,\n });\n }\n }\n\n async sendTemplate(opts: {\n templateName: string;\n data: Record<string, AnyType>;\n recipients: string | string[];\n options?: Partial<IMailMessage>;\n }): Promise<IMailSendResult> {\n const { templateName, data, recipients, options } = opts;\n\n try {\n if (!this.templateEngine) {\n throw getError({\n statusCode: HTTP.ResultCodes.RS_5.InternalServerError,\n messageCode: MailErrorCodes.INVALID_CONFIGURATION,\n message: 'Template engine not configured',\n });\n }\n\n this.logger.for(this.sendTemplate.name).debug('Rendering template: %s', templateName);\n const html = this.templateEngine.render({\n templateName,\n data,\n requireValidate: options?.requireValidate,\n });\n\n const templateData = this.templateEngine.getTemplate(templateName);\n\n const message: IMailMessage = {\n to: recipients,\n subject:\n options?.subject ??\n (templateData?.subject !== undefined\n ? this.templateEngine.render({\n templateData: templateData.subject,\n data,\n requireValidate: options?.requireValidate,\n })\n : 'No Subject'),\n html,\n ...options,\n };\n\n return await this.send(message);\n } catch (error) {\n this.logger.for(this.sendTemplate.name).error('Error sending template email: %s', error);\n throw error;\n }\n }\n\n async verify(): Promise<boolean> {\n try {\n this.logger.for(this.verify.name).debug('Verifying mail transport connection');\n const isValid = await this.transport.verify();\n this.logger.for(this.verify.name).debug('Verification result: %s', isValid);\n return isValid;\n } catch (error) {\n this.logger.for(this.verify.name).error('Verification failed: %s', error);\n throw getError({\n statusCode: HTTP.ResultCodes.RS_5.InternalServerError,\n messageCode: MailErrorCodes.VERIFICATION_FAILED,\n message: 'Mail transport verification failed',\n cause: error,\n });\n }\n }\n\n protected validateMessage(message: IMailMessage): void {\n if (!message.to || (Array.isArray(message.to) && message.to.length === 0)) {\n throw getError({\n error: MailErrors.INVALID_RECIPIENT,\n message: 'Recipient email address is required',\n });\n }\n\n if (!message.subject) {\n throw getError({\n error: MailErrors.INVALID_CONFIGURATION,\n message: 'Email subject is required',\n });\n }\n\n if (!message.text && !message.html) {\n throw getError({\n error: MailErrors.INVALID_CONFIGURATION,\n message: 'Email must have either text or html content',\n });\n }\n\n assertMailBodyIsText({ message });\n }\n\n protected getDefaultFrom(): string {\n const { from, fromName } = this.options;\n\n // A display name is only renderable around an address: `\"Name\" <undefined>` is not a sender.\n if (!from) {\n return MailDefaults.FALLBACK_FROM;\n }\n\n if (!fromName) {\n return from;\n }\n\n return `\"${fromName}\" <${from}>`;\n }\n}\n"]}
1
+ {"version":3,"file":"mail.service.js","sourceRoot":"","sources":["../../../../../src/components/mail/services/mail.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { HTTP } from '@venizia/ignis-helpers/common';\nimport { BaseService } from '@venizia/ignis-kernel';\nimport type {\n IMailMessage,\n IMailSendResult,\n IMailService,\n IMailTemplateEngine,\n IMailTransport,\n TMailOptions,\n} from '../common';\nimport { MailDefaults, MailErrorCodes, MailErrors, MailKeys } from '../common';\nimport { inject } from '@/base/metadata';\nimport { getError, isApplicationError } from '@venizia/ignis-helpers/core';\nimport { type AnyType } from '@venizia/ignis-helpers/common';\nimport { executePromiseWithLimit } from '@venizia/ignis-helpers';\nimport { releaseAttachmentStreams, resolveMailAttachments } from '../utilities/attachment.utility';\nimport { assertMailBodyIsText } from '../utilities/body.utility';\n\nexport class MailService extends BaseService implements IMailService {\n constructor(\n @inject({ key: MailKeys.MAIL_OPTIONS })\n protected options: TMailOptions,\n @inject({ key: MailKeys.MAIL_TRANSPORT_INSTANCE })\n protected transport: IMailTransport,\n @inject({ key: MailKeys.MAIL_TEMPLATE_ENGINE, isOptional: true })\n protected templateEngine?: IMailTemplateEngine,\n ) {\n super({ scope: MailService.name });\n this.logger\n .for(this.constructor.name)\n .info('Mail service initialized with provider: %s', this.options.provider);\n }\n\n async send(message: IMailMessage): Promise<IMailSendResult> {\n try {\n this.validateMessage(message);\n\n const emailMessage: IMailMessage = {\n ...message,\n from: message.from ?? this.getDefaultFrom(),\n };\n\n // The one place the attachment policy applies: every transport receives bytes, never a path.\n if (message.attachments) {\n const result2 = await resolveMailAttachments({\n attachments: message.attachments,\n attachmentRoot: this.options.attachmentRoot,\n maxBytes: this.options.maxAttachmentBytes ?? MailDefaults.MAX_ATTACHMENT_BYTES,\n });\n emailMessage.attachments = result2;\n }\n\n this.logger.for(this.send.name).debug('Sending email to: %s', emailMessage.to);\n const result = await this.transport.send(emailMessage);\n\n if (result.success) {\n this.logger\n .for(this.send.name)\n .debug('Email sent successfully. MessageId: %s', result.messageId);\n } else {\n this.logger.for(this.send.name).debug('Email send failed: %s', result.error);\n }\n\n return result;\n } catch (error) {\n this.logger.for(this.send.name).error('Error sending email: %s', error);\n releaseAttachmentStreams({ attachments: message.attachments });\n\n // An error the framework already shaped carries its own status and message code; re-wrapping as 500 would report a caller mistake as a server fault.\n if (isApplicationError(error)) {\n throw error;\n }\n\n throw getError({\n statusCode: HTTP.ResultCodes.RS_5.InternalServerError,\n messageCode: MailErrorCodes.SEND_FAILED,\n // The raw text can carry a file path or a provider detail; it rides in `cause`, which only development responses show.\n message: 'Failed to send email',\n cause: error,\n });\n }\n }\n\n async sendBatch(\n messages: IMailMessage[],\n options?: { concurrency?: number },\n ): Promise<IMailSendResult[]> {\n try {\n const concurrency = options?.concurrency ?? MailDefaults.BATCH_CONCURRENCY;\n this.logger\n .for(this.sendBatch.name)\n .info('Sending batch of %d emails with concurrency: %d', messages.length, concurrency);\n\n const tasks = messages.map((message) => async () => {\n try {\n const result = await this.send(message);\n return result;\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n };\n }\n });\n\n const results = await executePromiseWithLimit<IMailSendResult>({\n tasks,\n limit: concurrency,\n });\n\n const successCount = results.filter((r) => r.success).length;\n this.logger\n .for(this.sendBatch.name)\n .info(\n 'Batch send completed. Success: %d, Failed: %d',\n successCount,\n results.length - successCount,\n );\n\n return results;\n } catch (error) {\n this.logger.for(this.sendBatch.name).error('Error sending batch emails: %s', error);\n throw getError({\n statusCode: HTTP.ResultCodes.RS_5.InternalServerError,\n messageCode: MailErrorCodes.BATCH_SEND_FAILED,\n message: 'Failed to send batch emails',\n cause: error,\n });\n }\n }\n\n async sendTemplate(opts: {\n templateName: string;\n data: Record<string, AnyType>;\n recipients: string | string[];\n options?: Partial<IMailMessage>;\n }): Promise<IMailSendResult> {\n const { templateName, data, recipients, options } = opts;\n\n try {\n if (!this.templateEngine) {\n throw getError({\n statusCode: HTTP.ResultCodes.RS_5.InternalServerError,\n messageCode: MailErrorCodes.INVALID_CONFIGURATION,\n message: 'Template engine not configured',\n });\n }\n\n this.logger.for(this.sendTemplate.name).debug('Rendering template: %s', templateName);\n const html = this.templateEngine.render({\n templateName,\n data,\n requireValidate: options?.requireValidate,\n });\n\n const templateData = this.templateEngine.getTemplate(templateName);\n\n const message: IMailMessage = {\n to: recipients,\n subject:\n options?.subject ??\n (templateData?.subject !== undefined\n ? this.templateEngine.render({\n templateData: templateData.subject,\n data,\n requireValidate: options?.requireValidate,\n })\n : 'No Subject'),\n html,\n ...options,\n };\n\n const result = await this.send(message);\n return result;\n } catch (error) {\n this.logger.for(this.sendTemplate.name).error('Error sending template email: %s', error);\n throw error;\n }\n }\n\n async verify(): Promise<boolean> {\n try {\n this.logger.for(this.verify.name).debug('Verifying mail transport connection');\n const isValid = await this.transport.verify();\n this.logger.for(this.verify.name).debug('Verification result: %s', isValid);\n return isValid;\n } catch (error) {\n this.logger.for(this.verify.name).error('Verification failed: %s', error);\n throw getError({\n statusCode: HTTP.ResultCodes.RS_5.InternalServerError,\n messageCode: MailErrorCodes.VERIFICATION_FAILED,\n message: 'Mail transport verification failed',\n cause: error,\n });\n }\n }\n\n protected validateMessage(message: IMailMessage): void {\n if (!message.to || (Array.isArray(message.to) && message.to.length === 0)) {\n throw getError({\n error: MailErrors.INVALID_RECIPIENT,\n message: 'Recipient email address is required',\n });\n }\n\n if (!message.subject) {\n throw getError({\n error: MailErrors.INVALID_CONFIGURATION,\n message: 'Email subject is required',\n });\n }\n\n if (!message.text && !message.html) {\n throw getError({\n error: MailErrors.INVALID_CONFIGURATION,\n message: 'Email must have either text or html content',\n });\n }\n\n assertMailBodyIsText({ message });\n }\n\n protected getDefaultFrom(): string {\n const { from, fromName } = this.options;\n\n // A display name is only renderable around an address: `\"Name\" <undefined>` is not a sender.\n if (!from) {\n return MailDefaults.FALLBACK_FROM;\n }\n\n if (!fromName) {\n return from;\n }\n\n return `\"${fromName}\" <${from}>`;\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"attachment.utility.d.ts","sourceRoot":"","sources":["../../../../../src/components/mail/utilities/attachment.utility.ts"],"names":[],"mappings":"AAMA,OAAO,EAAc,KAAK,eAAe,EAAE,MAAM,WAAW,CAAC;AAyJ7D;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GAAU,MAAM;IAChD,UAAU,EAAE,eAAe,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,KAAG,OAAO,CAAC,MAAM,CAgCjB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GAAU,MAAM;IACjD,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,KAAG,OAAO,CAAC,eAAe,EAAE,CAwC5B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,GAAI,MAAM;IAAE,WAAW,CAAC,EAAE,eAAe,EAAE,CAAA;CAAE,KAAG,IA0BpF,CAAC"}
1
+ {"version":3,"file":"attachment.utility.d.ts","sourceRoot":"","sources":["../../../../../src/components/mail/utilities/attachment.utility.ts"],"names":[],"mappings":"AAMA,OAAO,EAAc,KAAK,eAAe,EAAE,MAAM,WAAW,CAAC;AA2J7D;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GAAU,MAAM;IAChD,UAAU,EAAE,eAAe,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,KAAG,OAAO,CAAC,MAAM,CAgCjB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GAAU,MAAM;IACjD,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,KAAG,OAAO,CAAC,eAAe,EAAE,CAwC5B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,GAAI,MAAM;IAAE,WAAW,CAAC,EAAE,eAAe,EAAE,CAAA;CAAE,KAAG,IA0BpF,CAAC"}
@@ -67,10 +67,11 @@ const resolveConfinedPath = async (opts) => {
67
67
  let filePath;
68
68
  // `resolve` throws synchronously on a non-string `path` from an untyped caller; it lands here too.
69
69
  try {
70
- [rootPath, filePath] = await Promise.all([
70
+ const resolved = await Promise.all([
71
71
  realpath(attachmentRoot),
72
72
  realpath(resolve(attachmentRoot, path)),
73
73
  ]);
74
+ [rootPath, filePath] = resolved;
74
75
  }
75
76
  catch (error) {
76
77
  throw buildPathRefusedError({ reason: PATH_OUTSIDE_REASON, cause: error });
@@ -106,7 +107,8 @@ const readConfinedFile = async (opts) => {
106
107
  autoClose: false,
107
108
  end: maxBytes === undefined ? Infinity : maxBytes - usedBytes,
108
109
  });
109
- return await drainStream({ stream, usedBytes, maxBytes });
110
+ const result = await drainStream({ stream, usedBytes, maxBytes });
111
+ return result;
110
112
  }
111
113
  finally {
112
114
  await handle.close();
@@ -1 +1 @@
1
- {"version":3,"file":"attachment.utility.js","sourceRoot":"","sources":["../../../../../src/components/mail/utilities/attachment.utility.ts"],"names":[],"mappings":"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","sourcesContent":["import { constants } from 'node:fs';\nimport { open, realpath } from 'node:fs/promises';\nimport { basename, isAbsolute, relative, resolve, sep } from 'node:path';\nimport { ContentTypeTable } from '@venizia/ignis-helpers/common';\nimport { getError } from '@venizia/ignis-helpers/core';\nimport { LoggerFactory } from '@venizia/ignis-helpers';\nimport { MailErrors, type IMailAttachment } from '../common';\n\nconst logger = LoggerFactory.getLogger(['MailAttachments']);\n\n// O_NONBLOCK: opening a FIFO must not wait for a writer; the fstat check then refuses it.\n// Windows has neither flag; there the realpath containment check stands alone.\nconst OPEN_FLAGS = constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0) | (constants.O_NONBLOCK ?? 0);\n\n// One reason for missing, directory, unreadable and escaping paths: no existence oracle.\nconst PATH_OUTSIDE_REASON =\n 'attachment.path must name a readable file inside attachmentRoot | pass the bytes as content instead';\n\ntype TAttachmentStream = AsyncIterable<Uint8Array | string>;\n\nconst buildPathRefusedError = (opts: { reason: string; cause?: unknown }) => {\n return getError({\n error: MailErrors.ATTACHMENT_PATH_REFUSED,\n message: `Mail attachment path refused | ${opts.reason}`,\n cause: opts.cause,\n });\n};\n\nconst buildTooLargeError = (opts: { maxBytes: number }) => {\n return getError({\n error: MailErrors.ATTACHMENT_TOO_LARGE,\n message: `Mail attachments are over maxAttachmentBytes (${opts.maxBytes} bytes per message) | raise maxAttachmentBytes on the mail options, or send smaller attachments`,\n });\n};\n\nconst assertWithinLimit = (opts: { byteLength: number; usedBytes: number; maxBytes?: number }) => {\n const { byteLength, usedBytes, maxBytes } = opts;\n\n if (maxBytes === undefined || usedBytes + byteLength <= maxBytes) {\n return;\n }\n\n throw buildTooLargeError({ maxBytes });\n};\n\nconst isAttachmentStream = (value: unknown): value is TAttachmentStream => {\n return typeof value === 'object' && value !== null && Symbol.asyncIterator in value;\n};\n\nconst toBuffer = (chunk: Uint8Array | string): Buffer => {\n if (typeof chunk === 'string') {\n return Buffer.from(chunk);\n }\n\n return Buffer.isBuffer(chunk)\n ? chunk\n : Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength);\n};\n\n/** Throwing out of `for await` destroys a Node Readable and cancels a web ReadableStream. */\nconst drainStream = async (opts: {\n stream: TAttachmentStream;\n usedBytes: number;\n maxBytes?: number;\n}): Promise<Buffer> => {\n const { stream, usedBytes, maxBytes } = opts;\n const chunks: Buffer[] = [];\n let streamBytes = 0;\n\n for await (const chunk of stream) {\n const buffer = toBuffer(chunk);\n streamBytes += buffer.byteLength;\n\n // Per chunk: a stream has no length up front, and the rest must never be buffered.\n assertWithinLimit({ byteLength: streamBytes, usedBytes, maxBytes });\n chunks.push(buffer);\n }\n\n return Buffer.concat(chunks, streamBytes);\n};\n\n/**\n * Both sides go through realpath, so neither `..` nor a symlink can leave the root. Not closed: a\n * directory inside the root swapped for a symlink between this check and `open` - that takes write\n * access inside the root, so keep the root writable only by what the application trusts.\n */\nconst resolveConfinedPath = async (opts: {\n path: string;\n attachmentRoot: string;\n}): Promise<string> => {\n const { path, attachmentRoot } = opts;\n\n let rootPath: string;\n let filePath: string;\n\n // `resolve` throws synchronously on a non-string `path` from an untyped caller; it lands here too.\n try {\n [rootPath, filePath] = await Promise.all([\n realpath(attachmentRoot),\n realpath(resolve(attachmentRoot, path)),\n ]);\n } catch (error) {\n throw buildPathRefusedError({ reason: PATH_OUTSIDE_REASON, cause: error });\n }\n\n const relation = relative(rootPath, filePath);\n const isOutside =\n relation === '' || relation === '..' || relation.startsWith(`..${sep}`) || isAbsolute(relation);\n\n if (isOutside) {\n throw buildPathRefusedError({ reason: PATH_OUTSIDE_REASON });\n }\n\n return filePath;\n};\n\nconst readConfinedFile = async (opts: {\n path: string;\n usedBytes: number;\n attachmentRoot?: string;\n maxBytes?: number;\n}): Promise<Buffer> => {\n const { path, usedBytes, attachmentRoot, maxBytes } = opts;\n\n if (!attachmentRoot) {\n throw buildPathRefusedError({\n reason:\n 'attachment.path is read only under attachmentRoot, which is not set | set attachmentRoot on the mail options, or pass the bytes as content',\n });\n }\n\n const filePath = await resolveConfinedPath({ path, attachmentRoot });\n const handle = await open(filePath, OPEN_FLAGS).catch((error) => {\n throw buildPathRefusedError({ reason: PATH_OUTSIDE_REASON, cause: error });\n });\n\n try {\n const stats = await handle.stat();\n\n if (!stats.isFile()) {\n throw buildPathRefusedError({ reason: PATH_OUTSIDE_REASON });\n }\n\n // Sized before reading, so a file known to be oversized is never opened for reading.\n assertWithinLimit({ byteLength: stats.size, usedBytes, maxBytes });\n\n // Counted while read, and read no further than one byte past the budget: the file may have\n // grown since fstat.\n const stream = handle.createReadStream({\n autoClose: false,\n end: maxBytes === undefined ? Infinity : maxBytes - usedBytes,\n });\n\n return await drainStream({ stream, usedBytes, maxBytes });\n } finally {\n await handle.close();\n }\n};\n\n/**\n * Reads one attachment's bytes. A string `content` is raw text unless `encoding` says otherwise\n * (nodemailer's convention); `null` counts as absent. A `path` is read only inside\n * `attachmentRoot` and refused without one. `maxBytes` caps the whole message; `usedBytes` is what\n * earlier attachments already took.\n */\nexport const readAttachmentContent = async (opts: {\n attachment: IMailAttachment;\n attachmentRoot?: string;\n maxBytes?: number;\n usedBytes?: number;\n}): Promise<Buffer> => {\n const { attachment, attachmentRoot, maxBytes, usedBytes = 0 } = opts;\n const { content } = attachment;\n\n if (content !== undefined && content !== null) {\n if (typeof content === 'string') {\n const encoding = attachment.encoding ?? 'utf-8';\n assertWithinLimit({ byteLength: Buffer.byteLength(content, encoding), usedBytes, maxBytes });\n return Buffer.from(content, encoding);\n }\n\n if (content instanceof Uint8Array) {\n assertWithinLimit({ byteLength: content.byteLength, usedBytes, maxBytes });\n return toBuffer(content);\n }\n\n if (!isAttachmentStream(content)) {\n throw getError({\n error: MailErrors.INVALID_CONFIGURATION,\n message:\n 'Invalid mail attachment | attachment.content must be a Buffer, a Uint8Array, a string or a stream',\n });\n }\n\n return drainStream({ stream: content, usedBytes, maxBytes });\n }\n\n if (attachment.path !== undefined && attachment.path !== null) {\n return readConfinedFile({ path: attachment.path, usedBytes, attachmentRoot, maxBytes });\n }\n\n return Buffer.alloc(0);\n};\n\n/**\n * Turns every attachment into `content` bytes and drops `path`, so no transport is ever handed a\n * file or URL to read on its own. `maxBytes` is shared by all attachments of the message. An\n * attachment read from `path` keeps the file's name and, when none is given, its content type.\n */\nexport const resolveMailAttachments = async (opts: {\n attachments: IMailAttachment[];\n attachmentRoot?: string;\n maxBytes?: number;\n}): Promise<IMailAttachment[]> => {\n const { attachments, attachmentRoot, maxBytes } = opts;\n const resolved: IMailAttachment[] = [];\n let usedBytes = 0;\n\n // Sequential on purpose: each read spends the budget the previous ones left.\n for (const attachment of attachments) {\n const { path, href, raw, content, encoding, ...rest } = attachment;\n\n // nodemailer reads both on its own - `href` fetches a URL, `raw` can carry a `path`.\n if ((href !== undefined && href !== null) || (raw !== undefined && raw !== null)) {\n throw buildPathRefusedError({\n reason: 'attachment.href and attachment.raw are not supported | pass the bytes as content',\n });\n }\n\n const bytes = await readAttachmentContent({\n attachment: { content, encoding, path },\n attachmentRoot,\n maxBytes,\n usedBytes,\n });\n\n usedBytes += bytes.byteLength;\n\n const isReadFromPath = (content === undefined || content === null) && typeof path === 'string';\n const filename = rest.filename ?? (isReadFromPath ? basename(path) : undefined);\n const contentType =\n rest.contentType ??\n (isReadFromPath && filename ? ContentTypeTable.resolve({ filename }) : undefined);\n\n resolved.push({\n ...rest,\n ...(filename === undefined ? {} : { filename }),\n ...(contentType === undefined ? {} : { contentType }),\n content: bytes,\n });\n }\n\n return resolved;\n};\n\n/**\n * Releases every attachment stream of a refused send, read or not, so none keeps a descriptor\n * open. A drained stream is already closed; releasing it again is harmless.\n */\nexport const releaseAttachmentStreams = (opts: { attachments?: IMailAttachment[] }): void => {\n for (const attachment of opts.attachments ?? []) {\n const { content } = attachment;\n\n if (content instanceof ReadableStream) {\n if (!content.locked) {\n content.cancel().catch((error) => {\n logger.warn(\n '[releaseAttachmentStreams] Cancelling a web stream failed | error: %s',\n error,\n );\n });\n }\n\n continue;\n }\n\n if (\n typeof content === 'object' &&\n content !== null &&\n 'destroy' in content &&\n typeof content.destroy === 'function'\n ) {\n content.destroy();\n }\n }\n};\n"]}
1
+ {"version":3,"file":"attachment.utility.js","sourceRoot":"","sources":["../../../../../src/components/mail/utilities/attachment.utility.ts"],"names":[],"mappings":"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","sourcesContent":["import { constants } from 'node:fs';\nimport { open, realpath } from 'node:fs/promises';\nimport { basename, isAbsolute, relative, resolve, sep } from 'node:path';\nimport { ContentTypeTable } from '@venizia/ignis-helpers/common';\nimport { getError } from '@venizia/ignis-helpers/core';\nimport { LoggerFactory } from '@venizia/ignis-helpers';\nimport { MailErrors, type IMailAttachment } from '../common';\n\nconst logger = LoggerFactory.getLogger(['MailAttachments']);\n\n// O_NONBLOCK: opening a FIFO must not wait for a writer; the fstat check then refuses it.\n// Windows has neither flag; there the realpath containment check stands alone.\nconst OPEN_FLAGS = constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0) | (constants.O_NONBLOCK ?? 0);\n\n// One reason for missing, directory, unreadable and escaping paths: no existence oracle.\nconst PATH_OUTSIDE_REASON =\n 'attachment.path must name a readable file inside attachmentRoot | pass the bytes as content instead';\n\ntype TAttachmentStream = AsyncIterable<Uint8Array | string>;\n\nconst buildPathRefusedError = (opts: { reason: string; cause?: unknown }) => {\n return getError({\n error: MailErrors.ATTACHMENT_PATH_REFUSED,\n message: `Mail attachment path refused | ${opts.reason}`,\n cause: opts.cause,\n });\n};\n\nconst buildTooLargeError = (opts: { maxBytes: number }) => {\n return getError({\n error: MailErrors.ATTACHMENT_TOO_LARGE,\n message: `Mail attachments are over maxAttachmentBytes (${opts.maxBytes} bytes per message) | raise maxAttachmentBytes on the mail options, or send smaller attachments`,\n });\n};\n\nconst assertWithinLimit = (opts: { byteLength: number; usedBytes: number; maxBytes?: number }) => {\n const { byteLength, usedBytes, maxBytes } = opts;\n\n if (maxBytes === undefined || usedBytes + byteLength <= maxBytes) {\n return;\n }\n\n throw buildTooLargeError({ maxBytes });\n};\n\nconst isAttachmentStream = (value: unknown): value is TAttachmentStream => {\n return typeof value === 'object' && value !== null && Symbol.asyncIterator in value;\n};\n\nconst toBuffer = (chunk: Uint8Array | string): Buffer => {\n if (typeof chunk === 'string') {\n return Buffer.from(chunk);\n }\n\n return Buffer.isBuffer(chunk)\n ? chunk\n : Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength);\n};\n\n/** Throwing out of `for await` destroys a Node Readable and cancels a web ReadableStream. */\nconst drainStream = async (opts: {\n stream: TAttachmentStream;\n usedBytes: number;\n maxBytes?: number;\n}): Promise<Buffer> => {\n const { stream, usedBytes, maxBytes } = opts;\n const chunks: Buffer[] = [];\n let streamBytes = 0;\n\n for await (const chunk of stream) {\n const buffer = toBuffer(chunk);\n streamBytes += buffer.byteLength;\n\n // Per chunk: a stream has no length up front, and the rest must never be buffered.\n assertWithinLimit({ byteLength: streamBytes, usedBytes, maxBytes });\n chunks.push(buffer);\n }\n\n return Buffer.concat(chunks, streamBytes);\n};\n\n/**\n * Both sides go through realpath, so neither `..` nor a symlink can leave the root. Not closed: a\n * directory inside the root swapped for a symlink between this check and `open` - that takes write\n * access inside the root, so keep the root writable only by what the application trusts.\n */\nconst resolveConfinedPath = async (opts: {\n path: string;\n attachmentRoot: string;\n}): Promise<string> => {\n const { path, attachmentRoot } = opts;\n\n let rootPath: string;\n let filePath: string;\n\n // `resolve` throws synchronously on a non-string `path` from an untyped caller; it lands here too.\n try {\n const resolved = await Promise.all([\n realpath(attachmentRoot),\n realpath(resolve(attachmentRoot, path)),\n ]);\n [rootPath, filePath] = resolved;\n } catch (error) {\n throw buildPathRefusedError({ reason: PATH_OUTSIDE_REASON, cause: error });\n }\n\n const relation = relative(rootPath, filePath);\n const isOutside =\n relation === '' || relation === '..' || relation.startsWith(`..${sep}`) || isAbsolute(relation);\n\n if (isOutside) {\n throw buildPathRefusedError({ reason: PATH_OUTSIDE_REASON });\n }\n\n return filePath;\n};\n\nconst readConfinedFile = async (opts: {\n path: string;\n usedBytes: number;\n attachmentRoot?: string;\n maxBytes?: number;\n}): Promise<Buffer> => {\n const { path, usedBytes, attachmentRoot, maxBytes } = opts;\n\n if (!attachmentRoot) {\n throw buildPathRefusedError({\n reason:\n 'attachment.path is read only under attachmentRoot, which is not set | set attachmentRoot on the mail options, or pass the bytes as content',\n });\n }\n\n const filePath = await resolveConfinedPath({ path, attachmentRoot });\n const handle = await open(filePath, OPEN_FLAGS).catch((error) => {\n throw buildPathRefusedError({ reason: PATH_OUTSIDE_REASON, cause: error });\n });\n\n try {\n const stats = await handle.stat();\n\n if (!stats.isFile()) {\n throw buildPathRefusedError({ reason: PATH_OUTSIDE_REASON });\n }\n\n // Sized before reading, so a file known to be oversized is never opened for reading.\n assertWithinLimit({ byteLength: stats.size, usedBytes, maxBytes });\n\n // Counted while read, and read no further than one byte past the budget: the file may have\n // grown since fstat.\n const stream = handle.createReadStream({\n autoClose: false,\n end: maxBytes === undefined ? Infinity : maxBytes - usedBytes,\n });\n\n const result = await drainStream({ stream, usedBytes, maxBytes });\n return result;\n } finally {\n await handle.close();\n }\n};\n\n/**\n * Reads one attachment's bytes. A string `content` is raw text unless `encoding` says otherwise\n * (nodemailer's convention); `null` counts as absent. A `path` is read only inside\n * `attachmentRoot` and refused without one. `maxBytes` caps the whole message; `usedBytes` is what\n * earlier attachments already took.\n */\nexport const readAttachmentContent = async (opts: {\n attachment: IMailAttachment;\n attachmentRoot?: string;\n maxBytes?: number;\n usedBytes?: number;\n}): Promise<Buffer> => {\n const { attachment, attachmentRoot, maxBytes, usedBytes = 0 } = opts;\n const { content } = attachment;\n\n if (content !== undefined && content !== null) {\n if (typeof content === 'string') {\n const encoding = attachment.encoding ?? 'utf-8';\n assertWithinLimit({ byteLength: Buffer.byteLength(content, encoding), usedBytes, maxBytes });\n return Buffer.from(content, encoding);\n }\n\n if (content instanceof Uint8Array) {\n assertWithinLimit({ byteLength: content.byteLength, usedBytes, maxBytes });\n return toBuffer(content);\n }\n\n if (!isAttachmentStream(content)) {\n throw getError({\n error: MailErrors.INVALID_CONFIGURATION,\n message:\n 'Invalid mail attachment | attachment.content must be a Buffer, a Uint8Array, a string or a stream',\n });\n }\n\n return drainStream({ stream: content, usedBytes, maxBytes });\n }\n\n if (attachment.path !== undefined && attachment.path !== null) {\n return readConfinedFile({ path: attachment.path, usedBytes, attachmentRoot, maxBytes });\n }\n\n return Buffer.alloc(0);\n};\n\n/**\n * Turns every attachment into `content` bytes and drops `path`, so no transport is ever handed a\n * file or URL to read on its own. `maxBytes` is shared by all attachments of the message. An\n * attachment read from `path` keeps the file's name and, when none is given, its content type.\n */\nexport const resolveMailAttachments = async (opts: {\n attachments: IMailAttachment[];\n attachmentRoot?: string;\n maxBytes?: number;\n}): Promise<IMailAttachment[]> => {\n const { attachments, attachmentRoot, maxBytes } = opts;\n const resolved: IMailAttachment[] = [];\n let usedBytes = 0;\n\n // Sequential on purpose: each read spends the budget the previous ones left.\n for (const attachment of attachments) {\n const { path, href, raw, content, encoding, ...rest } = attachment;\n\n // nodemailer reads both on its own - `href` fetches a URL, `raw` can carry a `path`.\n if ((href !== undefined && href !== null) || (raw !== undefined && raw !== null)) {\n throw buildPathRefusedError({\n reason: 'attachment.href and attachment.raw are not supported | pass the bytes as content',\n });\n }\n\n const bytes = await readAttachmentContent({\n attachment: { content, encoding, path },\n attachmentRoot,\n maxBytes,\n usedBytes,\n });\n\n usedBytes += bytes.byteLength;\n\n const isReadFromPath = (content === undefined || content === null) && typeof path === 'string';\n const filename = rest.filename ?? (isReadFromPath ? basename(path) : undefined);\n const contentType =\n rest.contentType ??\n (isReadFromPath && filename ? ContentTypeTable.resolve({ filename }) : undefined);\n\n resolved.push({\n ...rest,\n ...(filename === undefined ? {} : { filename }),\n ...(contentType === undefined ? {} : { contentType }),\n content: bytes,\n });\n }\n\n return resolved;\n};\n\n/**\n * Releases every attachment stream of a refused send, read or not, so none keeps a descriptor\n * open. A drained stream is already closed; releasing it again is harmless.\n */\nexport const releaseAttachmentStreams = (opts: { attachments?: IMailAttachment[] }): void => {\n for (const attachment of opts.attachments ?? []) {\n const { content } = attachment;\n\n if (content instanceof ReadableStream) {\n if (!content.locked) {\n content.cancel().catch((error) => {\n logger.warn(\n '[releaseAttachmentStreams] Cancelling a web stream failed | error: %s',\n error,\n );\n });\n }\n\n continue;\n }\n\n if (\n typeof content === 'object' &&\n content !== null &&\n 'destroy' in content &&\n typeof content.destroy === 'function'\n ) {\n content.destroy();\n }\n }\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"mime.utility.d.ts","sourceRoot":"","sources":["../../../../../src/components/mail/utilities/mime.utility.ts"],"names":[],"mappings":"AAGA,OAAO,EAAwC,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AA4FpF;;4FAE4F;AAC5F,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,MAAM,CAEpE;AAqQD;;;;;;;;;;;GAWG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CA6FhF"}
1
+ {"version":3,"file":"mime.utility.d.ts","sourceRoot":"","sources":["../../../../../src/components/mail/utilities/mime.utility.ts"],"names":[],"mappings":"AAGA,OAAO,EAAwC,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AA4FpF;;4FAE4F;AAC5F,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,MAAM,CAEpE;AAqQD;;;;;;;;;;;GAWG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAiGhF"}
@@ -348,7 +348,8 @@ export async function buildRawMimeMessage(message) {
348
348
  const relatedBoundary = newBoundary('Related');
349
349
  const parts = [bodyPart(relatedBoundary)];
350
350
  for (const attachment of inlineAttachments) {
351
- parts.push(await attachmentPart(attachment, relatedBoundary));
351
+ const result = await attachmentPart(attachment, relatedBoundary);
352
+ parts.push(result);
352
353
  }
353
354
  parts.push(`--${relatedBoundary}--`);
354
355
  return [
@@ -362,7 +363,8 @@ export async function buildRawMimeMessage(message) {
362
363
  const relatedBoundary = newBoundary('Related');
363
364
  const parts = [bodyPart(relatedBoundary)];
364
365
  for (const attachment of inlineAttachments) {
365
- parts.push(await attachmentPart(attachment, relatedBoundary));
366
+ const result = await attachmentPart(attachment, relatedBoundary);
367
+ parts.push(result);
366
368
  }
367
369
  parts.push(`--${relatedBoundary}--`);
368
370
  headers.push(`Content-Type: multipart/related; boundary="${relatedBoundary}"`);
@@ -372,13 +374,15 @@ export async function buildRawMimeMessage(message) {
372
374
  headers.push(`Content-Type: multipart/mixed; boundary="${mixedBoundary}"`);
373
375
  const bodyParts = [];
374
376
  if (inlineAttachments.length > 0) {
375
- bodyParts.push(await buildRelatedPart(mixedBoundary));
377
+ const result2 = await buildRelatedPart(mixedBoundary);
378
+ bodyParts.push(result2);
376
379
  }
377
380
  else {
378
381
  bodyParts.push(bodyPart(mixedBoundary));
379
382
  }
380
383
  for (const attachment of fileAttachments) {
381
- bodyParts.push(await attachmentPart(attachment, mixedBoundary));
384
+ const result3 = await attachmentPart(attachment, mixedBoundary);
385
+ bodyParts.push(result3);
382
386
  }
383
387
  bodyParts.push(`--${mixedBoundary}--`);
384
388
  return Buffer.from([...headers, '', bodyParts.join(CRLF)].join(CRLF), 'utf-8');