First public release. Email + OTP channels, multi-tenant by design, pluggable
providers and storage, zero runtime dependencies, and never an @prisma/client
import.
Added
BymaxNotificationModule— dynamic NestJS 11 module withforRootand
forRootAsync(useFactory). Conditional provider registration: only the channels
you configure are wired into the container; configuring an unconfigured channel throws
at startup.- Email channel —
EmailServiceplus theIEmailProvidercontract. Bundled
ResendEmailProvider(Resend) andNoOpEmailProvider(dev/test — logs subject and
recipient only, never the body). Attachment size guard (maxAttachmentBytes, default
10 MiB). - OTP channel —
OtpService(generate/verify/consume/ status) plus the
IOtpStoragecontract. Codes vianode:crypto.randomInt(numeric / alpha /
alphanumeric, built character-by-character to preserve leading zeros and avoid integer
overflow); verification viacrypto.timingSafeEqual. Atomic attempt counting
(consumeAttempt) and resend cooldown (tryAcquireCooldown,SET NX EX) so
maxAttemptsand anti-resend cannot be bypassed under concurrency. - Reference storages —
RedisOtpStorage(keys hashedsha256(tenantId:recipient),
Lua-atomic primitives) andInMemoryOtpStorage(single-threaded atomicity for dev/test). - Templating —
IEmailTemplateRendererplus the bundledDefaultTemplateRenderer
({{var}}interpolation with automatic HTML escaping in the HTML body; subject and
plaintext left raw).CANONICAL_EMAIL_TEMPLATESconstant for stable template names. - Multi-tenant — every operation scoped by
tenantId; SHA-256 store keys;
tenantIdResolver(anti-spoofing) read by the audit interceptor as the trusted source. - Audit log — opt-in
INotificationLogRepository(fire-and-forget,
swallowErrors: trueby default; optionalmaskRecipient) and a
NotificationAuditInterceptorfor HTTP-levelsent/failedentries. OTP codes are
never written to any audit entry. NotificationService— uniform dispatch façade over the configured channels.NotificationException+ a 22-entry error catalog (NOTIFICATION_ERROR_DEFINITIONS)
with stable codes and HTTP statuses; response shape{ error: { code, message, details } }../sharedsubpath — zero-dependency types and constants (NOTIFICATION_ERROR_CODES,
DEFAULT_TTLS,OtpPurpose,NotificationChannel) importable in any environment../reactsubpath —useOtpInput(multi-slot input with paste, Backspace, and
Arrow navigation) anduseOtpCountdown(expiry countdown) hooks. UX/state only — no
HTTP client, no Node builtins.- Adapter examples — Handlebars, MJML, and React Email renderers
(docs/templates/); a PrismaINotificationLogRepositoryand schema fragment
(docs/schemas/). Not imported by the library. - Supply chain — published with npm provenance (OIDC); CodeQL
security-extended,
OpenSSF Scorecard, and acheck:no-prismaCI gate.
Not implemented
The interfaces below ship so you can plan your dispatch code paths against them,
but the services behind them do not exist yet and configuring those channels is
rejected at startup rather than failing on the first send.
- SMS channel —
ISmsProvideris declared; configuring thesmschannel throws. - Push channel —
IPushProvideris declared on the same terms; configuring the
pushchannel throws. forRootAsyncuseClass/useExisting— onlyuseFactoryis wired.- Multi-provider failover and routing.