d28c873d40
Email can now carry attachments (e.g. an invoice PDF). The kernel already stored
attachments as message parts + the media StoragePort holds the bytes; the gap was
the email envelope.
- EMAIL payload carries attachment REFS ({filename, contentRef, mimeType}), not
bytes — the ledger + T8 PII redaction stay small; bytes are fetched at send time.
- SmtpProvider takes an AttachmentResolver; resolves each ref via storage and attaches
(nodemailer). FAILS CLOSED if a declared attachment can't be resolved (or no resolver
is wired) — never send a receipt/invoice missing its file; a FAILED command retries.
- CapabilityProviderRegistry injects the resolver from STORAGE_PORT (@Optional);
MediaModule exports STORAGE_PORT, CapabilityModule imports MediaModule. No cycle.
- TemplatedSender + MailService + the /v1/mail/send DTO pass attachments through.
Verified: 6 new unit tests (attach, fail-closed x2, plain-unaffected, resolver,
pass-through) + a REAL Ethereal SMTP send WITH a PDF attachment (SENT). Full suite
294/294, boundary + build clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
17 lines
666 B
TypeScript
17 lines
666 B
TypeScript
import { Module } from '@nestjs/common';
|
|
import { MediaModule } from '../media/media.module';
|
|
import { CapabilityProviderRegistry } from './capability.registry';
|
|
import { CapabilityBroker } from './capability.broker';
|
|
|
|
/**
|
|
* The governed egress boundary (P9 slice 1). Exports the broker + registry so the
|
|
* outbound layer routes all sends through policy + obligations + a pluggable
|
|
* provider. PLATFORM_PORTS (for the opa gate) comes from the @Global PlatformModule.
|
|
*/
|
|
@Module({
|
|
imports: [MediaModule],
|
|
providers: [CapabilityProviderRegistry, CapabilityBroker],
|
|
exports: [CapabilityBroker, CapabilityProviderRegistry],
|
|
})
|
|
export class CapabilityModule {}
|