@bernierllc/email-template-service 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Bernier LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,449 @@
1
+ # @bernierllc/email-template-service
2
+
3
+ Email template management service with provider synchronization, variable context validation, and base template support.
4
+
5
+ ## Features
6
+
7
+ - Complete template CRUD operations with validation
8
+ - Provider plugin system for email service integration (SendGrid, Mailgun, etc.)
9
+ - Bidirectional synchronization between local database and provider services
10
+ - Variable context registration and validation
11
+ - Base template support for consistent branding
12
+ - Conflict detection and resolution strategies
13
+ - Batch synchronization with error tracking
14
+ - TypeScript-first with strict type safety
15
+ - Extensible database adapter interface
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install @bernierllc/email-template-service
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ```typescript
26
+ import {
27
+ EmailTemplateService,
28
+ InMemoryDatabaseAdapter,
29
+ MockProviderPlugin
30
+ } from '@bernierllc/email-template-service';
31
+
32
+ // Initialize service
33
+ const service = new EmailTemplateService({
34
+ database: new InMemoryDatabaseAdapter(),
35
+ templateEngine: myTemplateEngine,
36
+ variableRegistry: myVariableRegistry,
37
+ providers: [new MockProviderPlugin()],
38
+ syncConfig: {
39
+ enabled: true,
40
+ providers: {
41
+ sendgrid: {
42
+ enabled: true,
43
+ strategy: 'auto',
44
+ direction: 'bidirectional'
45
+ }
46
+ },
47
+ defaultStrategy: 'auto',
48
+ conflictResolution: 'local_wins'
49
+ }
50
+ });
51
+
52
+ // Create a template
53
+ const template = await service.createTemplate({
54
+ name: 'Welcome Email',
55
+ subject: 'Welcome to our platform, {{firstName}}!',
56
+ htmlContent: '<html><body><h1>Welcome {{firstName}} {{lastName}}!</h1></body></html>',
57
+ textContent: 'Welcome {{firstName}} {{lastName}}!',
58
+ tags: ['welcome', 'onboarding'],
59
+ createdBy: 'admin'
60
+ });
61
+
62
+ // Sync to provider
63
+ await service.syncToProvider(template.id, 'sendgrid');
64
+
65
+ // List templates
66
+ const result = await service.listTemplates({
67
+ category: 'transactional',
68
+ page: 1,
69
+ limit: 10
70
+ });
71
+ ```
72
+
73
+ ## Core Concepts
74
+
75
+ ### Templates
76
+
77
+ Templates are email definitions with support for:
78
+ - HTML and text content
79
+ - Variable placeholders (e.g., `{{firstName}}`)
80
+ - Subject lines with variables
81
+ - Categorization and tagging
82
+ - Versioning
83
+ - Provider synchronization status
84
+
85
+ ### Provider Plugins
86
+
87
+ Provider plugins enable integration with email service providers:
88
+ - **SendGrid**: Template management and synchronization
89
+ - **Mailgun**: Template operations and tracking
90
+ - **Custom**: Implement `TemplateProviderPlugin` interface
91
+
92
+ Plugins support:
93
+ - Template CRUD operations in provider systems
94
+ - Bidirectional synchronization
95
+ - Feature capability detection
96
+ - Health monitoring and status reporting
97
+
98
+ ### Variable Contexts
99
+
100
+ Variable contexts define available variables for templates:
101
+ - Type-safe variable definitions
102
+ - Required vs. optional variables
103
+ - Context inheritance
104
+ - Template validation against contexts
105
+ - Auto-completion suggestions
106
+
107
+ ### Base Templates
108
+
109
+ Base templates provide consistent branding:
110
+ - Reusable header/footer HTML and CSS
111
+ - Theme management
112
+ - Branding elements (logos, colors, fonts)
113
+ - Dynamic content insertion
114
+ - Template building from base + content
115
+
116
+ ## API Reference
117
+
118
+ ### EmailTemplateService
119
+
120
+ #### Template Operations
121
+
122
+ ##### createTemplate(definition: TemplateDefinition): Promise<Template>
123
+
124
+ Create a new email template.
125
+
126
+ ```typescript
127
+ const template = await service.createTemplate({
128
+ name: 'Password Reset',
129
+ subject: 'Reset your password',
130
+ htmlContent: '<html><body>Click here to reset: {{resetLink}}</body></html>',
131
+ context: 'auth',
132
+ createdBy: 'admin'
133
+ });
134
+ ```
135
+
136
+ ##### updateTemplate(id: string, updates: TemplateUpdate): Promise<Template>
137
+
138
+ Update an existing template.
139
+
140
+ ```typescript
141
+ const updated = await service.updateTemplate(template.id, {
142
+ subject: 'Reset your password - Updated',
143
+ lastModifiedBy: 'admin'
144
+ });
145
+ ```
146
+
147
+ ##### deleteTemplate(id: string): Promise<void>
148
+
149
+ Delete a template and its provider copies.
150
+
151
+ ```typescript
152
+ await service.deleteTemplate(template.id);
153
+ ```
154
+
155
+ ##### getTemplate(id: string): Promise<Template | null>
156
+
157
+ Retrieve a template by ID.
158
+
159
+ ```typescript
160
+ const template = await service.getTemplate('tpl_123');
161
+ ```
162
+
163
+ ##### listTemplates(options: ListOptions): Promise<PaginatedResult<Template>>
164
+
165
+ List templates with filtering and pagination.
166
+
167
+ ```typescript
168
+ const result = await service.listTemplates({
169
+ category: 'marketing',
170
+ tags: ['promo'],
171
+ isActive: true,
172
+ search: 'holiday',
173
+ page: 1,
174
+ limit: 20,
175
+ sortBy: 'createdAt',
176
+ sortOrder: 'desc'
177
+ });
178
+ ```
179
+
180
+ #### Provider Synchronization
181
+
182
+ ##### syncToProvider(templateId: string, providerId: string): Promise<SyncResult>
183
+
184
+ Sync a template to a specific provider.
185
+
186
+ ```typescript
187
+ const result = await service.syncToProvider('tpl_123', 'sendgrid');
188
+ console.log(result.action); // 'created' or 'updated'
189
+ console.log(result.providerTemplateId);
190
+ ```
191
+
192
+ ##### syncFromProvider(providerTemplateId: string, providerId: string): Promise<Template>
193
+
194
+ Import a template from a provider.
195
+
196
+ ```typescript
197
+ const template = await service.syncFromProvider('sg_template_456', 'sendgrid');
198
+ ```
199
+
200
+ ##### syncAllTemplates(providerId: string): Promise<BatchSyncResult>
201
+
202
+ Sync all local templates to a provider.
203
+
204
+ ```typescript
205
+ const result = await service.syncAllTemplates('sendgrid');
206
+ console.log(`Synced: ${result.successful}/${result.total}`);
207
+ console.log(`Failed: ${result.failed}`);
208
+ ```
209
+
210
+ #### Base Template Operations
211
+
212
+ ##### createBaseTemplate(definition: BaseTemplateDefinition): Promise<BaseTemplate>
213
+
214
+ Create a reusable base template.
215
+
216
+ ```typescript
217
+ const baseTemplate = await service.createBaseTemplate({
218
+ name: 'Corporate Email Template',
219
+ headerHtml: '<header><img src="{{logo}}" /></header>',
220
+ footerHtml: '<footer>© 2025 Company Name</footer>',
221
+ theme: 'corporate'
222
+ });
223
+ ```
224
+
225
+ ##### buildTemplate(baseTemplateId: string, content: string, variables?: Record<string, unknown>): Promise<BuiltTemplate>
226
+
227
+ Build a complete template from base + content.
228
+
229
+ ```typescript
230
+ const built = await service.buildTemplate(
231
+ baseTemplate.id,
232
+ '<main><h1>{{title}}</h1><p>{{content}}</p></main>',
233
+ {
234
+ logo: 'https://example.com/logo.png',
235
+ title: 'Important Update',
236
+ content: 'This is the email body'
237
+ }
238
+ );
239
+
240
+ console.log(built.html); // Complete HTML with header, content, footer
241
+ console.log(built.text); // Plain text version
242
+ ```
243
+
244
+ #### Variable Context Management
245
+
246
+ ##### registerContext(name: string, context: VariableContext): Promise<void>
247
+
248
+ Register a variable context for templates.
249
+
250
+ ```typescript
251
+ await service.registerContext('user', {
252
+ description: 'User-related variables',
253
+ variables: {
254
+ firstName: { type: 'string', required: true, description: 'User first name' },
255
+ lastName: { type: 'string', required: true, description: 'User last name' },
256
+ email: { type: 'string', required: true, description: 'User email address' },
257
+ accountAge: { type: 'number', required: false, description: 'Days since signup' }
258
+ }
259
+ });
260
+ ```
261
+
262
+ ##### validateTemplate(templateId: string, contextOverride?: string): Promise<ValidationResult>
263
+
264
+ Validate a template against its variable context.
265
+
266
+ ```typescript
267
+ const validation = await service.validateTemplate('tpl_123');
268
+
269
+ if (!validation.isValid) {
270
+ console.log('Errors:', validation.errors);
271
+ console.log('Undefined variables:', validation.undefinedVariables);
272
+ }
273
+ ```
274
+
275
+ ##### getVariableSuggestions(context: string, prefix?: string): Promise<VariableSuggestion[]>
276
+
277
+ Get variable suggestions for auto-completion.
278
+
279
+ ```typescript
280
+ const suggestions = await service.getVariableSuggestions('user', 'first');
281
+ // Returns: [{ name: 'firstName', type: 'string', ... }]
282
+ ```
283
+
284
+ ## Database Adapters
285
+
286
+ The service supports any database through the `DatabaseAdapter` interface.
287
+
288
+ ### In-Memory Adapter (Testing/Development)
289
+
290
+ ```typescript
291
+ import { InMemoryDatabaseAdapter } from '@bernierllc/email-template-service';
292
+
293
+ const adapter = new InMemoryDatabaseAdapter();
294
+ ```
295
+
296
+ ### Custom Adapter
297
+
298
+ Implement the `DatabaseAdapter` interface for your database:
299
+
300
+ ```typescript
301
+ import { DatabaseAdapter, Template, BaseTemplate } from '@bernierllc/email-template-service';
302
+
303
+ class PostgresDatabaseAdapter implements DatabaseAdapter {
304
+ async createTemplate(template: Template): Promise<Template> {
305
+ // Insert into PostgreSQL
306
+ }
307
+
308
+ async updateTemplate(id: string, updates: Partial<Template>): Promise<Template> {
309
+ // Update PostgreSQL row
310
+ }
311
+
312
+ // Implement other required methods...
313
+ }
314
+ ```
315
+
316
+ ## Provider Plugins
317
+
318
+ ### Creating a Provider Plugin
319
+
320
+ ```typescript
321
+ import {
322
+ BaseProviderPlugin,
323
+ Template,
324
+ ProviderTemplate,
325
+ SyncResult
326
+ } from '@bernierllc/email-template-service';
327
+
328
+ class SendGridTemplatePlugin extends BaseProviderPlugin {
329
+ readonly name = 'SendGrid Template Plugin';
330
+ readonly version = '1.0.0';
331
+ readonly providerId = 'sendgrid';
332
+ readonly capabilities = [
333
+ {
334
+ feature: 'template-versioning',
335
+ description: 'Supports template versions',
336
+ methods: ['createTemplate', 'updateTemplate']
337
+ }
338
+ ];
339
+
340
+ async initialize(config: PluginConfig): Promise<void> {
341
+ await super.initialize(config);
342
+ this.client = new SendGridClient(config.apiKey);
343
+ }
344
+
345
+ async createTemplate(template: Template): Promise<string> {
346
+ this.ensureInitialized();
347
+ const result = await this.client.createTemplate({
348
+ name: template.name,
349
+ generation: 'dynamic',
350
+ subject: template.subject,
351
+ html_content: template.htmlContent
352
+ });
353
+ return result.id;
354
+ }
355
+
356
+ // Implement other required methods...
357
+ }
358
+ ```
359
+
360
+ ## Configuration
361
+
362
+ ### Sync Configuration
363
+
364
+ ```typescript
365
+ {
366
+ enabled: true, // Enable synchronization
367
+ providers: {
368
+ sendgrid: {
369
+ enabled: true,
370
+ strategy: 'auto', // 'auto', 'manual', or 'scheduled'
371
+ direction: 'bidirectional', // 'bidirectional', 'push_only', 'pull_only'
372
+ schedule: '0 */6 * * *', // Cron expression for scheduled sync
373
+ batchSize: 50 // Batch size for bulk operations
374
+ }
375
+ },
376
+ defaultStrategy: 'manual', // Default sync strategy
377
+ conflictResolution: 'local_wins' // 'local_wins', 'remote_wins', 'timestamp', 'manual'
378
+ }
379
+ ```
380
+
381
+ ## Error Handling
382
+
383
+ The service provides specific error types:
384
+
385
+ ```typescript
386
+ import {
387
+ TemplateNotFoundError,
388
+ TemplateValidationError,
389
+ ProviderNotFoundError,
390
+ SyncError,
391
+ SyncConflictError
392
+ } from '@bernierllc/email-template-service';
393
+
394
+ try {
395
+ await service.updateTemplate('invalid-id', { name: 'New Name' });
396
+ } catch (error) {
397
+ if (error instanceof TemplateNotFoundError) {
398
+ console.error('Template not found');
399
+ } else if (error instanceof TemplateValidationError) {
400
+ console.error('Validation errors:', error.errors);
401
+ } else if (error instanceof SyncError) {
402
+ console.error('Sync failed:', error.message);
403
+ }
404
+ }
405
+ ```
406
+
407
+ ## Testing
408
+
409
+ The package includes comprehensive tests using real data:
410
+
411
+ ```bash
412
+ # Run tests
413
+ npm test
414
+
415
+ # Run tests with coverage
416
+ npm run test:coverage
417
+
418
+ # Run tests once (CI)
419
+ npm run test:run
420
+ ```
421
+
422
+ ## Dependencies
423
+
424
+ ### Required
425
+ - `@bernierllc/template-variable-registry` - Variable context management
426
+ - `@bernierllc/email-template-engine` - Template processing
427
+ - `@bernierllc/email-sender` - Email sending abstraction
428
+ - `@bernierllc/logger` - Service logging
429
+
430
+ ### Optional
431
+ - `@bernierllc/cache-manager` - Template caching
432
+ - `@bernierllc/retry-policy` - Provider operation retries
433
+
434
+ ## Integration Status
435
+
436
+ - **Logger**: Integrated - All service operations and provider sync events logged
437
+ - **Docs-Suite**: Ready - Complete API documentation with examples
438
+ - **NeverHub**: Required - Template events, sync status, and provider health monitoring
439
+
440
+ ## License
441
+
442
+ Copyright (c) 2025 Bernier LLC. All rights reserved.
443
+
444
+ ## See Also
445
+
446
+ - [@bernierllc/email-template-engine](../email-template-engine) - Template processing and rendering
447
+ - [@bernierllc/template-variable-registry](../template-variable-registry) - Variable context management
448
+ - [@bernierllc/email-sender](../email-sender) - Email sending service
449
+ - [@bernierllc/email-sendgrid-plugin](../email-sendgrid-plugin) - SendGrid provider plugin
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=email-template-service.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email-template-service.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/email-template-service.test.ts"],"names":[],"mappings":""}