stratal 0.0.17 → 0.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/dist/{base-email.provider-DypUAfWm.mjs → base-email.provider-mjynzewK.mjs} +1 -1
- package/dist/{base-email.provider-DypUAfWm.mjs.map → base-email.provider-mjynzewK.mjs.map} +1 -1
- package/dist/bin/cloudflare-workers-loader.mjs +33 -1
- package/dist/bin/cloudflare-workers-loader.mjs.map +1 -1
- package/dist/bin/quarry.mjs +169 -7
- package/dist/bin/quarry.mjs.map +1 -1
- package/dist/cache/index.d.mts +3 -2
- package/dist/cache/index.d.mts.map +1 -1
- package/dist/cache/index.mjs +3 -10
- package/dist/cache/index.mjs.map +1 -1
- package/dist/{colors-Y7WIFXs7.mjs → colors-DJaRDXoS.mjs} +1 -1
- package/dist/{colors-Y7WIFXs7.mjs.map → colors-DJaRDXoS.mjs.map} +1 -1
- package/dist/{command-B1CPgsrU.mjs → command-BgSlsS4M.mjs} +3 -3
- package/dist/command-BgSlsS4M.mjs.map +1 -0
- package/dist/{command-TnkPYWta.d.mts → command-DsQq56Lp.d.mts} +2 -2
- package/dist/{command-TnkPYWta.d.mts.map → command-DsQq56Lp.d.mts.map} +1 -1
- package/dist/config/index.d.mts +81 -37
- package/dist/config/index.d.mts.map +1 -1
- package/dist/config/index.mjs +135 -61
- package/dist/config/index.mjs.map +1 -1
- package/dist/{consumer-registry-Bymm6ff4.d.mts → consumer-registry-Doom7BEh.d.mts} +1 -1
- package/dist/{consumer-registry-Bymm6ff4.d.mts.map → consumer-registry-Doom7BEh.d.mts.map} +1 -1
- package/dist/controller.decorator-LZY9aHYG.mjs +66 -0
- package/dist/controller.decorator-LZY9aHYG.mjs.map +1 -0
- package/dist/cron/index.d.mts +4 -3
- package/dist/cron/index.d.mts.map +1 -1
- package/dist/cron/index.mjs +1 -3
- package/dist/{cron-manager-CFBamKKk.mjs → cron-manager-C30t9UZM.mjs} +29 -19
- package/dist/cron-manager-C30t9UZM.mjs.map +1 -0
- package/dist/{cron-manager-D7imGwUT.d.mts → cron-manager-RuPtFVLy.d.mts} +28 -14
- package/dist/cron-manager-RuPtFVLy.d.mts.map +1 -0
- package/dist/di/index.d.mts +2 -2
- package/dist/di/index.mjs +3 -3
- package/dist/email/index.d.mts +3 -3
- package/dist/email/index.mjs +87 -18
- package/dist/email/index.mjs.map +1 -1
- package/dist/{en-DaewN8hc.mjs → en-rHmW6vD9.mjs} +14 -31
- package/dist/en-rHmW6vD9.mjs.map +1 -0
- package/dist/env-CamWD-U1.d.mts +25 -0
- package/dist/env-CamWD-U1.d.mts.map +1 -0
- package/dist/errors/index.d.mts +2 -2
- package/dist/errors/index.mjs +2 -3
- package/dist/errors-B4pYgYON.mjs +1714 -0
- package/dist/errors-B4pYgYON.mjs.map +1 -0
- package/dist/{errors-DuAR5Wke.mjs → errors-BUyUfr2Z.mjs} +14 -7
- package/dist/errors-BUyUfr2Z.mjs.map +1 -0
- package/dist/events/index.d.mts +2 -2
- package/dist/events/index.mjs +1 -2
- package/dist/{events-CvUSgEuN.mjs → events-COKixqnG.mjs} +2 -2
- package/dist/{events-CvUSgEuN.mjs.map → events-COKixqnG.mjs.map} +1 -1
- package/dist/{gateway-context-CNOLkLUC.mjs → gateway-context-cqZ8wMoi.mjs} +4 -9
- package/dist/gateway-context-cqZ8wMoi.mjs.map +1 -0
- package/dist/guards/index.d.mts +14 -5
- package/dist/guards/index.d.mts.map +1 -1
- package/dist/guards/index.mjs +1 -1
- package/dist/{guards-DUk_Kzst.mjs → guards-DMbsAxSX.mjs} +1 -1
- package/dist/guards-DMbsAxSX.mjs.map +1 -0
- package/dist/http-method.decorator-BT3ufnz8.mjs +96 -0
- package/dist/http-method.decorator-BT3ufnz8.mjs.map +1 -0
- package/dist/i18n/index.d.mts +3 -3
- package/dist/i18n/index.mjs +3 -16
- package/dist/i18n/messages/en/index.d.mts +1 -1
- package/dist/i18n/messages/en/index.mjs +1 -1
- package/dist/i18n/utils/index.d.mts +30 -0
- package/dist/i18n/utils/index.d.mts.map +1 -0
- package/dist/i18n/utils/index.mjs +2 -0
- package/dist/i18n/validation/index.d.mts +1 -1
- package/dist/i18n/validation/index.mjs +1 -1
- package/dist/i18n.module-CI_prYFD.mjs +2340 -0
- package/dist/i18n.module-CI_prYFD.mjs.map +1 -0
- package/dist/{index-NGxg-KP_.d.mts → index-B437eK7p.d.mts} +59 -16
- package/dist/index-B437eK7p.d.mts.map +1 -0
- package/dist/{index-Dp6A5ywM.d.mts → index-CWRS7Ri3.d.mts} +1 -1
- package/dist/{index-Dp6A5ywM.d.mts.map → index-CWRS7Ri3.d.mts.map} +1 -1
- package/dist/{index-D_w_Rmtd.d.mts → index-DFhEeFfC.d.mts} +13 -30
- package/dist/{index-D_w_Rmtd.d.mts.map → index-DFhEeFfC.d.mts.map} +1 -1
- package/dist/index-DPFqRs8L.d.mts +4318 -0
- package/dist/index-DPFqRs8L.d.mts.map +1 -0
- package/dist/{index-DGRe6Yoa.d.mts → index-Dnqm9ZB6.d.mts} +5 -4
- package/dist/index-Dnqm9ZB6.d.mts.map +1 -0
- package/dist/index-SHx31sBJ.d.mts +101 -0
- package/dist/index-SHx31sBJ.d.mts.map +1 -0
- package/dist/index.d.mts +5 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -20
- package/dist/{is-command-DJVI6wEJ.mjs → is-command-C6a7WTPw.mjs} +2 -2
- package/dist/{is-command-DJVI6wEJ.mjs.map → is-command-C6a7WTPw.mjs.map} +1 -1
- package/dist/{is-seeder-D5MIEcdz.mjs → is-seeder-CebjZCDn.mjs} +1 -1
- package/dist/{is-seeder-D5MIEcdz.mjs.map → is-seeder-CebjZCDn.mjs.map} +1 -1
- package/dist/logger/index.d.mts +1 -1
- package/dist/logger/index.mjs +1 -1
- package/dist/{logger-CGT91VY6.mjs → logger-V6Ms3QnQ.mjs} +63 -45
- package/dist/logger-V6Ms3QnQ.mjs.map +1 -0
- package/dist/macroable/index.d.mts +2 -0
- package/dist/macroable/index.mjs +2 -0
- package/dist/macroable-BmufBshB.mjs +122 -0
- package/dist/macroable-BmufBshB.mjs.map +1 -0
- package/dist/module/index.d.mts +3 -4
- package/dist/module/index.d.mts.map +1 -1
- package/dist/module/index.mjs +1 -10
- package/dist/module-qGE_1duv.mjs +732 -0
- package/dist/module-qGE_1duv.mjs.map +1 -0
- package/dist/openapi/index.d.mts +3 -3
- package/dist/openapi/index.mjs +2 -15
- package/dist/{openapi-tools.service-B3TxYKoQ.mjs → openapi-tools.service-CYWGuhue.mjs} +4 -1
- package/dist/{openapi-tools.service-B3TxYKoQ.mjs.map → openapi-tools.service-CYWGuhue.mjs.map} +1 -1
- package/dist/{openapi.service-DGnX3Fc4.d.mts → openapi.service-Bv_NioM9.d.mts} +9 -17
- package/dist/openapi.service-Bv_NioM9.d.mts.map +1 -0
- package/dist/quarry/index.d.mts +26 -12
- package/dist/quarry/index.d.mts.map +1 -1
- package/dist/quarry/index.mjs +4 -8
- package/dist/{quarry-registry-B2rkO-JS.mjs → quarry-registry-DFfRRkA7.mjs} +45 -40
- package/dist/quarry-registry-DFfRRkA7.mjs.map +1 -0
- package/dist/queue/index.d.mts +2 -2
- package/dist/queue/index.mjs +3 -13
- package/dist/queue/index.mjs.map +1 -1
- package/dist/{queue.module-BtI8f4Jo.mjs → queue.module-P-G-nCYz.mjs} +39 -42
- package/dist/queue.module-P-G-nCYz.mjs.map +1 -0
- package/dist/r2-storage.provider-LdzK9tfG.mjs +244 -0
- package/dist/r2-storage.provider-LdzK9tfG.mjs.map +1 -0
- package/dist/{resend.provider-bXMEkdRJ.mjs → resend.provider-bwILp0WI.mjs} +2 -4
- package/dist/{resend.provider-bXMEkdRJ.mjs.map → resend.provider-bwILp0WI.mjs.map} +1 -1
- package/dist/router/index.d.mts +2 -2
- package/dist/router/index.mjs +7 -16
- package/dist/seeder/index.d.mts +3 -4
- package/dist/seeder/index.d.mts.map +1 -1
- package/dist/seeder/index.mjs +2 -6
- package/dist/{seeder-R7RXJC35.mjs → seeder-BcqIFa2X.mjs} +5 -5
- package/dist/{seeder-R7RXJC35.mjs.map → seeder-BcqIFa2X.mjs.map} +1 -1
- package/dist/setup-CtekcwuO.mjs +37 -0
- package/dist/setup-CtekcwuO.mjs.map +1 -0
- package/dist/signed-url-COX7cCWR.mjs +74 -0
- package/dist/signed-url-COX7cCWR.mjs.map +1 -0
- package/dist/{smtp.provider-DrbHQztF.mjs → smtp.provider-B07yuARi.mjs} +2 -4
- package/dist/{smtp.provider-DrbHQztF.mjs.map → smtp.provider-B07yuARi.mjs.map} +1 -1
- package/dist/storage/index.d.mts +39 -17
- package/dist/storage/index.d.mts.map +1 -1
- package/dist/storage/index.mjs +3 -14
- package/dist/storage/providers/index.d.mts +30 -69
- package/dist/storage/providers/index.d.mts.map +1 -1
- package/dist/storage/providers/index.mjs +2 -5
- package/dist/{storage-CZKHOhci.mjs → storage-P6X4h9So.mjs} +102 -29
- package/dist/storage-P6X4h9So.mjs.map +1 -0
- package/dist/{storage-provider.interface-0IqcdhBf.d.mts → storage-provider.interface-CC1nniHk.d.mts} +20 -15
- package/dist/storage-provider.interface-CC1nniHk.d.mts.map +1 -0
- package/dist/stratal-BCiwCFN9.mjs +533 -0
- package/dist/stratal-BCiwCFN9.mjs.map +1 -0
- package/dist/{types-DahElfUw.d.mts → types-DIWemRad.d.mts} +2 -2
- package/dist/types-DIWemRad.d.mts.map +1 -0
- package/dist/{usage-generator-CVIsENuE.mjs → usage-generator-MBcRo0Q2.mjs} +2 -2
- package/dist/{usage-generator-CVIsENuE.mjs.map → usage-generator-MBcRo0Q2.mjs.map} +1 -1
- package/dist/{validation-DQTC259A.mjs → validation-Dbg3ehdP.mjs} +2 -2
- package/dist/{validation-DQTC259A.mjs.map → validation-Dbg3ehdP.mjs.map} +1 -1
- package/dist/websocket/index.d.mts +3 -3
- package/dist/websocket/index.d.mts.map +1 -1
- package/dist/websocket/index.mjs +1 -4
- package/dist/workers/index.d.mts +2 -1
- package/dist/workers/index.d.mts.map +1 -1
- package/dist/workers/index.mjs +2 -20
- package/dist/workers/index.mjs.map +1 -1
- package/package.json +41 -50
- package/dist/application-DfPtIzxF.d.mts +0 -177
- package/dist/application-DfPtIzxF.d.mts.map +0 -1
- package/dist/command-B1CPgsrU.mjs.map +0 -1
- package/dist/cron-manager-CFBamKKk.mjs.map +0 -1
- package/dist/cron-manager-D7imGwUT.d.mts.map +0 -1
- package/dist/en-DaewN8hc.mjs.map +0 -1
- package/dist/errors-DSKapqD8.mjs +0 -707
- package/dist/errors-DSKapqD8.mjs.map +0 -1
- package/dist/errors-DuAR5Wke.mjs.map +0 -1
- package/dist/gateway-context-CNOLkLUC.mjs.map +0 -1
- package/dist/guards-DUk_Kzst.mjs.map +0 -1
- package/dist/i18n.module-Dn9SrFdS.mjs +0 -1841
- package/dist/i18n.module-Dn9SrFdS.mjs.map +0 -1
- package/dist/index-BFCxSp_f.d.mts +0 -2625
- package/dist/index-BFCxSp_f.d.mts.map +0 -1
- package/dist/index-DGRe6Yoa.d.mts.map +0 -1
- package/dist/index-NGxg-KP_.d.mts.map +0 -1
- package/dist/logger-CGT91VY6.mjs.map +0 -1
- package/dist/middleware/index.d.mts +0 -2
- package/dist/middleware/index.mjs +0 -5
- package/dist/middleware-Bl-b5pkt.mjs +0 -362
- package/dist/middleware-Bl-b5pkt.mjs.map +0 -1
- package/dist/module-registry-CmjBX6ol.d.mts +0 -121
- package/dist/module-registry-CmjBX6ol.d.mts.map +0 -1
- package/dist/module-tUtyVJ5E.mjs +0 -371
- package/dist/module-tUtyVJ5E.mjs.map +0 -1
- package/dist/openapi.service-DGnX3Fc4.d.mts.map +0 -1
- package/dist/quarry-registry-B2rkO-JS.mjs.map +0 -1
- package/dist/queue.module-BtI8f4Jo.mjs.map +0 -1
- package/dist/router-context-D9R1v2Ac.mjs +0 -267
- package/dist/router-context-D9R1v2Ac.mjs.map +0 -1
- package/dist/s3-storage.provider-CttzNnDR.mjs +0 -335
- package/dist/s3-storage.provider-CttzNnDR.mjs.map +0 -1
- package/dist/storage-CZKHOhci.mjs.map +0 -1
- package/dist/storage-provider.interface-0IqcdhBf.d.mts.map +0 -1
- package/dist/stratal-D5smIU1y.mjs +0 -315
- package/dist/stratal-D5smIU1y.mjs.map +0 -1
- package/dist/types-DahElfUw.d.mts.map +0 -1
package/dist/email/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/email/email.tokens.ts","../../src/email/consumers/email.consumer.ts","../../src/email/services/email.service.ts","../../src/email/errors/resend-api-key-missing.error.ts","../../src/email/errors/smtp-configuration-missing.error.ts","../../src/email/errors/smtp-host-missing.error.ts","../../src/email/errors/email-smtp-connection-failed.error.ts","../../src/email/errors/email-resend-api-failed.error.ts","../../src/email/errors/email-provider-not-supported.error.ts","../../src/email/services/email-provider-factory.ts","../../src/email/email.module.ts","../../src/email/contracts/email-attachment.ts","../../src/email/contracts/email-message.contract.ts","../../src/email/contracts/send-email.input.ts"],"sourcesContent":["/**\n * Dependency Injection Tokens for Email Module\n *\n * These Symbol-based tokens ensure type-safe dependency injection\n * throughout the email module and prevent naming collisions.\n */\n\n/**\n * Email Module DI Tokens\n */\nexport const EMAIL_TOKENS = {\n /**\n * Email module configuration options\n */\n Options: Symbol.for('stratal:email:options'),\n\n /**\n * Main email service - facade for sending emails via queues\n */\n EmailService: Symbol.for('stratal:email:service'),\n\n /**\n * Factory for creating email provider instances based on configuration\n */\n EmailProviderFactory: Symbol.for('stratal:email:provider:factory'),\n\n /**\n * Email provider interface - abstracts provider implementation\n */\n EmailProvider: Symbol.for('stratal:email:provider'),\n\n /**\n * Queue sender for email dispatch.\n * Bound via EmailModule.forRoot({ queue: 'queue-name' })\n */\n EmailQueue: Symbol.for('stratal:email:queue'),\n} as const\n","import { inject } from 'tsyringe'\nimport { Transient } from '../../di/decorators'\nimport { LOGGER_TOKENS, type LoggerService } from '../../logger'\nimport type { IQueueConsumer, QueueMessage } from '../../queue/queue-consumer'\nimport { STORAGE_TOKENS, type StorageService } from '../../storage'\nimport type { EmailAttachment, ResolvedEmailAttachment, SendEmailInput } from '../contracts'\nimport { EMAIL_TOKENS } from '../email.tokens'\nimport type { EmailProviderFactory } from '../services/email-provider-factory'\n\n/**\n * Email Consumer\n *\n * Generic queue consumer that handles email.send and email.batch.send messages\n * from ANY queue. Message routing is based on message type, not queue name.\n *\n * This consumer:\n * - Resolves storage-based attachments to streams\n * - Creates email provider instances via factory\n * - Sends emails with proper logging (no PII)\n * - Handles errors with retry support\n *\n * @example\n * ```typescript\n * // Registered in EmailModule\n * @Module({\n * consumers: [EmailConsumer]\n * })\n * ```\n */\n@Transient()\nexport class EmailConsumer implements IQueueConsumer<SendEmailInput> {\n readonly messageTypes = ['email.send', 'email.batch.send']\n\n constructor(\n @inject(LOGGER_TOKENS.LoggerService)\n private readonly logger: LoggerService,\n @inject(EMAIL_TOKENS.EmailProviderFactory)\n private readonly providerFactory: EmailProviderFactory,\n @inject(STORAGE_TOKENS.StorageService)\n private readonly storage: StorageService\n ) { }\n\n async handle(message: QueueMessage<SendEmailInput>): Promise<void> {\n const { type, payload } = message\n const recipientCount = Array.isArray(payload.to) ? payload.to.length : 1\n\n this.logger.info('Processing email message', {\n type,\n recipientCount,\n hasHtml: !!payload.html,\n hasText: !!payload.text,\n hasAttachments: !!payload.attachments?.length,\n })\n\n try {\n // Resolve storage-based attachments before sending\n const resolvedAttachments = await this.resolveAttachments(payload.attachments)\n\n const provider = await this.providerFactory.create()\n const result = await provider.send({\n ...payload,\n attachments: resolvedAttachments,\n })\n\n this.logger.info('Email sent successfully', {\n type,\n recipientCount,\n messageId: result.messageId,\n })\n }\n catch (error) {\n this.logger.error('Failed to send email', {\n type,\n recipientCount,\n error: (error as Error).message,\n })\n throw error // Retry via queue\n }\n }\n\n onError(error: Error, message: QueueMessage<SendEmailInput>): Promise<void> {\n const recipientCount = Array.isArray(message.payload.to)\n ? message.payload.to.length\n : 1\n\n this.logger.error('Email send failed after retries', {\n recipientCount,\n error: error.message,\n stack: error.stack,\n })\n\n return Promise.resolve()\n }\n\n /**\n * Resolve email attachments\n *\n * Converts attachment schemas to resolved attachments.\n * - Inline attachments: decode base64 to Buffer\n * - Storage attachments: pass stream directly (providers support streams)\n */\n private async resolveAttachments(\n attachments: EmailAttachment[] | undefined\n ): Promise<ResolvedEmailAttachment[] | undefined> {\n if (!attachments?.length) return undefined\n\n return Promise.all(attachments.map(async (attachment) => {\n // Check for inline attachment (has content property)\n if ('content' in attachment) {\n return {\n filename: attachment.filename,\n content: Buffer.from(attachment.content, 'base64'),\n contentType: attachment.contentType,\n }\n }\n\n // Storage attachment - pass stream directly to provider\n const result = await this.storage.download(\n attachment.storageKey,\n attachment.disk\n )\n\n return {\n filename: attachment.filename,\n content: result.toStream() ?? Buffer.alloc(0),\n contentType: result.contentType,\n }\n }))\n }\n}\n","import { render } from '@react-email/render'\nimport type { ReactElement } from 'react'\nimport { inject } from 'tsyringe'\nimport { Transient } from '../../di/decorators'\nimport type { IQueueSender } from '../../queue'\nimport type { SendBatchEmailInput, SendEmailInput } from '../contracts'\nimport { EMAIL_TOKENS } from '../email.tokens'\n\nexport type SendEmailInputWithTemplate = Omit<SendEmailInput, 'html' | 'text'> & {\n template?: ReactElement\n}\n\nexport type SendBatchEmailInputWithTemplate = Omit<SendBatchEmailInput, 'messages'> & {\n messages: SendEmailInputWithTemplate[]\n}\n\n/**\n * Email Service\n *\n * Main facade for sending emails. Routes emails to queues for async processing.\n * The queue is injected via EMAIL_TOKENS.EmailQueue, configured by the application\n * via EmailModule.forRoot({ queue: 'queue-name' }).\n *\n * @example Basic usage\n * ```typescript\n * @inject(EMAIL_TOKENS.EmailService)\n * private readonly emailService: EmailService\n *\n * await this.emailService.send({\n * to: 'user@example.com',\n * subject: 'Welcome',\n * template: <WelcomeEmail name=\"John\" />\n * })\n * ```\n */\n@Transient(EMAIL_TOKENS.EmailService)\nexport class EmailService {\n constructor(\n @inject(EMAIL_TOKENS.EmailQueue)\n protected readonly queue: IQueueSender\n ) { }\n\n /**\n * Send a single email\n *\n * Dispatches the email to the queue for async processing.\n * Supports optional React template rendering.\n *\n * @param input - Email message details\n */\n async send({ template, ...input }: SendEmailInputWithTemplate): Promise<void> {\n await this.queue.dispatch({\n type: 'email.send',\n payload: { ...input, html: template ? await render(template) : undefined },\n })\n }\n\n /**\n * Send multiple emails in a batch\n *\n * Dispatches all emails to the queue for async processing.\n * Supports React template rendering for each message.\n *\n * @param input - Batch email details\n */\n async sendBatch(input: SendBatchEmailInputWithTemplate): Promise<void> {\n for (const message of input.messages) {\n await this.send(message)\n }\n }\n}\n","import { ApplicationError, ERROR_CODES } from '../../errors'\n\n/**\n * ResendApiKeyMissingError\n *\n * Thrown when the Resend API key is not configured in environment variables.\n * This prevents the Resend email provider from initializing.\n *\n * Resolution: Set the RESEND_EMAIL_API_KEY environment variable.\n */\nexport class ResendApiKeyMissingError extends ApplicationError {\n constructor() {\n super(\n 'errors.email.resendApiKeyMissing',\n ERROR_CODES.SYSTEM.CONFIGURATION_ERROR\n )\n }\n}\n","import { ApplicationError, ERROR_CODES } from '../../errors'\n\n/**\n * SmtpConfigurationMissingError\n *\n * Thrown when SMTP configuration is not found in environment variables.\n * This prevents the SMTP email provider from initializing.\n *\n * Resolution: Set the SMTP_URL environment variable with format: smtp://user:pass@host:port\n */\nexport class SmtpConfigurationMissingError extends ApplicationError {\n constructor() {\n super(\n 'errors.email.smtpConfigurationMissing',\n ERROR_CODES.SYSTEM.CONFIGURATION_ERROR\n )\n }\n}\n","import { ApplicationError, ERROR_CODES } from '../../errors'\n\n/**\n * SmtpHostMissingError\n *\n * Thrown when SMTP host could not be parsed from SMTP_URL or is empty.\n * This prevents the SMTP email provider from initializing.\n *\n * Resolution: Ensure SMTP_URL is correctly formatted: smtp://user:pass@host:port\n */\nexport class SmtpHostMissingError extends ApplicationError {\n constructor() {\n super(\n 'errors.email.smtpHostMissing',\n ERROR_CODES.SYSTEM.CONFIGURATION_ERROR\n )\n }\n}\n","import { ApplicationError, ERROR_CODES } from '../../errors'\n\n/**\n * EmailSmtpConnectionFailedError\n *\n * Thrown when connection to SMTP server fails during email sending.\n * This is a runtime error that may be temporary.\n *\n * Resolution: Check SMTP server availability, network connectivity, or wait and retry.\n */\nexport class EmailSmtpConnectionFailedError extends ApplicationError {\n constructor(smtpHost: string, smtpPort: number) {\n super(\n 'errors.email.smtpConnectionFailed',\n ERROR_CODES.SYSTEM.INFRASTRUCTURE_ERROR,\n { smtpHost, smtpPort }\n )\n }\n}\n","import { ApplicationError, ERROR_CODES } from '../../errors'\n\n/**\n * EmailResendApiFailedError\n *\n * Thrown when Resend API returns an error during email sending.\n * This is a runtime error from the Resend service.\n *\n * Resolution: Check Resend API status, API key validity, or wait and retry.\n */\nexport class EmailResendApiFailedError extends ApplicationError {\n constructor() {\n super(\n 'errors.email.resendApiFailed',\n ERROR_CODES.SYSTEM.INFRASTRUCTURE_ERROR,\n )\n }\n}\n","import { ApplicationError, ERROR_CODES } from '../../errors'\n\n/**\n * EmailProviderNotSupportedError\n *\n * Thrown when an unsupported email provider is configured.\n * Only 'resend' and 'smtp' providers are currently supported.\n *\n * Resolution: Set EMAIL_PROVIDER to either 'resend' or 'smtp'.\n */\nexport class EmailProviderNotSupportedError extends ApplicationError {\n constructor(provider: string) {\n super(\n 'errors.email.providerNotSupported',\n ERROR_CODES.SYSTEM.CONFIGURATION_ERROR,\n { provider }\n )\n }\n}\n","import { inject } from 'tsyringe'\nimport { Transient } from '../../di/decorators'\nimport type { EmailModuleOptions } from '../email.module'\nimport { EMAIL_TOKENS } from '../email.tokens'\nimport { EmailProviderNotSupportedError } from '../errors'\nimport type { IEmailProvider } from '../providers/email-provider.interface'\n\n/**\n * Email Provider Factory\n *\n * Creates email provider instances based on configuration.\n * Supports automatic provider selection from module options.\n *\n * Providers are loaded lazily via dynamic imports to avoid pulling in\n * heavy Node.js-only dependencies (e.g. nodemailer) at module parse time,\n * which would break Cloudflare Workers and vitest-pool-workers environments.\n */\n@Transient(EMAIL_TOKENS.EmailProviderFactory)\nexport class EmailProviderFactory {\n constructor(\n @inject(EMAIL_TOKENS.Options)\n private readonly options: EmailModuleOptions\n ) { }\n\n /**\n * Create email provider instance based on configuration\n *\n * @returns Email provider implementation\n * @throws EmailProviderNotSupportedError if provider is not supported\n */\n async create(): Promise<IEmailProvider> {\n switch (this.options.provider) {\n case 'resend': {\n const { ResendProvider } = await import('../providers/resend.provider')\n return new ResendProvider(this.options)\n }\n\n case 'smtp': {\n const { SmtpProvider } = await import('../providers/smtp.provider')\n return new SmtpProvider(this.options)\n }\n\n default:\n throw new EmailProviderNotSupportedError(this.options.provider)\n }\n }\n}\n","/**\n * Email Module\n *\n * Provides email sending capabilities with provider abstraction.\n * Supports multiple email providers (Resend, SMTP) with automatic provider selection.\n * Emails are sent asynchronously via Cloudflare Queues.\n *\n * **Usage:**\n * ```typescript\n * // In AppModule imports with static options\n * EmailModule.forRoot({\n * provider: 'resend',\n * apiKey: 'your-api-key',\n * from: { name: 'App', email: 'noreply@example.com' },\n * queue: 'notifications-queue',\n * })\n *\n * // Or with async options from config namespaces\n * EmailModule.forRootAsync({\n * inject: [emailConfig.KEY],\n * useFactory: (email) => ({\n * provider: email.provider,\n * apiKey: email.apiKey,\n * from: email.from,\n * queue: email.queue,\n * }),\n * })\n *\n * // In your service\n * @inject(EMAIL_TOKENS.EmailService)\n * private readonly emailService: EmailService\n *\n * await this.emailService.send({\n * to: 'user@example.com',\n * subject: 'Welcome',\n * template: <WelcomeEmail name=\"John\" />\n * })\n * ```\n */\n\nimport { Module } from '../module'\nimport type { AsyncModuleOptions, DynamicModule } from '../module/types'\nimport type { QueueName } from '../queue'\nimport { QUEUE_TOKENS, QueueRegistry } from '../queue'\nimport { EmailConsumer } from './consumers/email.consumer'\nimport { EMAIL_TOKENS } from './email.tokens'\nimport { EmailProviderFactory, EmailService } from './services'\n\n/**\n * SMTP configuration options\n */\nexport interface SmtpConfig {\n /** SMTP server host */\n host: string\n /** SMTP server port */\n port: number\n /** Use TLS */\n secure?: boolean\n /** SMTP username */\n username?: string\n /** SMTP password */\n password?: string\n}\n\n/**\n * Email module configuration options\n */\nexport interface EmailModuleOptions {\n /** Email provider type */\n provider: 'resend' | 'smtp'\n\n /** Default from address */\n from: { name: string; email: string }\n\n /** Resend API key (required for resend provider) */\n apiKey?: string\n\n /** SMTP configuration (required for smtp provider) */\n smtp?: SmtpConfig\n\n /** Default reply-to address */\n replyTo?: string\n\n /**\n * Queue name for email dispatch.\n * The queue must be registered via QueueModule.registerQueue(name).\n */\n queue: QueueName\n}\n\n@Module({\n providers: [\n { provide: EMAIL_TOKENS.EmailService, useClass: EmailService },\n { provide: EMAIL_TOKENS.EmailProviderFactory, useClass: EmailProviderFactory },\n ],\n consumers: [EmailConsumer],\n})\nexport class EmailModule {\n /**\n * Configure EmailModule with static options\n *\n * @param options - Email configuration options\n * @returns Dynamic module with email infrastructure\n *\n * @example\n * ```typescript\n * EmailModule.forRoot({\n * provider: 'resend',\n * apiKey: env.RESEND_API_KEY,\n * from: { name: 'App', email: 'noreply@example.com' },\n * queue: 'notifications-queue',\n * })\n * ```\n */\n static forRoot(options: EmailModuleOptions): DynamicModule {\n return {\n module: EmailModule,\n providers: [\n { provide: EMAIL_TOKENS.Options, useValue: options },\n { provide: EMAIL_TOKENS.EmailQueue, useExisting: options.queue },\n ],\n }\n }\n\n /**\n * Configure EmailModule with async factory\n *\n * Use when configuration depends on other services.\n *\n * @param options - Async configuration with factory and inject tokens\n * @returns Dynamic module with email infrastructure\n *\n * @example\n * ```typescript\n * EmailModule.forRootAsync({\n * inject: [emailConfig.KEY],\n * useFactory: (email) => ({\n * provider: email.provider,\n * apiKey: email.apiKey,\n * from: email.from,\n * smtp: email.smtp,\n * queue: email.queue,\n * })\n * })\n * ```\n */\n static forRootAsync(options: AsyncModuleOptions<EmailModuleOptions>): DynamicModule {\n return {\n module: EmailModule,\n providers: [\n {\n provide: EMAIL_TOKENS.Options,\n useFactory: options.useFactory,\n inject: options.inject,\n },\n // Resolve queue from QueueRegistry using the queue name from options\n {\n provide: EMAIL_TOKENS.EmailQueue,\n useFactory: (emailOptions: EmailModuleOptions, registry: QueueRegistry) =>\n registry.getQueue(emailOptions.queue),\n inject: [EMAIL_TOKENS.Options, QUEUE_TOKENS.QueueRegistry],\n },\n ],\n }\n }\n}\n","import { z } from '../../i18n/validation'\n\n/**\n * Inline Email Attachment Schema\n *\n * Attachment with content embedded as base64 string.\n * Use for small files that can fit in queue message.\n */\nexport const inlineEmailAttachmentSchema = z.object({\n /**\n * Filename to display for the attachment\n */\n filename: z.string().min(1).max(255),\n\n /**\n * Base64 encoded content of the attachment\n */\n content: z.string(),\n\n /**\n * MIME type of the attachment (e.g., 'application/pdf', 'image/png')\n */\n contentType: z.string(),\n\n /**\n * Optional size of the attachment in bytes\n */\n size: z.number().positive().optional(),\n})\n\n/**\n * Storage Email Attachment Schema\n *\n * Attachment stored in cloud storage.\n * Content type and size are derived from storage metadata.\n * Use for large files to avoid queue message size limits.\n */\nexport const storageEmailAttachmentSchema = z.object({\n /**\n * Filename to display for the attachment\n */\n filename: z.string().min(1).max(255),\n\n /**\n * Path to the file in storage\n */\n storageKey: z.string(),\n\n /**\n * Optional storage disk name (uses default if not provided)\n */\n disk: z.string().optional(),\n})\n\n/**\n * Email Attachment Schema\n *\n * Union type - type is inferred from presence of `content` vs `storageKey`.\n * - If `content` is present: inline attachment\n * - If `storageKey` is present: storage-based attachment\n */\nexport const emailAttachmentSchema = z.union([\n inlineEmailAttachmentSchema,\n storageEmailAttachmentSchema,\n])\n\n/**\n * Type definitions\n */\nexport type InlineEmailAttachment = z.infer<typeof inlineEmailAttachmentSchema>\nexport type StorageEmailAttachment = z.infer<typeof storageEmailAttachmentSchema>\nexport type EmailAttachment = z.infer<typeof emailAttachmentSchema>\n\n/**\n * Resolved Email Attachment\n *\n * Attachment after resolution, ready for email provider.\n * Content can be Buffer (for inline) or ReadableStream (for storage-based).\n * Both nodemailer and Resend support these formats directly.\n */\nexport interface ResolvedEmailAttachment {\n filename: string\n content: Buffer | ReadableStream\n contentType: string\n}\n","import { withI18n, z } from '../../i18n/validation'\nimport { emailAttachmentSchema, type ResolvedEmailAttachment } from './email-attachment'\n\n/**\n * Email Address Schema\n *\n * Represents an email address with optional name\n */\nexport const emailAddressSchema = z.object({\n name: z.string().optional(),\n email: z.string().email(),\n})\n\n/**\n * Base Email Message Schema\n *\n * Defines the core structure for email messages.\n * Ensures either html or text content is provided.\n */\nexport const emailMessageSchema = z\n .object({\n /**\n * Recipient email address(es)\n * Can be a single email string or array of emails\n */\n to: z.union([z.string().email(), z.array(z.string().email())]),\n\n /**\n * Sender email address with optional name\n * Falls back to default from config if not provided\n */\n from: emailAddressSchema.optional(),\n\n /**\n * Email subject line\n */\n subject: z.string().min(1).max(500),\n\n /**\n * HTML content of the email\n * Either html or text must be provided\n */\n html: z.string().optional(),\n\n /**\n * Plain text content of the email\n * Either html or text must be provided\n */\n text: z.string().optional(),\n\n /**\n * Reply-to email address\n */\n replyTo: z.string().email().optional(),\n\n /**\n * CC recipients\n */\n cc: z.array(z.string().email()).optional(),\n\n /**\n * BCC recipients\n */\n bcc: z.array(z.string().email()).optional(),\n\n /**\n * Email attachments\n */\n attachments: z.array(emailAttachmentSchema).optional(),\n })\n .refine(\n (data) => data.html ?? data.text,\n withI18n('zodI18n.errors.custom.emailOrTextRequired')\n )\n\n/**\n * Type definition for email message\n */\nexport type EmailMessage = z.infer<typeof emailMessageSchema>\n\n/**\n * Type definition for email address\n */\nexport type EmailAddress = z.infer<typeof emailAddressSchema>\n\n/**\n * Resolved Email Message\n *\n * Email message with attachments resolved to Buffer content.\n * Used by providers after the consumer resolves storage-based attachments.\n */\nexport type ResolvedEmailMessage = Omit<EmailMessage, 'attachments'> & {\n attachments?: ResolvedEmailAttachment[]\n}\n","import { z } from '../../i18n/validation'\nimport { emailMessageSchema } from './email-message.contract'\n\n/**\n * Send Email Input Schema\n *\n * Input schema for sending emails through the EmailService.\n * Extends the base email message with optional metadata.\n * Uses safeExtend() because emailMessageSchema contains refinements.\n */\nexport const sendEmailInputSchema = emailMessageSchema.safeExtend({\n /**\n * Optional metadata to include with the email\n * Can be used for tracking, categorization, etc.\n */\n metadata: z.record(z.string(), z.unknown()).optional(),\n})\n\n/**\n * Type definition for send email input\n */\nexport type SendEmailInput = z.infer<typeof sendEmailInputSchema>\n\n/**\n * Send Batch Email Input Schema\n *\n * Schema for sending multiple emails in a batch\n */\nexport const sendBatchEmailInputSchema = z.object({\n /**\n * Array of email messages to send\n */\n messages: z.array(sendEmailInputSchema).min(1).max(100),\n})\n\n/**\n * Type definition for send batch email input\n */\nexport type SendBatchEmailInput = z.infer<typeof sendBatchEmailInputSchema>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/email/email.tokens.ts","../../src/email/consumers/email.consumer.ts","../../src/email/services/email.service.ts","../../src/email/errors/resend-api-key-missing.error.ts","../../src/email/errors/smtp-configuration-missing.error.ts","../../src/email/errors/smtp-host-missing.error.ts","../../src/email/errors/email-smtp-connection-failed.error.ts","../../src/email/errors/email-resend-api-failed.error.ts","../../src/email/errors/email-provider-not-supported.error.ts","../../src/email/services/email-provider-factory.ts","../../src/email/email.module.ts","../../src/email/contracts/email-attachment.ts","../../src/email/contracts/email-message.contract.ts","../../src/email/contracts/send-email.input.ts"],"sourcesContent":["/**\n * Dependency Injection Tokens for Email Module\n *\n * These Symbol-based tokens ensure type-safe dependency injection\n * throughout the email module and prevent naming collisions.\n */\n\n/**\n * Email Module DI Tokens\n */\nexport const EMAIL_TOKENS = {\n /**\n * Email module configuration options\n */\n Options: Symbol.for('stratal:email:options'),\n\n /**\n * Main email service - facade for sending emails via queues\n */\n EmailService: Symbol.for('stratal:email:service'),\n\n /**\n * Factory for creating email provider instances based on configuration\n */\n EmailProviderFactory: Symbol.for('stratal:email:provider:factory'),\n\n /**\n * Email provider interface - abstracts provider implementation\n */\n EmailProvider: Symbol.for('stratal:email:provider'),\n\n /**\n * Queue sender for email dispatch.\n * Bound via EmailModule.forRoot({ queue: 'queue-name' })\n */\n EmailQueue: Symbol.for('stratal:email:queue'),\n} as const\n","import { inject } from 'tsyringe'\nimport { Transient } from '../../di/decorators'\nimport { LOGGER_TOKENS, type LoggerService } from '../../logger'\nimport type { IQueueConsumer, QueueMessage } from '../../queue/queue-consumer'\nimport { STORAGE_TOKENS, type StorageService } from '../../storage'\nimport type { EmailAttachment, ResolvedEmailAttachment, SendEmailInput } from '../contracts'\nimport { EMAIL_TOKENS } from '../email.tokens'\nimport type { EmailProviderFactory } from '../services/email-provider-factory'\n\n/**\n * Email Consumer\n *\n * Generic queue consumer that handles email.send and email.batch.send messages\n * from ANY queue. Message routing is based on message type, not queue name.\n *\n * This consumer:\n * - Resolves storage-based attachments to streams\n * - Creates email provider instances via factory\n * - Sends emails with proper logging (no PII)\n * - Handles errors with retry support\n *\n * @example\n * ```typescript\n * // Registered in EmailModule\n * @Module({\n * consumers: [EmailConsumer]\n * })\n * ```\n */\n@Transient()\nexport class EmailConsumer implements IQueueConsumer<SendEmailInput> {\n readonly messageTypes = ['email.send', 'email.batch.send']\n\n constructor(\n @inject(LOGGER_TOKENS.LoggerService)\n private readonly logger: LoggerService,\n @inject(EMAIL_TOKENS.EmailProviderFactory)\n private readonly providerFactory: EmailProviderFactory,\n @inject(STORAGE_TOKENS.StorageService)\n private readonly storage: StorageService\n ) { }\n\n async handle(message: QueueMessage<SendEmailInput>): Promise<void> {\n const { type, payload } = message\n const recipientCount = Array.isArray(payload.to) ? payload.to.length : 1\n\n this.logger.info('Processing email message', {\n type,\n recipientCount,\n hasHtml: !!payload.html,\n hasText: !!payload.text,\n hasAttachments: !!payload.attachments?.length,\n })\n\n try {\n // Resolve storage-based attachments before sending\n const resolvedAttachments = await this.resolveAttachments(payload.attachments)\n\n const provider = await this.providerFactory.create()\n const result = await provider.send({\n ...payload,\n attachments: resolvedAttachments,\n })\n\n this.logger.info('Email sent successfully', {\n type,\n recipientCount,\n messageId: result.messageId,\n })\n }\n catch (error) {\n this.logger.error('Failed to send email', {\n type,\n recipientCount,\n error: (error as Error).message,\n })\n throw error // Retry via queue\n }\n }\n\n onError(error: Error, message: QueueMessage<SendEmailInput>): Promise<void> {\n const recipientCount = Array.isArray(message.payload.to)\n ? message.payload.to.length\n : 1\n\n this.logger.error('Email send failed after retries', {\n recipientCount,\n error: error.message,\n stack: error.stack,\n })\n\n return Promise.resolve()\n }\n\n /**\n * Resolve email attachments\n *\n * Converts attachment schemas to resolved attachments.\n * - Inline attachments: decode base64 to Buffer\n * - Storage attachments: pass stream directly (providers support streams)\n */\n private async resolveAttachments(\n attachments: EmailAttachment[] | undefined\n ): Promise<ResolvedEmailAttachment[] | undefined> {\n if (!attachments?.length) return undefined\n\n return Promise.all(attachments.map(async (attachment) => {\n // Check for inline attachment (has content property)\n if ('content' in attachment) {\n return {\n filename: attachment.filename,\n content: Buffer.from(attachment.content, 'base64'),\n contentType: attachment.contentType,\n }\n }\n\n // Storage attachment - pass stream directly to provider\n const result = await this.storage.download(\n attachment.storageKey,\n attachment.disk\n )\n\n return {\n filename: attachment.filename,\n content: result.toStream() ?? Buffer.alloc(0),\n contentType: result.contentType,\n }\n }))\n }\n}\n","import { render } from '@react-email/render'\nimport type { ReactElement } from 'react'\nimport { inject } from 'tsyringe'\nimport { Transient } from '../../di/decorators'\nimport type { IQueueSender } from '../../queue'\nimport type { SendBatchEmailInput, SendEmailInput } from '../contracts'\nimport { EMAIL_TOKENS } from '../email.tokens'\n\nexport type SendEmailInputWithTemplate = Omit<SendEmailInput, 'html' | 'text'> & {\n template?: ReactElement\n}\n\nexport type SendBatchEmailInputWithTemplate = Omit<SendBatchEmailInput, 'messages'> & {\n messages: SendEmailInputWithTemplate[]\n}\n\n/**\n * Email Service\n *\n * Main facade for sending emails. Routes emails to queues for async processing.\n * The queue is injected via EMAIL_TOKENS.EmailQueue, configured by the application\n * via EmailModule.forRoot({ queue: 'queue-name' }).\n *\n * @example Basic usage\n * ```typescript\n * @inject(EMAIL_TOKENS.EmailService)\n * private readonly emailService: EmailService\n *\n * await this.emailService.send({\n * to: 'user@example.com',\n * subject: 'Welcome',\n * template: <WelcomeEmail name=\"John\" />\n * })\n * ```\n */\n@Transient(EMAIL_TOKENS.EmailService)\nexport class EmailService {\n constructor(\n @inject(EMAIL_TOKENS.EmailQueue)\n protected readonly queue: IQueueSender\n ) { }\n\n /**\n * Send a single email\n *\n * Dispatches the email to the queue for async processing.\n * Supports optional React template rendering.\n *\n * @param input - Email message details\n */\n async send({ template, ...input }: SendEmailInputWithTemplate): Promise<void> {\n await this.queue.dispatch({\n type: 'email.send',\n payload: { ...input, html: template ? await render(template) : undefined },\n })\n }\n\n /**\n * Send multiple emails in a batch\n *\n * Dispatches all emails to the queue for async processing.\n * Supports React template rendering for each message.\n *\n * @param input - Batch email details\n */\n async sendBatch(input: SendBatchEmailInputWithTemplate): Promise<void> {\n for (const message of input.messages) {\n await this.send(message)\n }\n }\n}\n","import { ApplicationError, ERROR_CODES } from '../../errors'\n\n/**\n * ResendApiKeyMissingError\n *\n * Thrown when the Resend API key is not configured in environment variables.\n * This prevents the Resend email provider from initializing.\n *\n * Resolution: Set the RESEND_EMAIL_API_KEY environment variable.\n */\nexport class ResendApiKeyMissingError extends ApplicationError {\n constructor() {\n super(\n 'errors.email.resendApiKeyMissing',\n ERROR_CODES.SYSTEM.CONFIGURATION_ERROR\n )\n }\n}\n","import { ApplicationError, ERROR_CODES } from '../../errors'\n\n/**\n * SmtpConfigurationMissingError\n *\n * Thrown when SMTP configuration is not found in environment variables.\n * This prevents the SMTP email provider from initializing.\n *\n * Resolution: Set the SMTP_URL environment variable with format: smtp://user:pass@host:port\n */\nexport class SmtpConfigurationMissingError extends ApplicationError {\n constructor() {\n super(\n 'errors.email.smtpConfigurationMissing',\n ERROR_CODES.SYSTEM.CONFIGURATION_ERROR\n )\n }\n}\n","import { ApplicationError, ERROR_CODES } from '../../errors'\n\n/**\n * SmtpHostMissingError\n *\n * Thrown when SMTP host could not be parsed from SMTP_URL or is empty.\n * This prevents the SMTP email provider from initializing.\n *\n * Resolution: Ensure SMTP_URL is correctly formatted: smtp://user:pass@host:port\n */\nexport class SmtpHostMissingError extends ApplicationError {\n constructor() {\n super(\n 'errors.email.smtpHostMissing',\n ERROR_CODES.SYSTEM.CONFIGURATION_ERROR\n )\n }\n}\n","import { ApplicationError, ERROR_CODES } from '../../errors'\n\n/**\n * EmailSmtpConnectionFailedError\n *\n * Thrown when connection to SMTP server fails during email sending.\n * This is a runtime error that may be temporary.\n *\n * Resolution: Check SMTP server availability, network connectivity, or wait and retry.\n */\nexport class EmailSmtpConnectionFailedError extends ApplicationError {\n constructor(smtpHost: string, smtpPort: number) {\n super(\n 'errors.email.smtpConnectionFailed',\n ERROR_CODES.SYSTEM.INFRASTRUCTURE_ERROR,\n { smtpHost, smtpPort }\n )\n }\n}\n","import { ApplicationError, ERROR_CODES } from '../../errors'\n\n/**\n * EmailResendApiFailedError\n *\n * Thrown when Resend API returns an error during email sending.\n * This is a runtime error from the Resend service.\n *\n * Resolution: Check Resend API status, API key validity, or wait and retry.\n */\nexport class EmailResendApiFailedError extends ApplicationError {\n constructor() {\n super(\n 'errors.email.resendApiFailed',\n ERROR_CODES.SYSTEM.INFRASTRUCTURE_ERROR,\n )\n }\n}\n","import { ApplicationError, ERROR_CODES } from '../../errors'\n\n/**\n * EmailProviderNotSupportedError\n *\n * Thrown when an unsupported email provider is configured.\n * Only 'resend' and 'smtp' providers are currently supported.\n *\n * Resolution: Set EMAIL_PROVIDER to either 'resend' or 'smtp'.\n */\nexport class EmailProviderNotSupportedError extends ApplicationError {\n constructor(provider: string) {\n super(\n 'errors.email.providerNotSupported',\n ERROR_CODES.SYSTEM.CONFIGURATION_ERROR,\n { provider }\n )\n }\n}\n","import { inject } from 'tsyringe'\nimport { Transient } from '../../di/decorators'\nimport type { EmailModuleOptions } from '../email.module'\nimport { EMAIL_TOKENS } from '../email.tokens'\nimport { EmailProviderNotSupportedError } from '../errors'\nimport type { IEmailProvider } from '../providers/email-provider.interface'\n\n/**\n * Email Provider Factory\n *\n * Creates email provider instances based on configuration.\n * Supports automatic provider selection from module options.\n *\n * Providers are loaded lazily via dynamic imports to avoid pulling in\n * heavy Node.js-only dependencies (e.g. nodemailer) at module parse time,\n * which would break Cloudflare Workers and vitest-pool-workers environments.\n */\n@Transient(EMAIL_TOKENS.EmailProviderFactory)\nexport class EmailProviderFactory {\n constructor(\n @inject(EMAIL_TOKENS.Options)\n private readonly options: EmailModuleOptions\n ) { }\n\n /**\n * Create email provider instance based on configuration\n *\n * @returns Email provider implementation\n * @throws EmailProviderNotSupportedError if provider is not supported\n */\n async create(): Promise<IEmailProvider> {\n switch (this.options.provider) {\n case 'resend': {\n const { ResendProvider } = await import('../providers/resend.provider')\n return new ResendProvider(this.options)\n }\n\n case 'smtp': {\n const { SmtpProvider } = await import('../providers/smtp.provider')\n return new SmtpProvider(this.options)\n }\n\n default:\n throw new EmailProviderNotSupportedError(this.options.provider)\n }\n }\n}\n","/**\n * Email Module\n *\n * Provides email sending capabilities with provider abstraction.\n * Supports multiple email providers (Resend, SMTP) with automatic provider selection.\n * Emails are sent asynchronously via Cloudflare Queues.\n *\n * **Usage:**\n * ```typescript\n * // In AppModule imports with static options\n * EmailModule.forRoot({\n * provider: 'resend',\n * apiKey: 'your-api-key',\n * from: { name: 'App', email: 'noreply@example.com' },\n * queue: 'notifications-queue',\n * })\n *\n * // Or with async options from config namespaces\n * EmailModule.forRootAsync({\n * inject: [emailConfig.KEY],\n * useFactory: (email) => ({\n * provider: email.provider,\n * apiKey: email.apiKey,\n * from: email.from,\n * queue: email.queue,\n * }),\n * })\n *\n * // In your service\n * @inject(EMAIL_TOKENS.EmailService)\n * private readonly emailService: EmailService\n *\n * await this.emailService.send({\n * to: 'user@example.com',\n * subject: 'Welcome',\n * template: <WelcomeEmail name=\"John\" />\n * })\n * ```\n */\n\nimport { Module } from '../module'\nimport type { AsyncModuleOptions, DynamicModule } from '../module/types'\nimport type { QueueName } from '../queue'\nimport { QUEUE_TOKENS, type QueueRegistry } from '../queue'\nimport { EmailConsumer } from './consumers/email.consumer'\nimport { EMAIL_TOKENS } from './email.tokens'\nimport { EmailProviderFactory, EmailService } from './services'\n\n/**\n * SMTP configuration options\n */\nexport interface SmtpConfig {\n /** SMTP server host */\n host: string\n /** SMTP server port */\n port: number\n /** Use TLS */\n secure?: boolean\n /** SMTP username */\n username?: string\n /** SMTP password */\n password?: string\n}\n\n/**\n * Email module configuration options\n */\nexport interface EmailModuleOptions {\n /** Email provider type */\n provider: 'resend' | 'smtp'\n\n /** Default from address */\n from: { name: string; email: string }\n\n /** Resend API key (required for resend provider) */\n apiKey?: string\n\n /** SMTP configuration (required for smtp provider) */\n smtp?: SmtpConfig\n\n /** Default reply-to address */\n replyTo?: string\n\n /**\n * Queue name for email dispatch.\n * The queue must be registered via QueueModule.registerQueue(name).\n */\n queue: QueueName\n}\n\n@Module({\n providers: [\n { provide: EMAIL_TOKENS.EmailService, useClass: EmailService },\n { provide: EMAIL_TOKENS.EmailProviderFactory, useClass: EmailProviderFactory },\n ],\n consumers: [EmailConsumer],\n})\nexport class EmailModule {\n /**\n * Configure EmailModule with static options\n *\n * @param options - Email configuration options\n * @returns Dynamic module with email infrastructure\n *\n * @example\n * ```typescript\n * EmailModule.forRoot({\n * provider: 'resend',\n * apiKey: env.RESEND_API_KEY,\n * from: { name: 'App', email: 'noreply@example.com' },\n * queue: 'notifications-queue',\n * })\n * ```\n */\n static forRoot(options: EmailModuleOptions): DynamicModule {\n return {\n module: EmailModule,\n providers: [\n { provide: EMAIL_TOKENS.Options, useValue: options },\n { provide: EMAIL_TOKENS.EmailQueue, useExisting: options.queue },\n ],\n }\n }\n\n /**\n * Configure EmailModule with async factory\n *\n * Use when configuration depends on other services.\n *\n * @param options - Async configuration with factory and inject tokens\n * @returns Dynamic module with email infrastructure\n *\n * @example\n * ```typescript\n * EmailModule.forRootAsync({\n * inject: [emailConfig.KEY],\n * useFactory: (email) => ({\n * provider: email.provider,\n * apiKey: email.apiKey,\n * from: email.from,\n * smtp: email.smtp,\n * queue: email.queue,\n * })\n * })\n * ```\n */\n static forRootAsync(options: AsyncModuleOptions<EmailModuleOptions>): DynamicModule {\n return {\n module: EmailModule,\n providers: [\n {\n provide: EMAIL_TOKENS.Options,\n useFactory: options.useFactory,\n inject: options.inject,\n },\n // Resolve queue from QueueRegistry using the queue name from options\n {\n provide: EMAIL_TOKENS.EmailQueue,\n useFactory: (emailOptions: EmailModuleOptions, registry: QueueRegistry) =>\n registry.getQueue(emailOptions.queue),\n inject: [EMAIL_TOKENS.Options, QUEUE_TOKENS.QueueRegistry],\n },\n ],\n }\n }\n}\n","import { z } from '../../i18n/validation'\n\n/**\n * Inline Email Attachment Schema\n *\n * Attachment with content embedded as base64 string.\n * Use for small files that can fit in queue message.\n */\nexport const inlineEmailAttachmentSchema = z.object({\n /**\n * Filename to display for the attachment\n */\n filename: z.string().min(1).max(255),\n\n /**\n * Base64 encoded content of the attachment\n */\n content: z.string(),\n\n /**\n * MIME type of the attachment (e.g., 'application/pdf', 'image/png')\n */\n contentType: z.string(),\n\n /**\n * Optional size of the attachment in bytes\n */\n size: z.number().positive().optional(),\n})\n\n/**\n * Storage Email Attachment Schema\n *\n * Attachment stored in cloud storage.\n * Content type and size are derived from storage metadata.\n * Use for large files to avoid queue message size limits.\n */\nexport const storageEmailAttachmentSchema = z.object({\n /**\n * Filename to display for the attachment\n */\n filename: z.string().min(1).max(255),\n\n /**\n * Path to the file in storage\n */\n storageKey: z.string(),\n\n /**\n * Optional storage disk name (uses default if not provided)\n */\n disk: z.string().optional(),\n})\n\n/**\n * Email Attachment Schema\n *\n * Union type - type is inferred from presence of `content` vs `storageKey`.\n * - If `content` is present: inline attachment\n * - If `storageKey` is present: storage-based attachment\n */\nexport const emailAttachmentSchema = z.union([\n inlineEmailAttachmentSchema,\n storageEmailAttachmentSchema,\n])\n\n/**\n * Type definitions\n */\nexport type InlineEmailAttachment = z.infer<typeof inlineEmailAttachmentSchema>\nexport type StorageEmailAttachment = z.infer<typeof storageEmailAttachmentSchema>\nexport type EmailAttachment = z.infer<typeof emailAttachmentSchema>\n\n/**\n * Resolved Email Attachment\n *\n * Attachment after resolution, ready for email provider.\n * Content can be Buffer (for inline) or ReadableStream (for storage-based).\n * Both nodemailer and Resend support these formats directly.\n */\nexport interface ResolvedEmailAttachment {\n filename: string\n content: Buffer | ReadableStream\n contentType: string\n}\n","import { withI18n, z } from '../../i18n/validation'\nimport { emailAttachmentSchema, type ResolvedEmailAttachment } from './email-attachment'\n\n/**\n * Email Address Schema\n *\n * Represents an email address with optional name\n */\nexport const emailAddressSchema = z.object({\n name: z.string().optional(),\n email: z.string().email(),\n})\n\n/**\n * Base Email Message Schema\n *\n * Defines the core structure for email messages.\n * Ensures either html or text content is provided.\n */\nexport const emailMessageSchema = z\n .object({\n /**\n * Recipient email address(es)\n * Can be a single email string or array of emails\n */\n to: z.union([z.string().email(), z.array(z.string().email())]),\n\n /**\n * Sender email address with optional name\n * Falls back to default from config if not provided\n */\n from: emailAddressSchema.optional(),\n\n /**\n * Email subject line\n */\n subject: z.string().min(1).max(500),\n\n /**\n * HTML content of the email\n * Either html or text must be provided\n */\n html: z.string().optional(),\n\n /**\n * Plain text content of the email\n * Either html or text must be provided\n */\n text: z.string().optional(),\n\n /**\n * Reply-to email address\n */\n replyTo: z.string().email().optional(),\n\n /**\n * CC recipients\n */\n cc: z.array(z.string().email()).optional(),\n\n /**\n * BCC recipients\n */\n bcc: z.array(z.string().email()).optional(),\n\n /**\n * Email attachments\n */\n attachments: z.array(emailAttachmentSchema).optional(),\n })\n .refine(\n (data) => data.html ?? data.text,\n withI18n('zodI18n.errors.custom.emailOrTextRequired')\n )\n\n/**\n * Type definition for email message\n */\nexport type EmailMessage = z.infer<typeof emailMessageSchema>\n\n/**\n * Type definition for email address\n */\nexport type EmailAddress = z.infer<typeof emailAddressSchema>\n\n/**\n * Resolved Email Message\n *\n * Email message with attachments resolved to Buffer content.\n * Used by providers after the consumer resolves storage-based attachments.\n */\nexport type ResolvedEmailMessage = Omit<EmailMessage, 'attachments'> & {\n attachments?: ResolvedEmailAttachment[]\n}\n","import { z } from '../../i18n/validation'\nimport { emailMessageSchema } from './email-message.contract'\n\n/**\n * Send Email Input Schema\n *\n * Input schema for sending emails through the EmailService.\n * Extends the base email message with optional metadata.\n * Uses safeExtend() because emailMessageSchema contains refinements.\n */\nexport const sendEmailInputSchema = emailMessageSchema.safeExtend({\n /**\n * Optional metadata to include with the email\n * Can be used for tracking, categorization, etc.\n */\n metadata: z.record(z.string(), z.unknown()).optional(),\n})\n\n/**\n * Type definition for send email input\n */\nexport type SendEmailInput = z.infer<typeof sendEmailInputSchema>\n\n/**\n * Send Batch Email Input Schema\n *\n * Schema for sending multiple emails in a batch\n */\nexport const sendBatchEmailInputSchema = z.object({\n /**\n * Array of email messages to send\n */\n messages: z.array(sendEmailInputSchema).min(1).max(100),\n})\n\n/**\n * Type definition for send batch email input\n */\nexport type SendBatchEmailInput = z.infer<typeof sendBatchEmailInputSchema>\n"],"mappings":"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"}
|
|
@@ -34,6 +34,12 @@ const errors = {
|
|
|
34
34
|
routeAccessDenied: "Resource not found",
|
|
35
35
|
controllerMethodNotFound: "Method {methodName} not found on {controllerName}",
|
|
36
36
|
controllerRegistration: "Failed to register controller {controllerName}: {reason}",
|
|
37
|
+
duplicateRouteName: "Duplicate route name \"{name}\". Already registered by {existingHandler}, cannot register {newHandler}.",
|
|
38
|
+
routeNameNotFound: "Route \"{name}\" not found in registry.",
|
|
39
|
+
missingRouteParam: "Missing required parameter \"{param}\" for route \"{name}\" (path: {path}).",
|
|
40
|
+
routerUseScopeViolation: "router.use() can only be called on the root Router, not inside group() callbacks. Use router.middleware() for scoped middleware.",
|
|
41
|
+
middlewareNextCalledMultipleTimes: "next() was called multiple times in \"{middlewareName}\" middleware. Ensure each middleware calls next() at most once.",
|
|
42
|
+
missingEnvironmentVariable: "Environment variable \"{variable}\" is required but not set.",
|
|
37
43
|
websocketBodyNotAvailable: "body() is not available in WebSocket gateways. Use WebSocket messages instead.",
|
|
38
44
|
websocketDuplicateEventHandler: "@{decorator}() is already applied to '{existingMethod}'. Only one method per gateway can handle this event.",
|
|
39
45
|
contextNotInitialized: "Context has not been initialized",
|
|
@@ -68,7 +74,11 @@ const errors = {
|
|
|
68
74
|
cronExecutionFailed: "{count} cron job(s) failed for schedule \"{schedule}\": {jobs}",
|
|
69
75
|
localeNotSupported: "Locale '{locale}' is not supported. Supported locales: {supportedLocales}",
|
|
70
76
|
translationMissing: "Translation missing for key '{key}' in locale '{locale}'",
|
|
77
|
+
containerNotInitialized: "Application container has not been initialized. Ensure Application.initialize() has been called.",
|
|
78
|
+
domainMismatch: "The requested domain does not match any configured route",
|
|
79
|
+
invalidSignature: "The URL signature is invalid or has expired",
|
|
71
80
|
schemaValidation: "Schema validation failed",
|
|
81
|
+
responseValidation: "Response validation failed",
|
|
72
82
|
openapiValidation: "OpenAPI validation failed: {details}",
|
|
73
83
|
openapiRouteRegistration: "Failed to register OpenAPI route {path}: {reason}",
|
|
74
84
|
email: {
|
|
@@ -86,8 +96,9 @@ const errors = {
|
|
|
86
96
|
fileTooLarge: "File size {size} exceeds maximum allowed size of {maxSize}",
|
|
87
97
|
presignedUrlInvalidExpiry: "Expiry must be between {min} and {max} seconds",
|
|
88
98
|
diskNotConfigured: "Disk \"{disk}\" is not configured",
|
|
89
|
-
|
|
90
|
-
|
|
99
|
+
responseBodyMissing: "No body in storage response for path: {path}",
|
|
100
|
+
r2BindingNotFound: "R2 binding \"{binding}\" was not found in the environment",
|
|
101
|
+
r2PresignedUrlSecretMissing: "APP_SECRET environment variable is required for presigned URLs"
|
|
91
102
|
},
|
|
92
103
|
cache: {
|
|
93
104
|
getFailed: "Failed to retrieve value from cache for key '{key}'",
|
|
@@ -95,34 +106,6 @@ const errors = {
|
|
|
95
106
|
deleteFailed: "Failed to delete value from cache for key '{key}'",
|
|
96
107
|
listFailed: "Failed to list cache keys"
|
|
97
108
|
},
|
|
98
|
-
auth: {
|
|
99
|
-
tokenRequired: "Verification token is required",
|
|
100
|
-
invalidToken: "Invalid or expired verification token",
|
|
101
|
-
verificationFailed: "Verification failed. Please try again.",
|
|
102
|
-
userNotFound: "User not found. Please check your credentials.",
|
|
103
|
-
invalidCredentials: "Invalid email or password",
|
|
104
|
-
invalidPassword: "Invalid password",
|
|
105
|
-
invalidEmail: "Invalid email address",
|
|
106
|
-
sessionExpired: "Your session has expired. Please sign in again.",
|
|
107
|
-
emailNotVerified: "Please verify your email address before signing in",
|
|
108
|
-
passwordTooShort: "Password must be at least {minLength} characters",
|
|
109
|
-
passwordTooLong: "Password must be at most {maxLength} characters",
|
|
110
|
-
accountAlreadyExists: "An account with this email already exists",
|
|
111
|
-
failedToCreateUser: "Failed to create user account. Please try again.",
|
|
112
|
-
failedToCreateSession: "Failed to create session. Please try again.",
|
|
113
|
-
failedToGetSession: "Failed to retrieve session. Please try again.",
|
|
114
|
-
failedToUpdateUser: "Failed to update user information. Please try again.",
|
|
115
|
-
failedToGetUserInfo: "Failed to retrieve user information. Please try again.",
|
|
116
|
-
socialAccountLinked: "This social account is already linked to another user",
|
|
117
|
-
providerNotFound: "Authentication provider not found",
|
|
118
|
-
userEmailNotFound: "User email address not found",
|
|
119
|
-
accountNotFound: "Account not found",
|
|
120
|
-
credentialAccountNotFound: "Credential account not found",
|
|
121
|
-
cannotUnlinkLastAccount: "Cannot unlink your last account",
|
|
122
|
-
userAlreadyHasPassword: "User already has a password set",
|
|
123
|
-
emailCannotBeUpdated: "Email address cannot be updated at this time",
|
|
124
|
-
tokenExpired: "The verification token has expired. Please request a new verification email."
|
|
125
|
-
},
|
|
126
109
|
seederNameCollision: "Seeder name collision: \"{name}\" is already registered. Use distinct class names for each seeder.",
|
|
127
110
|
seederNotRegistered: "Seeder \"{name}\" is not registered",
|
|
128
111
|
migration: {
|
|
@@ -316,4 +299,4 @@ var en_exports = /* @__PURE__ */ __exportAll({
|
|
|
316
299
|
//#endregion
|
|
317
300
|
export { errors as a, emails as i, zodI18n as n, common as o, validation as r, en_exports as t };
|
|
318
301
|
|
|
319
|
-
//# sourceMappingURL=en-
|
|
302
|
+
//# sourceMappingURL=en-rHmW6vD9.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"en-rHmW6vD9.mjs","names":[],"sources":["../src/i18n/messages/en/common.ts","../src/i18n/messages/en/errors.ts","../src/i18n/messages/en/emails.ts","../src/i18n/messages/en/validation.ts","../src/i18n/messages/en/zod.ts","../src/i18n/messages/en/index.ts"],"sourcesContent":["/**\n * System Common Messages - English\n *\n * Common messages used by packages/modules infrastructure.\n * These are automatically merged with application-specific messages.\n */\n\nexport const common = {\n api: {\n title: 'Stratal API',\n description: 'Platform API',\n serverDescription: 'API server',\n security: {\n bearerAuth: 'JWT Bearer token authentication',\n apiKey: 'API key for service authentication',\n sessionCookie: 'Session cookie for browser authentication'\n }\n }\n} as const\n","/**\n * System Error Messages - English\n *\n * Error messages used by packages/modules infrastructure.\n * These are automatically merged with application-specific messages.\n */\n\nexport const errors = {\n // Generic errors\n internalError: 'An internal error occurred',\n notFound: 'Resource not found',\n unauthorized: 'Unauthorized. Please sign in.',\n forbidden: 'Access denied',\n\n // Router errors\n honoAppAlreadyConfigured: 'HonoApp has already been configured and can only be configured once',\n routeNotFound: 'Route not found: {method} {path}',\n routeAccessDenied: 'Resource not found',\n controllerMethodNotFound: 'Method {methodName} not found on {controllerName}',\n controllerRegistration: 'Failed to register controller {controllerName}: {reason}',\n duplicateRouteName: 'Duplicate route name \"{name}\". Already registered by {existingHandler}, cannot register {newHandler}.',\n routeNameNotFound: 'Route \"{name}\" not found in registry.',\n missingRouteParam: 'Missing required parameter \"{param}\" for route \"{name}\" (path: {path}).',\n routerUseScopeViolation: 'router.use() can only be called on the root Router, not inside group() callbacks. Use router.middleware() for scoped middleware.',\n middlewareNextCalledMultipleTimes: 'next() was called multiple times in \"{middlewareName}\" middleware. Ensure each middleware calls next() at most once.',\n missingEnvironmentVariable: 'Environment variable \"{variable}\" is required but not set.',\n\n // WebSocket errors\n websocketBodyNotAvailable: 'body() is not available in WebSocket gateways. Use WebSocket messages instead.',\n websocketDuplicateEventHandler: '@{decorator}() is already applied to \\'{existingMethod}\\'. Only one method per gateway can handle this event.',\n\n // Context errors\n contextNotInitialized: 'Context has not been initialized',\n userNotAuthenticated: 'User is not authenticated',\n insufficientPermissions: 'Insufficient permissions to perform this action',\n requestContainerNotInitialized: 'Request container has not been initialized',\n requestScopeOperationNotAllowed: '{methodName}() cannot be called on this container scope. Check if you are calling it on the correct container (global vs request-scoped).',\n conditionalBindingFallback: 'Conditional binding predicate returned false for token \"{token}\" but no fallback was provided and no existing registration exists.',\n\n // Configuration errors\n configNotInitialized: 'Configuration service has not been initialized',\n configModuleNotInitialized: 'ConfigModule.forRoot() was not called before module initialization',\n stratalNotInitialized: 'Stratal has not been instantiated. Ensure you export a Stratal instance as the default export.',\n\n // Module errors\n moduleAlreadyRegistered: 'Module {moduleName} is already registered',\n moduleDependencyNotFound: 'Module dependency {dependency} not found for module {moduleName}',\n moduleCircularDependency: 'Circular dependency detected: {cycle}',\n invalidModuleProvider: 'Invalid module provider configuration: {provider}',\n\n // Database errors\n databaseGeneric: 'Database error occurred',\n databaseRecordNotFound: 'Record not found in database',\n databaseUniqueConstraint: 'Record already exists',\n databaseForeignKeyConstraint: 'Related record not found',\n databaseConnectionFailed: 'Failed to connect to database',\n databaseTimeout: 'Database query timeout',\n databaseNullConstraint: 'Required field is missing',\n databaseTooManyConnections: 'Too many database connections',\n databaseTransactionConflict: 'Transaction conflict or deadlock',\n databaseConstraintFailed: 'A database constraint was violated',\n databaseTableNotFound: 'The specified table does not exist in the database',\n databaseColumnNotFound: 'The specified column does not exist in the table',\n databaseInvalidQuery: 'The database query is invalid or malformed',\n invalidErrorCodeRange: 'Invalid error code range: {code}',\n\n // Queue errors\n queueBindingNotFound: 'Queue binding {queueName} not found in environment',\n queueProviderNotSupported: 'Queue provider \"{provider}\" is not supported. Valid providers: cloudflare, sync',\n\n // Cron errors\n cronExecutionFailed: '{count} cron job(s) failed for schedule \"{schedule}\": {jobs}',\n\n // i18n errors\n localeNotSupported: \"Locale '{locale}' is not supported. Supported locales: {supportedLocales}\",\n translationMissing: \"Translation missing for key '{key}' in locale '{locale}'\",\n\n // Container errors\n containerNotInitialized: 'Application container has not been initialized. Ensure Application.initialize() has been called.',\n\n // Domain routing errors\n domainMismatch: 'The requested domain does not match any configured route',\n\n // Signature errors\n invalidSignature: 'The URL signature is invalid or has expired',\n\n // Schema validation errors\n schemaValidation: 'Schema validation failed',\n responseValidation: 'Response validation failed',\n\n // OpenAPI errors\n openapiValidation: 'OpenAPI validation failed: {details}',\n openapiRouteRegistration: 'Failed to register OpenAPI route {path}: {reason}',\n\n // Email errors\n email: {\n resendApiKeyMissing: 'Resend API key not configured. Set RESEND_EMAIL_API_KEY environment variable.',\n smtpConfigurationMissing: 'SMTP configuration missing. Set SMTP_URL environment variable.',\n smtpHostMissing: 'SMTP host not configured. Check SMTP_URL format (smtp://user:pass@host:port).',\n smtpConnectionFailed: 'Failed to connect to SMTP server {smtpHost}:{smtpPort}',\n resendApiFailed: 'Resend API error',\n providerNotSupported: 'Unsupported email provider: {provider}. Supported providers: resend, smtp'\n },\n\n // Storage errors\n storage: {\n fileNotFound: 'File at path \"{path}\" was not found',\n invalidDisk: 'Storage disk \"{disk}\" is not configured',\n invalidFileType: 'File type \"{mimeType}\" is not allowed',\n fileTooLarge: 'File size {size} exceeds maximum allowed size of {maxSize}',\n presignedUrlInvalidExpiry: 'Expiry must be between {min} and {max} seconds',\n diskNotConfigured: 'Disk \"{disk}\" is not configured',\n responseBodyMissing: 'No body in storage response for path: {path}',\n r2BindingNotFound: 'R2 binding \"{binding}\" was not found in the environment',\n r2PresignedUrlSecretMissing: 'APP_SECRET environment variable is required for presigned URLs',\n },\n\n // Cache errors\n cache: {\n getFailed: \"Failed to retrieve value from cache for key '{key}'\",\n putFailed: \"Failed to store value in cache for key '{key}'\",\n deleteFailed: \"Failed to delete value from cache for key '{key}'\",\n listFailed: 'Failed to list cache keys'\n },\n\n // Seeder errors\n seederNameCollision: 'Seeder name collision: \"{name}\" is already registered. Use distinct class names for each seeder.',\n seederNotRegistered: 'Seeder \"{name}\" is not registered',\n\n // Migration errors\n migration: {\n failed: 'Migration {migrationName} failed: {error}',\n checksumMismatch: 'Migration {migrationName} checksum mismatch (expected: {expected}, actual: {actual})',\n alreadyApplied: 'Migration {migrationName} has already been applied',\n notFound: 'Migration {migrationName} not found',\n },\n} as const\n","/**\n * System Email Messages - English\n *\n * Email-related messages used by packages/modules infrastructure.\n * These are automatically merged with application-specific messages.\n */\n\nexport const emails = {\n magicLink: {\n subject: 'Your sign-in link'\n }\n} as const\n","/**\n * Form validation messages - English\n */\n\nexport const validation = {\n required: 'This field is required',\n email: 'Invalid email address',\n minLength: 'Must be at least {min} characters',\n maxLength: 'Must not exceed {max} characters',\n min: 'Must be at least {min}',\n max: 'Must not exceed {max}',\n pattern: 'Invalid format',\n numeric: 'Must be a number',\n url: 'Invalid URL',\n date: 'Invalid date',\n passwordStrength: 'Password must contain at least one uppercase letter, one lowercase letter, and one number',\n passwordMatch: 'Passwords do not match',\n unique: 'This value already exists',\n phone: 'Invalid phone number',\n fileRequired: 'Please upload a file',\n fileTooLarge: 'File must be smaller than {max}',\n invalidFileType: 'Please upload a PDF, JPG, or PNG file',\n schoolTypes: {\n required: 'School type is required',\n atLeastOne: 'Please select at least one school type',\n invalidCode: 'Invalid school type: {code}',\n notAvailableInCountry: '{schoolType} is not available in {country}',\n countryNotSupported: 'Country {country} is not supported'\n },\n timezone: {\n required: 'Timezone is required',\n invalid: 'Invalid timezone. Please select a valid IANA timezone.'\n },\n locale: {\n required: 'Language is required',\n invalid: 'Invalid language. Supported languages: {locales}'\n }\n} as const\n","/**\n * Zod validation error messages - English\n *\n * Comprehensive messages for all Zod validation error codes\n * Structured to match Zod's issue types and validation contexts\n */\n\nexport const zodI18n = {\n errors: {\n // General errors\n required: 'Required',\n invalid_type: 'Expected {expected}, received {received}',\n invalid_literal: 'Invalid literal value, expected {expected}',\n custom: {\n default: 'Invalid value',\n // Email validation\n emailOrTextRequired: 'Either html or text content must be provided',\n invalidFromEmail: 'Invalid from email address',\n // Storage validation\n fileInstanceRequired: 'File must be a File instance',\n filePathRequired: 'File path is required',\n diskNameRequired: 'Disk name is required',\n endpointRequired: 'Endpoint URL is required for S3',\n bucketNameRequired: 'Bucket name is required',\n accessKeyRequired: 'Access key ID is required',\n secretKeyRequired: 'Secret access key is required',\n storageDiskRequired: 'At least one storage disk is required',\n // Database validation\n databaseUrlRequired: 'Database URL is required',\n // Domain validation\n domainRequired: 'Domain is required',\n domainTooLong: 'Domain too long',\n invalidDomainFormat: 'Invalid domain format',\n },\n invalid_union: 'Invalid input',\n invalid_union_discriminator: 'Invalid discriminator value. Expected {options}',\n invalid_enum_value: 'Invalid enum value. Expected {options}, received {received}',\n unrecognized_keys: 'Unrecognized key(s) in object: {keys}',\n invalid_arguments: 'Invalid function arguments',\n invalid_return_type: 'Invalid function return type',\n invalid_date: 'Invalid date',\n invalid_intersection_types: 'Intersection results could not be merged',\n not_multiple_of: 'Number must be a multiple of {multipleOf}',\n not_finite: 'Number must be finite',\n\n // String-specific validation errors\n invalid_string: {\n email: 'Invalid email address',\n url: 'Invalid URL',\n uuid: 'Invalid UUID',\n cuid: 'Invalid CUID',\n cuid2: 'Invalid CUID2',\n ulid: 'Invalid ULID',\n regex: 'Invalid format',\n datetime: 'Invalid datetime',\n ip: 'Invalid IP address',\n emoji: 'Invalid emoji',\n startsWith: 'Must start with \"{startsWith}\"',\n endsWith: 'Must end with \"{endsWith}\"',\n includes: 'Must include \"{includes}\"',\n base64: 'Invalid Base64',\n nanoid: 'Invalid NanoID',\n cidr: 'Invalid CIDR',\n jwt: 'Invalid JWT',\n time: 'Invalid time',\n },\n\n // Size validation errors (strings, arrays, numbers)\n too_small: {\n string: {\n exact: 'Must be exactly {minimum} characters',\n inclusive: 'Must be at least {minimum} characters',\n not_inclusive: 'Must be more than {minimum} characters',\n },\n number: {\n exact: 'Must be exactly {minimum}',\n inclusive: 'Must be at least {minimum}',\n not_inclusive: 'Must be greater than {minimum}',\n },\n array: {\n exact: 'Must contain exactly {minimum} item(s)',\n inclusive: 'Must contain at least {minimum} item(s)',\n not_inclusive: 'Must contain more than {minimum} item(s)',\n },\n set: {\n exact: 'Must contain exactly {minimum} item(s)',\n inclusive: 'Must contain at least {minimum} item(s)',\n not_inclusive: 'Must contain more than {minimum} item(s)',\n },\n date: {\n exact: 'Date must be {minimum}',\n inclusive: 'Date must be {minimum} or later',\n not_inclusive: 'Date must be after {minimum}',\n },\n bigint: {\n exact: 'Must be exactly {minimum}',\n inclusive: 'Must be at least {minimum}',\n not_inclusive: 'Must be greater than {minimum}',\n },\n },\n\n too_big: {\n string: {\n exact: 'Must be exactly {maximum} characters',\n inclusive: 'Must be at most {maximum} characters',\n not_inclusive: 'Must be less than {maximum} characters',\n },\n number: {\n exact: 'Must be exactly {maximum}',\n inclusive: 'Must be at most {maximum}',\n not_inclusive: 'Must be less than {maximum}',\n },\n array: {\n exact: 'Must contain exactly {maximum} item(s)',\n inclusive: 'Must contain at most {maximum} item(s)',\n not_inclusive: 'Must contain less than {maximum} item(s)',\n },\n set: {\n exact: 'Must contain exactly {maximum} item(s)',\n inclusive: 'Must contain at most {maximum} item(s)',\n not_inclusive: 'Must contain less than {maximum} item(s)',\n },\n date: {\n exact: 'Date must be {maximum}',\n inclusive: 'Date must be {maximum} or earlier',\n not_inclusive: 'Date must be before {maximum}',\n },\n bigint: {\n exact: 'Must be exactly {maximum}',\n inclusive: 'Must be at most {maximum}',\n not_inclusive: 'Must be less than {maximum}',\n },\n },\n },\n} as const\n","/**\n * System Messages - English\n *\n * Re-exports all system message categories.\n * These messages are used by packages/modules infrastructure\n * and are automatically merged with application-specific messages.\n */\n\nexport { common } from './common'\nexport { errors } from './errors'\nexport { emails } from './emails'\nexport { validation } from './validation'\nexport { zodI18n } from './zod'\n"],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/env.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Cloudflare Worker Environment Bindings
|
|
4
|
+
*
|
|
5
|
+
* This interface defines the base environment bindings required by Stratal.
|
|
6
|
+
* Use TypeScript module augmentation to add your own application-specific bindings.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* declare module 'stratal' {
|
|
11
|
+
* interface StratalEnv {
|
|
12
|
+
* DATABASE: D1Database
|
|
13
|
+
* NOTIFICATIONS_QUEUE: Queue
|
|
14
|
+
* }
|
|
15
|
+
* }
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
interface StratalEnv {
|
|
19
|
+
ENVIRONMENT: string;
|
|
20
|
+
CACHE: KVNamespace;
|
|
21
|
+
APP_SECRET?: string;
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
export { StratalEnv as t };
|
|
25
|
+
//# sourceMappingURL=env-CamWD-U1.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-CamWD-U1.d.mts","names":[],"sources":["../src/env.ts"],"mappings":";;AAgBA;;;;;;;;;;;;;;;UAAiB,UAAA;EACf,WAAA;EACA,KAAA,EAAO,WAAA;EACP,UAAA;AAAA"}
|
package/dist/errors/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { ApplicationError, ERROR_CODES, Environment, ErrorCode, ErrorResponse,
|
|
1
|
+
import { An as ContextCallback, F as QueueExceptionContext, Fn as RespondCallback, Hr as ErrorResponse, I as createCliExceptionContext, L as createCronExceptionContext, M as CronExceptionContext, Mn as RenderableCallback, N as ExceptionContext, Nn as Reportable, On as ExceptionHandler, P as HttpExceptionContext, Pn as ReportableCallback, R as createHttpExceptionContext, Ur as isErrorResponse, Vr as Environment, a as InternalError, c as getHttpStatus, d as ApplicationError, f as ERROR_CODES, i as isApplicationError, j as CliExceptionContext, jn as LogSeverity, kn as ApplicationErrorConstructor, l as resolveHttpStatus, n as RequestContainerNotInitializedError, o as HttpException, p as ErrorCode, r as ContainerNotInitializedError, s as abort, t as StratalNotInitializedError, u as DefaultExceptionHandler, z as createQueueExceptionContext } from "../index-DPFqRs8L.mjs";
|
|
2
|
+
export { ApplicationError, ApplicationErrorConstructor, CliExceptionContext, ContainerNotInitializedError, ContextCallback, CronExceptionContext, DefaultExceptionHandler, ERROR_CODES, Environment, ErrorCode, ErrorResponse, ExceptionContext, ExceptionHandler, HttpException, HttpExceptionContext, InternalError, LogSeverity, QueueExceptionContext, RenderableCallback, Reportable, ReportableCallback, RequestContainerNotInitializedError, RespondCallback, StratalNotInitializedError, abort, createCliExceptionContext, createCronExceptionContext, createHttpExceptionContext, createQueueExceptionContext, getHttpStatus, isApplicationError, isErrorResponse, resolveHttpStatus };
|
package/dist/errors/index.mjs
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
export { ApplicationError, ERROR_CODES, GlobalErrorHandler, InternalError, RequestContainerNotInitializedError, StratalNotInitializedError, getHttpStatus, isApplicationError, isErrorResponse };
|
|
1
|
+
import { C as abort, H as ApplicationError, O as ContainerNotInitializedError, S as HttpException, _ as ExceptionHandler, a as createHttpExceptionContext, b as getHttpStatus, g as DefaultExceptionHandler, h as isErrorResponse, i as createCronExceptionContext, k as ERROR_CODES, n as RequestContainerNotInitializedError, o as createQueueExceptionContext, r as createCliExceptionContext, t as StratalNotInitializedError, v as isApplicationError, x as resolveHttpStatus, y as InternalError } from "../errors-B4pYgYON.mjs";
|
|
2
|
+
export { ApplicationError, ContainerNotInitializedError, DefaultExceptionHandler, ERROR_CODES, ExceptionHandler, HttpException, InternalError, RequestContainerNotInitializedError, StratalNotInitializedError, abort, createCliExceptionContext, createCronExceptionContext, createHttpExceptionContext, createQueueExceptionContext, getHttpStatus, isApplicationError, isErrorResponse, resolveHttpStatus };
|