@bernierllc/email-manager 0.4.0 → 0.4.2

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 (68) hide show
  1. package/README.md +653 -0
  2. package/dist/capabilities/degradation-logger.d.ts +59 -0
  3. package/dist/capabilities/degradation-logger.d.ts.map +1 -0
  4. package/dist/capabilities/degradation-logger.js +106 -0
  5. package/dist/capabilities/degradation-logger.js.map +1 -0
  6. package/dist/capabilities/feature-router.d.ts +55 -0
  7. package/dist/capabilities/feature-router.d.ts.map +1 -0
  8. package/dist/capabilities/feature-router.js +143 -0
  9. package/dist/capabilities/feature-router.js.map +1 -0
  10. package/dist/capabilities/in-memory-resolver.d.ts +27 -0
  11. package/dist/capabilities/in-memory-resolver.d.ts.map +1 -0
  12. package/dist/capabilities/in-memory-resolver.js +79 -0
  13. package/dist/capabilities/in-memory-resolver.js.map +1 -0
  14. package/dist/capabilities/index.d.ts +13 -0
  15. package/dist/capabilities/index.d.ts.map +1 -0
  16. package/dist/capabilities/index.js +21 -0
  17. package/dist/capabilities/index.js.map +1 -0
  18. package/dist/capabilities/matrix.d.ts +66 -0
  19. package/dist/capabilities/matrix.d.ts.map +1 -0
  20. package/dist/capabilities/matrix.js +247 -0
  21. package/dist/capabilities/matrix.js.map +1 -0
  22. package/dist/capabilities/redis-resolver.d.ts +95 -0
  23. package/dist/capabilities/redis-resolver.d.ts.map +1 -0
  24. package/dist/capabilities/redis-resolver.js +227 -0
  25. package/dist/capabilities/redis-resolver.js.map +1 -0
  26. package/dist/capabilities/resolver-factory.d.ts +30 -0
  27. package/dist/capabilities/resolver-factory.d.ts.map +1 -0
  28. package/dist/capabilities/resolver-factory.js +70 -0
  29. package/dist/capabilities/resolver-factory.js.map +1 -0
  30. package/dist/capabilities/resolver.d.ts +40 -0
  31. package/dist/capabilities/resolver.d.ts.map +1 -0
  32. package/dist/capabilities/resolver.js +18 -0
  33. package/dist/capabilities/resolver.js.map +1 -0
  34. package/dist/capabilities/routing-metadata.d.ts +16 -0
  35. package/dist/capabilities/routing-metadata.d.ts.map +1 -0
  36. package/dist/capabilities/routing-metadata.js +17 -0
  37. package/dist/capabilities/routing-metadata.js.map +1 -0
  38. package/dist/capabilities/safe-resolver.d.ts +24 -0
  39. package/dist/capabilities/safe-resolver.d.ts.map +1 -0
  40. package/dist/capabilities/safe-resolver.js +48 -0
  41. package/dist/capabilities/safe-resolver.js.map +1 -0
  42. package/dist/config/schema.d.ts +99 -4
  43. package/dist/config/schema.d.ts.map +1 -1
  44. package/dist/config/schema.js +17 -0
  45. package/dist/config/schema.js.map +1 -1
  46. package/dist/email-manager.d.ts.map +1 -1
  47. package/dist/email-manager.js +28 -1
  48. package/dist/email-manager.js.map +1 -1
  49. package/dist/enhanced-email-manager.d.ts +163 -1
  50. package/dist/enhanced-email-manager.d.ts.map +1 -1
  51. package/dist/enhanced-email-manager.js +412 -8
  52. package/dist/enhanced-email-manager.js.map +1 -1
  53. package/dist/errors.d.ts +11 -0
  54. package/dist/errors.d.ts.map +1 -1
  55. package/dist/errors.js +13 -0
  56. package/dist/errors.js.map +1 -1
  57. package/dist/index.d.ts +13 -1
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +7 -1
  60. package/dist/index.js.map +1 -1
  61. package/dist/managers/provider-manager.d.ts +43 -0
  62. package/dist/managers/provider-manager.d.ts.map +1 -1
  63. package/dist/managers/provider-manager.js +102 -4
  64. package/dist/managers/provider-manager.js.map +1 -1
  65. package/dist/types.d.ts +66 -0
  66. package/dist/types.d.ts.map +1 -1
  67. package/dist/types.js.map +1 -1
  68. package/package.json +38 -25
@@ -0,0 +1,247 @@
1
+ /*
2
+ Copyright (c) 2025 Bernier LLC
3
+
4
+ This file is licensed to the client under a limited-use license.
5
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
6
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
+ */
8
+ // ---------------------------------------------------------------------------
9
+ // Feature Definitions -- SINGLE SOURCE OF TRUTH
10
+ // ---------------------------------------------------------------------------
11
+ // Every feature the platform recognises is declared here. Types, arrays, and
12
+ // the capability matrix itself are all derived from this object so that adding
13
+ // a feature in one place is enough.
14
+ // ---------------------------------------------------------------------------
15
+ export const FEATURE_DEFINITIONS = {
16
+ sendEmail: 'Send a single email through a provider',
17
+ batchSend: 'Send multiple emails in a single operation',
18
+ providerTemplates: 'Use templates stored on the provider (server-side)',
19
+ localTemplates: 'Render templates locally before sending',
20
+ calendarInvites: 'Attach iCalendar (.ics) invites to outgoing emails',
21
+ calendarEventMgmt: 'Create, update, and cancel calendar events programmatically',
22
+ webhooksReceive: 'Receive webhook callbacks from the provider for email events',
23
+ webhookNormalization: 'Normalize provider-specific webhook payloads into a common format',
24
+ deliveryTracking: 'Track delivery status of sent emails (delivered, bounced, etc.)',
25
+ inboundEmailParsing: 'Parse inbound emails forwarded by the provider',
26
+ /**
27
+ * subscriptionMgmt -- Manage subscriber lists, preference centres, and
28
+ * opt-in / opt-out lifecycle. This is distinct from suppressionLists which
29
+ * deals with bounce/complaint suppression at the provider level.
30
+ */
31
+ subscriptionMgmt: 'Manage subscriber lists, preferences, and opt-in/opt-out lifecycle',
32
+ /**
33
+ * suppressionLists -- Provider-level bounce and complaint suppression.
34
+ * Automatically maintained by the provider to prevent sending to addresses
35
+ * that have bounced or filed complaints. Distinct from subscriptionMgmt
36
+ * which handles user-facing subscription preferences.
37
+ */
38
+ suppressionLists: 'Provider-level bounce and complaint suppression lists',
39
+ unsubscribeUrlGeneration: 'Generate one-click unsubscribe URLs for email headers',
40
+ scheduledSend: 'Schedule emails for future delivery',
41
+ openClickTracking: 'Track email opens and link clicks',
42
+ emailContentParsing: 'Parse and transform email content (HTML/text)',
43
+ emailHeaderMgmt: 'Set and manage custom email headers',
44
+ attachments: 'Attach files to outgoing emails',
45
+ advancedAttachmentProcessing: 'Process, validate, and transform attachments (resize, compress, etc.)',
46
+ dkimSpf: 'Programmatic DKIM/SPF authentication configuration',
47
+ linkBranding: 'Custom branded tracking domains for links',
48
+ retryResilience: 'Automatic retry with backoff on transient failures',
49
+ multiProviderFailover: 'Automatic failover to alternate providers on failure',
50
+ };
51
+ /**
52
+ * Ordered array of every feature name (derived from FEATURE_DEFINITIONS keys).
53
+ */
54
+ export const FEATURE_NAMES = Object.keys(FEATURE_DEFINITIONS);
55
+ export const PROVIDER_NAMES = [
56
+ 'sendgrid',
57
+ 'mailgun',
58
+ 'postmark',
59
+ 'ses',
60
+ 'smtp',
61
+ ];
62
+ // ---------------------------------------------------------------------------
63
+ // Helper builders -- keep individual entries concise
64
+ // ---------------------------------------------------------------------------
65
+ function provider(description, opts) {
66
+ return {
67
+ source: 'provider',
68
+ description,
69
+ ...(opts?.limitations ? { limitations: opts.limitations } : {}),
70
+ degradation: null,
71
+ overridable: opts?.overridable ?? false,
72
+ };
73
+ }
74
+ function platform(description, degradation, opts) {
75
+ return {
76
+ source: 'platform',
77
+ description,
78
+ degradation: degradation ?? null,
79
+ overridable: opts?.overridable ?? true,
80
+ };
81
+ }
82
+ function enhanced(description, opts) {
83
+ return {
84
+ source: 'enhanced',
85
+ description,
86
+ degradation: null,
87
+ overridable: opts?.overridable ?? true,
88
+ };
89
+ }
90
+ function unsupported(description) {
91
+ return {
92
+ source: 'unsupported',
93
+ description,
94
+ degradation: null,
95
+ overridable: false,
96
+ };
97
+ }
98
+ // ---------------------------------------------------------------------------
99
+ // CAPABILITY MATRIX
100
+ // ---------------------------------------------------------------------------
101
+ const sendgridCapabilities = {
102
+ sendEmail: provider('Send email via SendGrid v3 API'),
103
+ batchSend: provider('Batch send via SendGrid personalizations', { overridable: true }),
104
+ providerTemplates: provider('SendGrid dynamic templates (Handlebars)', { overridable: true }),
105
+ localTemplates: platform('Render templates locally before sending via SendGrid'),
106
+ calendarInvites: platform('Attach iCalendar invites to SendGrid emails'),
107
+ calendarEventMgmt: platform('Manage calendar events locally, send via SendGrid'),
108
+ webhooksReceive: provider('SendGrid Event Webhook for delivery events', { overridable: true }),
109
+ webhookNormalization: enhanced('Normalize SendGrid webhook payloads to common format'),
110
+ deliveryTracking: provider('Track delivery via SendGrid Event Webhook', { overridable: true }),
111
+ inboundEmailParsing: enhanced('Parse inbound emails from SendGrid Inbound Parse'),
112
+ subscriptionMgmt: enhanced('Manage subscriber lists and preferences with SendGrid contacts'),
113
+ suppressionLists: provider('SendGrid global and group suppression management', { overridable: true }),
114
+ unsubscribeUrlGeneration: platform('Generate unsubscribe URLs for SendGrid emails'),
115
+ scheduledSend: provider('SendGrid scheduled send via send_at parameter', { limitations: '72hr max scheduling window', overridable: true }),
116
+ openClickTracking: provider('SendGrid open and click tracking via tracking settings', { overridable: true }),
117
+ emailContentParsing: platform('Parse and transform email content for SendGrid'),
118
+ emailHeaderMgmt: platform('Set custom headers on SendGrid emails'),
119
+ attachments: provider('File attachments via SendGrid API'),
120
+ advancedAttachmentProcessing: platform('Process and validate attachments before SendGrid send'),
121
+ dkimSpf: provider('SendGrid domain authentication (DKIM/SPF)', { overridable: true }),
122
+ linkBranding: provider('SendGrid branded link tracking domains', { overridable: true }),
123
+ retryResilience: platform('Automatic retry with backoff for SendGrid API calls'),
124
+ multiProviderFailover: platform('Failover from SendGrid to alternate providers'),
125
+ };
126
+ const mailgunCapabilities = {
127
+ sendEmail: provider('Send email via Mailgun API'),
128
+ batchSend: provider('Batch send via Mailgun recipient variables', { overridable: true }),
129
+ providerTemplates: provider('Mailgun stored templates', { overridable: true }),
130
+ localTemplates: platform('Render templates locally before sending via Mailgun'),
131
+ calendarInvites: platform('Attach iCalendar invites to Mailgun emails'),
132
+ calendarEventMgmt: platform('Manage calendar events locally, send via Mailgun'),
133
+ webhooksReceive: provider('Mailgun webhooks for delivery events', { overridable: true }),
134
+ webhookNormalization: enhanced('Normalize Mailgun webhook payloads to common format'),
135
+ deliveryTracking: provider('Track delivery via Mailgun webhooks', { overridable: true }),
136
+ inboundEmailParsing: enhanced('Parse inbound emails from Mailgun Routes'),
137
+ subscriptionMgmt: enhanced('Manage subscriber lists and preferences with Mailgun mailing lists'),
138
+ suppressionLists: provider('Mailgun bounce, complaint, and unsubscribe suppression', { overridable: true }),
139
+ unsubscribeUrlGeneration: platform('Generate unsubscribe URLs for Mailgun emails'),
140
+ scheduledSend: provider('Mailgun scheduled delivery via o:deliverytime', { limitations: '3-day max scheduling window', overridable: true }),
141
+ openClickTracking: provider('Mailgun open and click tracking', { overridable: true }),
142
+ emailContentParsing: platform('Parse and transform email content for Mailgun'),
143
+ emailHeaderMgmt: platform('Set custom headers on Mailgun emails'),
144
+ attachments: provider('File attachments via Mailgun API'),
145
+ advancedAttachmentProcessing: platform('Process and validate attachments before Mailgun send'),
146
+ dkimSpf: provider('Mailgun domain verification (DKIM/SPF)', { overridable: true }),
147
+ linkBranding: provider('Mailgun custom tracking domains', { overridable: true }),
148
+ retryResilience: platform('Automatic retry with backoff for Mailgun API calls'),
149
+ multiProviderFailover: platform('Failover from Mailgun to alternate providers'),
150
+ };
151
+ const postmarkCapabilities = {
152
+ sendEmail: provider('Send email via Postmark API'),
153
+ batchSend: provider('Batch send via Postmark batch API endpoint', { overridable: true }),
154
+ providerTemplates: provider('Postmark server-side templates', { overridable: true }),
155
+ localTemplates: platform('Render templates locally before sending via Postmark'),
156
+ calendarInvites: platform('Attach iCalendar invites to Postmark emails'),
157
+ calendarEventMgmt: platform('Manage calendar events locally, send via Postmark'),
158
+ webhooksReceive: provider('Postmark webhooks for delivery, bounce, and spam events', { overridable: true }),
159
+ webhookNormalization: enhanced('Normalize Postmark webhook payloads to common format'),
160
+ deliveryTracking: provider('Track delivery via Postmark webhooks and message streams', { overridable: true }),
161
+ inboundEmailParsing: enhanced('Parse inbound emails from Postmark Inbound'),
162
+ subscriptionMgmt: platform('Platform-managed subscription preferences (Postmark lacks native list management)', { strategy: 'platform-managed', description: 'Subscription management handled entirely by platform since Postmark has no native list management', docUrl: '/degradation/subscription-mgmt' }),
163
+ suppressionLists: provider('Postmark suppression stream management', { overridable: true }),
164
+ unsubscribeUrlGeneration: platform('Generate unsubscribe URLs for Postmark emails'),
165
+ scheduledSend: platform('Platform-managed scheduled send (Postmark has no native scheduling)', { strategy: 'platform-scheduled', description: 'Emails are queued locally and sent at the scheduled time', docUrl: '/degradation/scheduled-send' }),
166
+ openClickTracking: provider('Postmark open tracking (click tracking not supported)', { limitations: 'Open tracking only; no click tracking', overridable: true }),
167
+ emailContentParsing: platform('Parse and transform email content for Postmark'),
168
+ emailHeaderMgmt: platform('Set custom headers on Postmark emails'),
169
+ attachments: provider('File attachments via Postmark API'),
170
+ advancedAttachmentProcessing: platform('Process and validate attachments before Postmark send'),
171
+ dkimSpf: provider('Postmark domain authentication (DKIM/SPF)', { overridable: true }),
172
+ linkBranding: unsupported('Postmark does not support custom branded tracking domains'),
173
+ retryResilience: platform('Automatic retry with backoff for Postmark API calls'),
174
+ multiProviderFailover: platform('Failover from Postmark to alternate providers'),
175
+ };
176
+ const sesCapabilities = {
177
+ sendEmail: provider('Send email via AWS SES v2 API'),
178
+ batchSend: provider('Batch send via SES SendBulkEmail', { overridable: true }),
179
+ providerTemplates: provider('SES email templates', { overridable: true }),
180
+ localTemplates: platform('Render templates locally before sending via SES'),
181
+ calendarInvites: platform('Attach iCalendar invites to SES emails'),
182
+ calendarEventMgmt: platform('Manage calendar events locally, send via SES'),
183
+ webhooksReceive: provider('SES event notifications via SNS', { limitations: 'Requires SNS topic configuration', overridable: true }),
184
+ webhookNormalization: enhanced('Normalize SES/SNS notification payloads to common format'),
185
+ deliveryTracking: provider('Track delivery via SES SNS notifications', { limitations: 'Requires SNS topic configuration', overridable: true }),
186
+ inboundEmailParsing: enhanced('Parse inbound emails from SES receiving rules'),
187
+ subscriptionMgmt: platform('Platform-managed subscription preferences (SES has no native list management)', { strategy: 'platform-managed', description: 'Subscription management handled entirely by platform since SES has no native list management', docUrl: '/degradation/subscription-mgmt' }),
188
+ suppressionLists: provider('SES account-level suppression list', { overridable: true }),
189
+ unsubscribeUrlGeneration: platform('Generate unsubscribe URLs for SES emails'),
190
+ scheduledSend: platform('Platform-managed scheduled send (SES has no native scheduling)', { strategy: 'platform-scheduled', description: 'Emails are queued locally and sent at the scheduled time', docUrl: '/degradation/scheduled-send' }),
191
+ openClickTracking: unsupported('SES does not provide native open/click tracking'),
192
+ emailContentParsing: platform('Parse and transform email content for SES'),
193
+ emailHeaderMgmt: platform('Set custom headers on SES emails'),
194
+ attachments: provider('File attachments via SES raw email'),
195
+ advancedAttachmentProcessing: platform('Process and validate attachments before SES send'),
196
+ dkimSpf: provider('SES DKIM signing and domain verification', { overridable: true }),
197
+ linkBranding: unsupported('SES does not support custom branded tracking domains'),
198
+ retryResilience: platform('Automatic retry with backoff for SES API calls'),
199
+ multiProviderFailover: platform('Failover from SES to alternate providers'),
200
+ };
201
+ const smtpCapabilities = {
202
+ sendEmail: provider('Send email via SMTP transport'),
203
+ batchSend: platform('Sequential batch sending over SMTP (no native batch support)', { strategy: 'sequential-batch', description: 'Emails are sent sequentially over the SMTP connection', docUrl: '/degradation/batch-send' }),
204
+ providerTemplates: unsupported('SMTP does not support server-side templates'),
205
+ localTemplates: platform('Render templates locally before sending via SMTP'),
206
+ calendarInvites: platform('Attach iCalendar invites to SMTP emails'),
207
+ calendarEventMgmt: platform('Manage calendar events locally, send via SMTP'),
208
+ webhooksReceive: unsupported('SMTP does not provide webhook callbacks'),
209
+ webhookNormalization: unsupported('SMTP does not provide webhooks to normalize'),
210
+ deliveryTracking: unsupported('SMTP does not provide delivery tracking callbacks'),
211
+ inboundEmailParsing: unsupported('SMTP does not provide inbound email parsing'),
212
+ subscriptionMgmt: platform('Platform-managed subscription preferences (SMTP has no native list management)', { strategy: 'platform-managed', description: 'Subscription management handled entirely by platform since SMTP has no native capabilities', docUrl: '/degradation/subscription-mgmt' }),
213
+ suppressionLists: platform('Platform-managed suppression lists (SMTP has no native suppression)', { strategy: 'platform-managed', description: 'Suppression list maintained locally since SMTP has no provider-level suppression', docUrl: '/degradation/suppression-lists' }),
214
+ unsubscribeUrlGeneration: platform('Generate unsubscribe URLs for SMTP emails'),
215
+ scheduledSend: platform('Platform-managed scheduled send (SMTP has no native scheduling)', { strategy: 'platform-scheduled', description: 'Emails are queued locally and sent at the scheduled time', docUrl: '/degradation/scheduled-send' }),
216
+ openClickTracking: unsupported('SMTP does not support open/click tracking'),
217
+ emailContentParsing: platform('Parse and transform email content for SMTP'),
218
+ emailHeaderMgmt: platform('Set custom headers on SMTP emails'),
219
+ attachments: provider('File attachments via SMTP MIME'),
220
+ advancedAttachmentProcessing: platform('Process and validate attachments before SMTP send'),
221
+ dkimSpf: unsupported('SMTP DKIM/SPF requires manual server configuration, not programmatic'),
222
+ linkBranding: unsupported('SMTP does not support custom branded tracking domains'),
223
+ retryResilience: platform('Automatic retry with backoff for SMTP connections'),
224
+ multiProviderFailover: platform('Failover from SMTP to alternate providers'),
225
+ };
226
+ // ---------------------------------------------------------------------------
227
+ // Exported Matrix
228
+ // ---------------------------------------------------------------------------
229
+ export const CAPABILITY_MATRIX = {
230
+ version: '1.0.0',
231
+ lastUpdated: '2026-02-22',
232
+ providers: {
233
+ sendgrid: sendgridCapabilities,
234
+ mailgun: mailgunCapabilities,
235
+ postmark: postmarkCapabilities,
236
+ ses: sesCapabilities,
237
+ smtp: smtpCapabilities,
238
+ },
239
+ };
240
+ /**
241
+ * Returns a deep clone of the capability matrix to prevent mutation of the
242
+ * canonical data.
243
+ */
244
+ export function getCapabilityMatrix() {
245
+ return structuredClone(CAPABILITY_MATRIX);
246
+ }
247
+ //# sourceMappingURL=matrix.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matrix.js","sourceRoot":"","sources":["../../src/capabilities/matrix.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,95 @@
1
+ import type { CapabilityEntry } from '@bernierllc/email-sender';
2
+ import type { CapabilityResolverAdmin } from './resolver.js';
3
+ /**
4
+ * Minimal Redis client interface. Compatible with ioredis instances.
5
+ * Users provide their own Redis client so we don't bundle ioredis.
6
+ */
7
+ export interface RedisLike {
8
+ get(key: string): Promise<string | null>;
9
+ set(key: string, value: string, ...args: unknown[]): Promise<string | null>;
10
+ del(...keys: string[]): Promise<number>;
11
+ keys(pattern: string): Promise<string[]>;
12
+ pipeline(): RedisPipelineLike;
13
+ }
14
+ export interface RedisPipelineLike {
15
+ get(key: string): RedisPipelineLike;
16
+ set(key: string, value: string, ...args: unknown[]): RedisPipelineLike;
17
+ exec(): Promise<Array<[Error | null, unknown]>>;
18
+ }
19
+ export interface RedisCapabilityResolverOptions {
20
+ /** User-provided Redis client (ioredis instance or compatible). */
21
+ redis: RedisLike;
22
+ /** Key prefix. Default: 'ecap'. */
23
+ keyPrefix?: string;
24
+ /** Agnostic scoping namespace (e.g., org_id). */
25
+ namespace?: string;
26
+ /** Cache TTL in seconds. 0 = no expiry. Default: 0. */
27
+ ttl?: number;
28
+ /** Optional logger for diagnostics. */
29
+ logger?: {
30
+ warn: (...args: unknown[]) => void;
31
+ };
32
+ }
33
+ /**
34
+ * Redis-backed {@link CapabilityResolverAdmin}.
35
+ *
36
+ * Stores per-provider, per-feature capability overrides in Redis. When a key
37
+ * is not present in Redis the resolver falls back to the static
38
+ * {@link CAPABILITY_MATRIX}.
39
+ *
40
+ * Entries read from Redis are validated against `capabilityEntrySchema`; if
41
+ * validation fails the static matrix entry is returned instead.
42
+ */
43
+ export declare class RedisCapabilityResolver implements CapabilityResolverAdmin {
44
+ private readonly redis;
45
+ private readonly prefix;
46
+ private readonly namespace;
47
+ private readonly ttl;
48
+ private readonly logger;
49
+ constructor(options: RedisCapabilityResolverOptions);
50
+ /**
51
+ * Builds the Redis key for a specific provider + feature pair.
52
+ *
53
+ * Without namespace: `ecap:sendgrid:batchSend`
54
+ * With namespace `org_123`: `ecap:org_123:sendgrid:batchSend`
55
+ */
56
+ buildKey(provider: string, feature: string): string;
57
+ /**
58
+ * Builds the key pattern for listing all features of a provider.
59
+ */
60
+ private buildProviderPattern;
61
+ /**
62
+ * Extracts the feature name from a full Redis key.
63
+ */
64
+ private extractFeature;
65
+ getCapability(provider: string, feature: string): Promise<CapabilityEntry>;
66
+ getProviderCapabilities(provider: string): Promise<Record<string, CapabilityEntry>>;
67
+ setCapability(provider: string, feature: string, entry: CapabilityEntry): Promise<void>;
68
+ setProviderCapabilities(provider: string, capabilities: Record<string, CapabilityEntry>): Promise<void>;
69
+ /**
70
+ * Parses a JSON string from Redis and validates it against the schema.
71
+ * Falls back to the static matrix entry on any error.
72
+ */
73
+ private parseAndValidate;
74
+ /**
75
+ * Parses and validates silently, returning null on failure (used in batch operations).
76
+ */
77
+ private parseAndValidateQuiet;
78
+ /**
79
+ * Gets a capability entry from the static CAPABILITY_MATRIX.
80
+ * Throws CapabilityResolverError if provider or feature is unknown.
81
+ */
82
+ private getMatrixEntry;
83
+ /**
84
+ * Gets all capabilities for a provider from the static matrix.
85
+ * Returns an empty object for unknown providers (Redis may have entries for providers
86
+ * not in the static matrix).
87
+ */
88
+ private getMatrixProviderCaps;
89
+ /**
90
+ * Validates a capability entry against the Zod schema, throwing a
91
+ * {@link CapabilityResolverError} with code `INVALID_ENTRY` on failure.
92
+ */
93
+ private validateEntry;
94
+ }
95
+ //# sourceMappingURL=redis-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-resolver.d.ts","sourceRoot":"","sources":["../../src/capabilities/redis-resolver.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAIhE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAM7D;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5E,GAAG,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACzC,QAAQ,IAAI,iBAAiB,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,CAAC;IACpC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,iBAAiB,CAAC;IACvE,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;CACjD;AAED,MAAM,WAAW,8BAA8B;IAC7C,mEAAmE;IACnE,KAAK,EAAE,SAAS,CAAC;IACjB,mCAAmC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uCAAuC;IACvC,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;KAAE,CAAC;CACjD;AAMD;;;;;;;;;GASG;AACH,qBAAa,uBAAwB,YAAW,uBAAuB;IACrE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAY;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;IAC/C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqD;gBAEhE,OAAO,EAAE,8BAA8B;IAYnD;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAOnD;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAO5B;;OAEG;IACH,OAAO,CAAC,cAAc;IAShB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAmB1E,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAkDnF,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAavF,uBAAuB,CAC3B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,GAC5C,OAAO,CAAC,IAAI,CAAC;IAwBhB;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAsBxB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAY7B;;;OAGG;IACH,OAAO,CAAC,cAAc;IAoBtB;;;;OAIG;IACH,OAAO,CAAC,qBAAqB;IAK7B;;;OAGG;IACH,OAAO,CAAC,aAAa;CAUtB"}
@@ -0,0 +1,227 @@
1
+ /*
2
+ Copyright (c) 2025 Bernier LLC
3
+
4
+ This file is licensed to the client under a limited-use license.
5
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
6
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
+ */
8
+ import { capabilityEntrySchema } from '@bernierllc/email-sender';
9
+ import { CAPABILITY_MATRIX } from './matrix.js';
10
+ import { CapabilityResolverError } from './resolver.js';
11
+ // ---------------------------------------------------------------------------
12
+ // Implementation
13
+ // ---------------------------------------------------------------------------
14
+ /**
15
+ * Redis-backed {@link CapabilityResolverAdmin}.
16
+ *
17
+ * Stores per-provider, per-feature capability overrides in Redis. When a key
18
+ * is not present in Redis the resolver falls back to the static
19
+ * {@link CAPABILITY_MATRIX}.
20
+ *
21
+ * Entries read from Redis are validated against `capabilityEntrySchema`; if
22
+ * validation fails the static matrix entry is returned instead.
23
+ */
24
+ export class RedisCapabilityResolver {
25
+ constructor(options) {
26
+ this.redis = options.redis;
27
+ this.prefix = options.keyPrefix ?? 'ecap';
28
+ this.namespace = options.namespace;
29
+ this.ttl = options.ttl ?? 0;
30
+ this.logger = options.logger;
31
+ }
32
+ // -----------------------------------------------------------------------
33
+ // Key helpers
34
+ // -----------------------------------------------------------------------
35
+ /**
36
+ * Builds the Redis key for a specific provider + feature pair.
37
+ *
38
+ * Without namespace: `ecap:sendgrid:batchSend`
39
+ * With namespace `org_123`: `ecap:org_123:sendgrid:batchSend`
40
+ */
41
+ buildKey(provider, feature) {
42
+ if (this.namespace) {
43
+ return `${this.prefix}:${this.namespace}:${provider}:${feature}`;
44
+ }
45
+ return `${this.prefix}:${provider}:${feature}`;
46
+ }
47
+ /**
48
+ * Builds the key pattern for listing all features of a provider.
49
+ */
50
+ buildProviderPattern(provider) {
51
+ if (this.namespace) {
52
+ return `${this.prefix}:${this.namespace}:${provider}:*`;
53
+ }
54
+ return `${this.prefix}:${provider}:*`;
55
+ }
56
+ /**
57
+ * Extracts the feature name from a full Redis key.
58
+ */
59
+ extractFeature(key) {
60
+ const parts = key.split(':');
61
+ return parts[parts.length - 1];
62
+ }
63
+ // -----------------------------------------------------------------------
64
+ // Read operations
65
+ // -----------------------------------------------------------------------
66
+ async getCapability(provider, feature) {
67
+ const key = this.buildKey(provider, feature);
68
+ let raw;
69
+ try {
70
+ raw = await this.redis.get(key);
71
+ }
72
+ catch {
73
+ // Redis unavailable -- fall back to static matrix
74
+ return this.getMatrixEntry(provider, feature);
75
+ }
76
+ if (raw === null) {
77
+ // No override in Redis -- use static matrix
78
+ return this.getMatrixEntry(provider, feature);
79
+ }
80
+ return this.parseAndValidate(raw, provider, feature);
81
+ }
82
+ async getProviderCapabilities(provider) {
83
+ // Start with static matrix entries as base
84
+ const matrixCaps = this.getMatrixProviderCaps(provider);
85
+ // Overlay Redis overrides
86
+ let keys;
87
+ try {
88
+ keys = await this.redis.keys(this.buildProviderPattern(provider));
89
+ }
90
+ catch {
91
+ // Redis unavailable -- return matrix entries only
92
+ return { ...matrixCaps };
93
+ }
94
+ if (keys.length === 0) {
95
+ return { ...matrixCaps };
96
+ }
97
+ // Use pipeline for efficient batch fetch
98
+ const pipeline = this.redis.pipeline();
99
+ for (const key of keys) {
100
+ pipeline.get(key);
101
+ }
102
+ let results;
103
+ try {
104
+ results = await pipeline.exec();
105
+ }
106
+ catch {
107
+ return { ...matrixCaps };
108
+ }
109
+ const result = { ...matrixCaps };
110
+ for (let i = 0; i < keys.length; i++) {
111
+ const [err, raw] = results[i];
112
+ if (err || typeof raw !== 'string')
113
+ continue;
114
+ const feature = this.extractFeature(keys[i]);
115
+ const parsed = this.parseAndValidateQuiet(raw);
116
+ if (parsed) {
117
+ result[feature] = parsed;
118
+ }
119
+ }
120
+ return result;
121
+ }
122
+ // -----------------------------------------------------------------------
123
+ // Write operations
124
+ // -----------------------------------------------------------------------
125
+ async setCapability(provider, feature, entry) {
126
+ this.validateEntry(entry);
127
+ const key = this.buildKey(provider, feature);
128
+ const json = JSON.stringify(entry);
129
+ if (this.ttl > 0) {
130
+ await this.redis.set(key, json, 'EX', this.ttl);
131
+ }
132
+ else {
133
+ await this.redis.set(key, json);
134
+ }
135
+ }
136
+ async setProviderCapabilities(provider, capabilities) {
137
+ // Validate all entries before committing any
138
+ for (const [feature, entry] of Object.entries(capabilities)) {
139
+ this.validateEntry(entry, feature);
140
+ }
141
+ const pipeline = this.redis.pipeline();
142
+ for (const [feature, entry] of Object.entries(capabilities)) {
143
+ const key = this.buildKey(provider, feature);
144
+ const json = JSON.stringify(entry);
145
+ if (this.ttl > 0) {
146
+ pipeline.set(key, json, 'EX', this.ttl);
147
+ }
148
+ else {
149
+ pipeline.set(key, json);
150
+ }
151
+ }
152
+ await pipeline.exec();
153
+ }
154
+ // -----------------------------------------------------------------------
155
+ // Internal helpers
156
+ // -----------------------------------------------------------------------
157
+ /**
158
+ * Parses a JSON string from Redis and validates it against the schema.
159
+ * Falls back to the static matrix entry on any error.
160
+ */
161
+ parseAndValidate(raw, provider, feature) {
162
+ let parsed;
163
+ try {
164
+ parsed = JSON.parse(raw);
165
+ }
166
+ catch {
167
+ this.logger?.warn(`RedisCapabilityResolver: invalid JSON for key ${this.buildKey(provider, feature)}, falling back to matrix`);
168
+ return this.getMatrixEntry(provider, feature);
169
+ }
170
+ const result = capabilityEntrySchema.safeParse(parsed);
171
+ if (!result.success) {
172
+ this.logger?.warn(`RedisCapabilityResolver: schema validation failed for key ${this.buildKey(provider, feature)}, falling back to matrix`);
173
+ return this.getMatrixEntry(provider, feature);
174
+ }
175
+ return result.data;
176
+ }
177
+ /**
178
+ * Parses and validates silently, returning null on failure (used in batch operations).
179
+ */
180
+ parseAndValidateQuiet(raw) {
181
+ let parsed;
182
+ try {
183
+ parsed = JSON.parse(raw);
184
+ }
185
+ catch {
186
+ return null;
187
+ }
188
+ const result = capabilityEntrySchema.safeParse(parsed);
189
+ return result.success ? result.data : null;
190
+ }
191
+ /**
192
+ * Gets a capability entry from the static CAPABILITY_MATRIX.
193
+ * Throws CapabilityResolverError if provider or feature is unknown.
194
+ */
195
+ getMatrixEntry(provider, feature) {
196
+ const providerCaps = CAPABILITY_MATRIX.providers[provider];
197
+ if (!providerCaps) {
198
+ throw new CapabilityResolverError(`Unknown provider "${provider}"`, 'UNKNOWN_PROVIDER');
199
+ }
200
+ const entry = providerCaps[feature];
201
+ if (!entry) {
202
+ throw new CapabilityResolverError(`Unknown feature "${feature}" for provider "${provider}"`, 'UNKNOWN_FEATURE');
203
+ }
204
+ return entry;
205
+ }
206
+ /**
207
+ * Gets all capabilities for a provider from the static matrix.
208
+ * Returns an empty object for unknown providers (Redis may have entries for providers
209
+ * not in the static matrix).
210
+ */
211
+ getMatrixProviderCaps(provider) {
212
+ const providerCaps = CAPABILITY_MATRIX.providers[provider];
213
+ return providerCaps ? { ...providerCaps } : {};
214
+ }
215
+ /**
216
+ * Validates a capability entry against the Zod schema, throwing a
217
+ * {@link CapabilityResolverError} with code `INVALID_ENTRY` on failure.
218
+ */
219
+ validateEntry(entry, label) {
220
+ const result = capabilityEntrySchema.safeParse(entry);
221
+ if (!result.success) {
222
+ const prefix = label ? `Invalid entry for "${label}": ` : 'Invalid capability entry: ';
223
+ throw new CapabilityResolverError(prefix + result.error.issues.map((i) => i.message).join('; '), 'INVALID_ENTRY');
224
+ }
225
+ }
226
+ }
227
+ //# sourceMappingURL=redis-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis-resolver.js","sourceRoot":"","sources":["../../src/capabilities/redis-resolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import type { CapabilityResolver } from './resolver.js';
2
+ export interface ResolverFactoryOptions {
3
+ /** User-provided resolver instance. When set, the factory returns it directly. */
4
+ capabilityResolver?: CapabilityResolver;
5
+ /** Redis key namespace (forwarded to RedisCapabilityResolver). */
6
+ namespace?: string;
7
+ /** Redis TTL in seconds (forwarded to RedisCapabilityResolver). */
8
+ ttl?: number;
9
+ }
10
+ export interface ResolverFactoryLogger {
11
+ warn: (...args: unknown[]) => void;
12
+ info: (...args: unknown[]) => void;
13
+ }
14
+ /**
15
+ * Creates the best-available {@link CapabilityResolver} based on the runtime
16
+ * environment and the supplied options.
17
+ *
18
+ * Resolution order:
19
+ * 1. If `options.capabilityResolver` is provided, return it directly.
20
+ * 2. If the `EMAIL_MANAGER_REDIS_URL` environment variable is set **and**
21
+ * `ioredis` is installed, return a {@link SafeCapabilityResolver} wrapping
22
+ * a {@link RedisCapabilityResolver} with an {@link InMemoryCapabilityResolver}
23
+ * fallback.
24
+ * 3. Otherwise return an {@link InMemoryCapabilityResolver}.
25
+ *
26
+ * `ioredis` is loaded via dynamic `import()` so it remains an optional peer
27
+ * dependency -- projects that do not use Redis never need to install it.
28
+ */
29
+ export declare function createCapabilityResolver(options?: ResolverFactoryOptions, logger?: ResolverFactoryLogger): Promise<CapabilityResolver>;
30
+ //# sourceMappingURL=resolver-factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver-factory.d.ts","sourceRoot":"","sources":["../../src/capabilities/resolver-factory.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAOxD,MAAM,WAAW,sBAAsB;IACrC,kFAAkF;IAClF,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IACnC,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;CACpC;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,CAAC,EAAE,sBAAsB,EAChC,MAAM,CAAC,EAAE,qBAAqB,GAC7B,OAAO,CAAC,kBAAkB,CAAC,CA+C7B"}