@bernierllc/email-sendgrid-plugin 1.8.1 → 1.8.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.
@@ -1,129 +0,0 @@
1
- "use strict";
2
- /*
3
- Copyright (c) 2025 Bernier LLC
4
-
5
- This file is licensed to the client under a limited-use license.
6
- The client may use and modify this code *only within the scope of the project it was delivered for*.
7
- Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
8
- */
9
- Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.SenderManager = void 0;
11
- const errors_1 = require("../api/errors");
12
- class SenderManager {
13
- client;
14
- converter;
15
- logger;
16
- sendersCache;
17
- cacheTimestamp;
18
- cacheTTL = 300000; // 5 minutes
19
- constructor(client, converter, logger) {
20
- this.client = client;
21
- this.converter = converter;
22
- this.logger = logger;
23
- }
24
- async getVerifiedSenders(useCache = true) {
25
- // Return cached data if valid
26
- if (useCache &&
27
- this.sendersCache &&
28
- this.cacheTimestamp &&
29
- Date.now() - this.cacheTimestamp < this.cacheTTL) {
30
- this.logger?.debug('Returning cached verified senders');
31
- return this.sendersCache;
32
- }
33
- const response = await this.client.request('/verified_senders', {
34
- method: 'GET'
35
- });
36
- if (!this.client.isSuccessResponse(response)) {
37
- throw new errors_1.SendGridApiError('Failed to fetch verified senders', response.statusCode, response.body);
38
- }
39
- const data = this.client.parseResponseBody(response);
40
- const senders = data.results.map(sender => this.converter.convertToVerifiedSender(sender));
41
- // Update cache
42
- this.sendersCache = senders;
43
- this.cacheTimestamp = Date.now();
44
- this.logger?.info(`Fetched ${senders.length} verified senders from SendGrid`);
45
- return senders;
46
- }
47
- async validateSender(email) {
48
- const verifiedSenders = await this.getVerifiedSenders();
49
- const emailDomain = this.converter.extractDomain(email);
50
- // Check for exact match
51
- const exactMatch = verifiedSenders.find(sender => sender.email.toLowerCase() === email.toLowerCase() && sender.isVerified);
52
- if (exactMatch) {
53
- return {
54
- isValid: true,
55
- verifiedSender: exactMatch,
56
- domain: emailDomain,
57
- domainMatches: [exactMatch],
58
- errors: [],
59
- suggestions: []
60
- };
61
- }
62
- // Check for domain matches
63
- const domainMatches = verifiedSenders.filter(sender => {
64
- const senderDomain = this.converter.extractDomain(sender.email);
65
- return this.converter.isDomainMatch(emailDomain, senderDomain) && sender.isVerified;
66
- });
67
- if (domainMatches.length > 0) {
68
- return {
69
- isValid: true,
70
- domain: emailDomain,
71
- domainMatches,
72
- errors: [],
73
- suggestions: domainMatches.map(sender => sender.email)
74
- };
75
- }
76
- // No matches found
77
- return {
78
- isValid: false,
79
- domain: emailDomain,
80
- domainMatches: [],
81
- errors: [`No verified sender found for email ${email} or domain ${emailDomain}`],
82
- suggestions: this.getSenderSuggestions(email, verifiedSenders)
83
- };
84
- }
85
- async selectBestSender(fromEmail) {
86
- const validation = await this.validateSender(fromEmail);
87
- if (!validation.isValid || validation.domainMatches.length === 0) {
88
- return null;
89
- }
90
- // Prefer exact match
91
- if (validation.verifiedSender) {
92
- return validation.verifiedSender;
93
- }
94
- // Fall back to best domain match
95
- // Priority: most recently verified, then alphabetical
96
- return validation.domainMatches.sort((a, b) => {
97
- if (a.lastVerifiedAt && b.lastVerifiedAt) {
98
- return b.lastVerifiedAt.getTime() - a.lastVerifiedAt.getTime();
99
- }
100
- if (a.lastVerifiedAt && !b.lastVerifiedAt)
101
- return -1;
102
- if (!a.lastVerifiedAt && b.lastVerifiedAt)
103
- return 1;
104
- return a.email.localeCompare(b.email);
105
- })[0];
106
- }
107
- clearCache() {
108
- this.sendersCache = undefined;
109
- this.cacheTimestamp = undefined;
110
- this.logger?.debug('Cleared verified senders cache');
111
- }
112
- getSenderSuggestions(email, senders) {
113
- const emailDomain = this.converter.extractDomain(email);
114
- // Find senders with similar domains
115
- const suggestions = [];
116
- for (const sender of senders) {
117
- if (!sender.isVerified)
118
- continue;
119
- const senderDomain = this.converter.extractDomain(sender.email);
120
- // Check for partial domain matches
121
- if (emailDomain.includes(senderDomain) ||
122
- senderDomain.includes(emailDomain)) {
123
- suggestions.push(sender.email);
124
- }
125
- }
126
- return suggestions.slice(0, 5); // Limit to 5 suggestions
127
- }
128
- }
129
- exports.SenderManager = SenderManager;
@@ -1,60 +0,0 @@
1
- import { SendGridPluginConfig } from '../types/config-types';
2
- import { PluginConfig, ProviderCapability, Template, ProviderTemplate, TemplateVersion, SyncResult, ValidationResult, DateRange, TemplateAnalytics, FeatureConfig } from '../types/plugin-types';
3
- import { VerifiedSender, SenderValidationResult } from '../types/sendgrid-types';
4
- export interface TemplateProviderPlugin {
5
- readonly name: string;
6
- readonly version: string;
7
- readonly providerId: string;
8
- initialize(config: PluginConfig): Promise<void>;
9
- destroy(): Promise<void>;
10
- createTemplate(template: Template): Promise<string>;
11
- updateTemplate(providerTemplateId: string, template: Template): Promise<void>;
12
- deleteTemplate(providerTemplateId: string): Promise<void>;
13
- getTemplate(providerTemplateId: string): Promise<ProviderTemplate>;
14
- listTemplates(): Promise<ProviderTemplate[]>;
15
- syncToProvider(template: Template): Promise<SyncResult>;
16
- syncFromProvider(providerTemplateId: string): Promise<Template>;
17
- getCapabilities(): ProviderCapability[];
18
- supportsFeature(feature: string): boolean;
19
- getFeatureConfig(feature: string): FeatureConfig | null;
20
- }
21
- export declare class SendGridTemplatePlugin implements TemplateProviderPlugin {
22
- readonly name = "sendgrid-templates";
23
- readonly version = "1.0.0";
24
- readonly providerId = "sendgrid";
25
- private client;
26
- private rateLimiter?;
27
- private ownershipValidator;
28
- private prefixEnforcer;
29
- private templateConverter;
30
- private senderConverter;
31
- private templateManager;
32
- private senderManager;
33
- private syncManager;
34
- private readonly config;
35
- private readonly logger?;
36
- private initialized;
37
- constructor(config: SendGridPluginConfig);
38
- initialize(__pluginConfig: PluginConfig): Promise<void>;
39
- destroy(): Promise<void>;
40
- createTemplate(template: Template): Promise<string>;
41
- updateTemplate(providerTemplateId: string, template: Template): Promise<void>;
42
- deleteTemplate(providerTemplateId: string): Promise<void>;
43
- getTemplate(providerTemplateId: string): Promise<ProviderTemplate>;
44
- listTemplates(): Promise<ProviderTemplate[]>;
45
- syncToProvider(template: Template): Promise<SyncResult>;
46
- syncFromProvider(providerTemplateId: string): Promise<Template>;
47
- getVerifiedSenders(): Promise<VerifiedSender[]>;
48
- validateSender(email: string): Promise<SenderValidationResult>;
49
- selectBestSender(email: string): Promise<VerifiedSender | null>;
50
- createTemplateVersion(templateId: string, version: TemplateVersion): Promise<string>;
51
- activateTemplateVersion(templateId: string, versionId: string): Promise<void>;
52
- getTemplateAnalytics(__templateId: string, __dateRange: DateRange): Promise<TemplateAnalytics>;
53
- batchSyncTemplates(templates: Template[]): Promise<SyncResult[]>;
54
- getCapabilities(): ProviderCapability[];
55
- supportsFeature(feature: string): boolean;
56
- getFeatureConfig(feature: string): FeatureConfig | null;
57
- validateRoute(__route: unknown): ValidationResult;
58
- private loadExistingTemplates;
59
- private ensureInitialized;
60
- }
@@ -1,215 +0,0 @@
1
- "use strict";
2
- /*
3
- Copyright (c) 2025 Bernier LLC
4
-
5
- This file is licensed to the client under a limited-use license.
6
- The client may use and modify this code *only within the scope of the project it was delivered for*.
7
- Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
8
- */
9
- Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.SendGridTemplatePlugin = void 0;
11
- const sendgrid_client_1 = require("../api/sendgrid-client");
12
- const rate_limiter_1 = require("../api/rate-limiter");
13
- const ownership_validator_1 = require("../safety/ownership-validator");
14
- const prefix_enforcer_1 = require("../safety/prefix-enforcer");
15
- const template_converter_1 = require("../converters/template-converter");
16
- const sender_converter_1 = require("../converters/sender-converter");
17
- const template_manager_1 = require("./template-manager");
18
- const sender_manager_1 = require("./sender-manager");
19
- const sync_manager_1 = require("./sync-manager");
20
- class SendGridTemplatePlugin {
21
- name = 'sendgrid-templates';
22
- version = '1.0.0';
23
- providerId = 'sendgrid';
24
- client;
25
- rateLimiter;
26
- ownershipValidator;
27
- prefixEnforcer;
28
- templateConverter;
29
- senderConverter;
30
- templateManager;
31
- senderManager;
32
- syncManager;
33
- config;
34
- logger;
35
- initialized = false;
36
- constructor(config) {
37
- this.config = config;
38
- }
39
- async initialize(__pluginConfig) {
40
- if (this.initialized) {
41
- return;
42
- }
43
- // Initialize rate limiter if configured
44
- if (this.config.rateLimits) {
45
- this.rateLimiter = new rate_limiter_1.SimpleRateLimiter({
46
- maxRequestsPerSecond: this.config.rateLimits.apiCallsPerSecond,
47
- maxRequestsPerMinute: this.config.rateLimits.templatesPerMinute
48
- });
49
- }
50
- // Initialize SendGrid client
51
- this.client = new sendgrid_client_1.SendGridClient({
52
- apiKey: this.config.apiKey,
53
- rateLimiter: this.rateLimiter,
54
- logger: this.logger
55
- });
56
- // Initialize safety components
57
- this.ownershipValidator = new ownership_validator_1.OwnershipValidator(this.config.safetyMode, this.logger);
58
- this.prefixEnforcer = new prefix_enforcer_1.PrefixEnforcer(this.config.templatePrefix);
59
- // Initialize converters
60
- this.templateConverter = new template_converter_1.TemplateConverter(this.prefixEnforcer);
61
- this.senderConverter = new sender_converter_1.SenderConverter();
62
- // Initialize managers
63
- this.templateManager = new template_manager_1.TemplateManager(this.client, this.ownershipValidator, this.prefixEnforcer, this.templateConverter, this.logger);
64
- this.senderManager = new sender_manager_1.SenderManager(this.client, this.senderConverter, this.logger);
65
- this.syncManager = new sync_manager_1.SyncManager(this.templateManager, this.templateConverter, this.client, this.logger);
66
- // Load existing templates
67
- await this.loadExistingTemplates();
68
- this.initialized = true;
69
- this.logger?.info('SendGrid plugin initialized successfully');
70
- }
71
- async destroy() {
72
- if (!this.initialized) {
73
- return;
74
- }
75
- this.ownershipValidator.clearRegistrations();
76
- this.senderManager.clearCache();
77
- this.initialized = false;
78
- this.logger?.info('SendGrid plugin destroyed');
79
- }
80
- // Template operations
81
- async createTemplate(template) {
82
- this.ensureInitialized();
83
- return this.templateManager.createTemplate(template);
84
- }
85
- async updateTemplate(providerTemplateId, template) {
86
- this.ensureInitialized();
87
- return this.templateManager.updateTemplate(providerTemplateId, template);
88
- }
89
- async deleteTemplate(providerTemplateId) {
90
- this.ensureInitialized();
91
- return this.templateManager.deleteTemplate(providerTemplateId);
92
- }
93
- async getTemplate(providerTemplateId) {
94
- this.ensureInitialized();
95
- return this.templateManager.getTemplate(providerTemplateId);
96
- }
97
- async listTemplates() {
98
- this.ensureInitialized();
99
- return this.templateManager.listTemplates();
100
- }
101
- // Synchronization
102
- async syncToProvider(template) {
103
- this.ensureInitialized();
104
- return this.syncManager.syncToProvider(template);
105
- }
106
- async syncFromProvider(providerTemplateId) {
107
- this.ensureInitialized();
108
- return this.syncManager.syncFromProvider(providerTemplateId);
109
- }
110
- // SendGrid-specific features
111
- async getVerifiedSenders() {
112
- this.ensureInitialized();
113
- return this.senderManager.getVerifiedSenders();
114
- }
115
- async validateSender(email) {
116
- this.ensureInitialized();
117
- return this.senderManager.validateSender(email);
118
- }
119
- async selectBestSender(email) {
120
- this.ensureInitialized();
121
- return this.senderManager.selectBestSender(email);
122
- }
123
- async createTemplateVersion(templateId, version) {
124
- this.ensureInitialized();
125
- return this.templateManager.createTemplateVersion(templateId, version);
126
- }
127
- async activateTemplateVersion(templateId, versionId) {
128
- this.ensureInitialized();
129
- return this.templateManager.activateTemplateVersion(templateId, versionId);
130
- }
131
- async getTemplateAnalytics(__templateId, __dateRange) {
132
- this.ensureInitialized();
133
- // TODO: Implement analytics fetching from SendGrid
134
- throw new Error('Template analytics not yet implemented');
135
- }
136
- async batchSyncTemplates(templates) {
137
- this.ensureInitialized();
138
- return this.syncManager.batchSync(templates);
139
- }
140
- // Capabilities
141
- getCapabilities() {
142
- return [
143
- {
144
- feature: 'template-management',
145
- description: 'Full CRUD operations for SendGrid templates',
146
- methods: ['createTemplate', 'updateTemplate', 'deleteTemplate', 'getTemplate', 'listTemplates'],
147
- config: {
148
- supportsVersioning: true,
149
- supportsActivation: true,
150
- maxNameLength: 100,
151
- templatePrefix: this.config.templatePrefix
152
- }
153
- },
154
- {
155
- feature: 'sender-verification',
156
- description: 'SendGrid verified sender management',
157
- methods: ['getVerifiedSenders', 'validateSender', 'selectBestSender'],
158
- config: {
159
- supportsDomainMatching: true,
160
- supportsAutoSelection: true
161
- }
162
- },
163
- {
164
- feature: 'template-analytics',
165
- description: 'SendGrid template usage and performance analytics',
166
- methods: ['getTemplateAnalytics'],
167
- config: {
168
- supportsRealTime: false,
169
- historicalDataDays: 30,
170
- metricsAvailable: ['opens', 'clicks', 'bounces', 'deliveries']
171
- }
172
- },
173
- {
174
- feature: 'batch-operations',
175
- description: 'Efficient bulk template operations',
176
- methods: ['batchSyncTemplates'],
177
- config: {
178
- maxBatchSize: 50,
179
- supportsTransactions: false
180
- }
181
- }
182
- ];
183
- }
184
- supportsFeature(feature) {
185
- return this.getCapabilities().some(cap => cap.feature === feature);
186
- }
187
- getFeatureConfig(feature) {
188
- const capability = this.getCapabilities().find(cap => cap.feature === feature);
189
- return capability?.config || null;
190
- }
191
- validateRoute(__route) {
192
- // Placeholder validation
193
- return {
194
- isValid: true,
195
- errors: []
196
- };
197
- }
198
- // Private methods
199
- async loadExistingTemplates() {
200
- try {
201
- const templates = await this.templateManager.listTemplates();
202
- this.logger?.info(`Loaded ${templates.length} existing SendGrid templates`);
203
- }
204
- catch (error) {
205
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
206
- this.logger?.error(`Failed to load existing templates: ${errorMessage}`);
207
- }
208
- }
209
- ensureInitialized() {
210
- if (!this.initialized) {
211
- throw new Error('SendGrid plugin not initialized. Call initialize() first.');
212
- }
213
- }
214
- }
215
- exports.SendGridTemplatePlugin = SendGridTemplatePlugin;
@@ -1,19 +0,0 @@
1
- import { TemplateManager } from './template-manager';
2
- import { TemplateConverter } from '../converters/template-converter';
3
- import { SendGridClient } from '../api/sendgrid-client';
4
- import { Template, SyncResult } from '../types/plugin-types';
5
- export declare class SyncManager {
6
- private readonly templateManager;
7
- private readonly converter;
8
- private readonly client;
9
- private readonly logger?;
10
- constructor(templateManager: TemplateManager, converter: TemplateConverter, client: SendGridClient, logger?: {
11
- info: (message: string) => void;
12
- error: (message: string) => void;
13
- } | undefined);
14
- syncToProvider(template: Template): Promise<SyncResult>;
15
- syncFromProvider(providerTemplateId: string): Promise<Template>;
16
- batchSync(templates: Template[]): Promise<SyncResult[]>;
17
- private generateLocalId;
18
- private sleep;
19
- }
@@ -1,98 +0,0 @@
1
- "use strict";
2
- /*
3
- Copyright (c) 2025 Bernier LLC
4
-
5
- This file is licensed to the client under a limited-use license.
6
- The client may use and modify this code *only within the scope of the project it was delivered for*.
7
- Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
8
- */
9
- Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.SyncManager = void 0;
11
- class SyncManager {
12
- templateManager;
13
- converter;
14
- client;
15
- logger;
16
- constructor(templateManager, converter, client, logger) {
17
- this.templateManager = templateManager;
18
- this.converter = converter;
19
- this.client = client;
20
- this.logger = logger;
21
- }
22
- async syncToProvider(template) {
23
- try {
24
- const existingProviderId = template.providerTemplateIds?.sendgrid;
25
- if (existingProviderId) {
26
- // Update existing template
27
- await this.templateManager.updateTemplate(existingProviderId, template);
28
- return {
29
- success: true,
30
- action: 'updated',
31
- providerTemplateId: existingProviderId,
32
- localTemplateId: template.id,
33
- metadata: {
34
- newVersion: template.version
35
- }
36
- };
37
- }
38
- else {
39
- // Create new template
40
- const newProviderId = await this.templateManager.createTemplate(template);
41
- return {
42
- success: true,
43
- action: 'created',
44
- providerTemplateId: newProviderId,
45
- localTemplateId: template.id,
46
- metadata: {
47
- version: template.version
48
- }
49
- };
50
- }
51
- }
52
- catch (error) {
53
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
54
- this.logger?.error(`Template sync failed: ${errorMessage}`);
55
- return {
56
- success: false,
57
- action: 'failed',
58
- localTemplateId: template.id,
59
- error: errorMessage,
60
- metadata: {
61
- errorType: error instanceof Error ? error.constructor.name : 'UnknownError',
62
- timestamp: new Date()
63
- }
64
- };
65
- }
66
- }
67
- async syncFromProvider(providerTemplateId) {
68
- const response = await this.client.request(`/templates/${providerTemplateId}`, {
69
- method: 'GET'
70
- });
71
- if (!this.client.isSuccessResponse(response)) {
72
- throw new Error(`Failed to fetch template from SendGrid: ${response.statusCode}`);
73
- }
74
- const templateData = this.client.parseResponseBody(response);
75
- const localId = this.generateLocalId();
76
- return this.converter.convertToLocalTemplate(templateData, localId);
77
- }
78
- async batchSync(templates) {
79
- this.logger?.info(`Starting batch sync of ${templates.length} templates`);
80
- const results = [];
81
- for (const template of templates) {
82
- const result = await this.syncToProvider(template);
83
- results.push(result);
84
- // Add delay to respect rate limits
85
- await this.sleep(100);
86
- }
87
- const successful = results.filter(r => r.success).length;
88
- this.logger?.info(`Batch sync completed: ${successful}/${templates.length} successful`);
89
- return results;
90
- }
91
- generateLocalId() {
92
- return `local_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
93
- }
94
- sleep(ms) {
95
- return new Promise(resolve => setTimeout(resolve, ms));
96
- }
97
- }
98
- exports.SyncManager = SyncManager;
@@ -1,25 +0,0 @@
1
- import { SendGridClient } from '../api/sendgrid-client';
2
- import { OwnershipValidator } from '../safety/ownership-validator';
3
- import { PrefixEnforcer } from '../safety/prefix-enforcer';
4
- import { TemplateConverter } from '../converters/template-converter';
5
- import { Template, ProviderTemplate, TemplateVersion } from '../types/plugin-types';
6
- export declare class TemplateManager {
7
- private readonly client;
8
- private readonly ownershipValidator;
9
- private readonly prefixEnforcer;
10
- private readonly converter;
11
- private readonly logger?;
12
- constructor(client: SendGridClient, ownershipValidator: OwnershipValidator, prefixEnforcer: PrefixEnforcer, converter: TemplateConverter, logger?: {
13
- info: (message: string) => void;
14
- error: (message: string) => void;
15
- } | undefined);
16
- createTemplate(template: Template): Promise<string>;
17
- updateTemplate(providerTemplateId: string, template: Template): Promise<void>;
18
- deleteTemplate(providerTemplateId: string): Promise<void>;
19
- getTemplate(providerTemplateId: string): Promise<ProviderTemplate>;
20
- listTemplates(): Promise<ProviderTemplate[]>;
21
- createTemplateVersion(templateId: string, version: TemplateVersion): Promise<string>;
22
- activateTemplateVersion(templateId: string, versionId: string): Promise<void>;
23
- private hasConflicts;
24
- private detectConflicts;
25
- }