@bernierllc/email-sendgrid-plugin 1.4.5 → 1.7.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.
Files changed (43) hide show
  1. package/dist/api/errors.js +5 -0
  2. package/dist/api/rate-limiter.js +3 -1
  3. package/dist/api/sendgrid-client.js +4 -1
  4. package/dist/converters/template-converter.js +1 -0
  5. package/dist/errors.d.ts +11 -0
  6. package/dist/errors.js +24 -0
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.js +4 -1
  9. package/dist/plugin/sender-manager.js +6 -1
  10. package/dist/plugin/sendgrid-plugin.js +15 -4
  11. package/dist/plugin/sync-manager.js +4 -0
  12. package/dist/plugin/template-manager.js +5 -0
  13. package/dist/safety/ownership-validator.js +3 -1
  14. package/dist/safety/prefix-enforcer.js +1 -0
  15. package/package.json +8 -4
  16. package/.eslintrc.js +0 -27
  17. package/CHANGELOG.md +0 -117
  18. package/__tests__/api/rate-limiter.test.ts +0 -125
  19. package/__tests__/converters/attachment-header-mapping.test.ts +0 -337
  20. package/__tests__/converters/sender-converter.test.ts +0 -148
  21. package/__tests__/converters/template-converter.test.ts +0 -230
  22. package/__tests__/plugin/sender-manager.test.ts +0 -135
  23. package/__tests__/plugin/sendgrid-plugin.test.ts +0 -195
  24. package/__tests__/plugin/template-manager.test.ts +0 -110
  25. package/__tests__/safety/ownership-validator.test.ts +0 -130
  26. package/__tests__/safety/prefix-enforcer.test.ts +0 -136
  27. package/jest.config.js +0 -26
  28. package/src/api/errors.ts +0 -49
  29. package/src/api/rate-limiter.ts +0 -79
  30. package/src/api/sendgrid-client.ts +0 -86
  31. package/src/converters/sender-converter.ts +0 -138
  32. package/src/converters/template-converter.ts +0 -97
  33. package/src/index.ts +0 -61
  34. package/src/plugin/sender-manager.ts +0 -166
  35. package/src/plugin/sendgrid-plugin.ts +0 -316
  36. package/src/plugin/sync-manager.ts +0 -115
  37. package/src/plugin/template-manager.ts +0 -240
  38. package/src/safety/ownership-validator.ts +0 -55
  39. package/src/safety/prefix-enforcer.ts +0 -37
  40. package/src/types/config-types.ts +0 -34
  41. package/src/types/plugin-types.ts +0 -97
  42. package/src/types/sendgrid-types.ts +0 -98
  43. package/tsconfig.json +0 -18
@@ -1,115 +0,0 @@
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
- import { TemplateManager } from './template-manager';
10
- import { TemplateConverter } from '../converters/template-converter';
11
- import { SendGridClient } from '../api/sendgrid-client';
12
- import { Template, SyncResult } from '../types/plugin-types';
13
- import { SendGridTemplateData } from '../types/sendgrid-types';
14
-
15
- export class SyncManager {
16
- constructor(
17
- private readonly templateManager: TemplateManager,
18
- private readonly converter: TemplateConverter,
19
- private readonly client: SendGridClient,
20
- private readonly logger?: {
21
- info: (message: string) => void;
22
- error: (message: string) => void;
23
- }
24
- ) {}
25
-
26
- async syncToProvider(template: Template): Promise<SyncResult> {
27
- try {
28
- const existingProviderId = template.providerTemplateIds?.sendgrid;
29
-
30
- if (existingProviderId) {
31
- // Update existing template
32
- await this.templateManager.updateTemplate(existingProviderId, template);
33
-
34
- return {
35
- success: true,
36
- action: 'updated',
37
- providerTemplateId: existingProviderId,
38
- localTemplateId: template.id,
39
- metadata: {
40
- newVersion: template.version
41
- }
42
- };
43
- } else {
44
- // Create new template
45
- const newProviderId = await this.templateManager.createTemplate(template);
46
-
47
- return {
48
- success: true,
49
- action: 'created',
50
- providerTemplateId: newProviderId,
51
- localTemplateId: template.id,
52
- metadata: {
53
- version: template.version
54
- }
55
- };
56
- }
57
- } catch (error) {
58
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
59
- this.logger?.error(`Template sync failed: ${errorMessage}`);
60
-
61
- return {
62
- success: false,
63
- action: 'failed',
64
- localTemplateId: template.id,
65
- error: errorMessage,
66
- metadata: {
67
- errorType: error instanceof Error ? error.constructor.name : 'UnknownError',
68
- timestamp: new Date()
69
- }
70
- };
71
- }
72
- }
73
-
74
- async syncFromProvider(providerTemplateId: string): Promise<Template> {
75
- const response = await this.client.request(`/templates/${providerTemplateId}`, {
76
- method: 'GET'
77
- });
78
-
79
- if (!this.client.isSuccessResponse(response)) {
80
- throw new Error(`Failed to fetch template from SendGrid: ${response.statusCode}`);
81
- }
82
-
83
- const templateData = this.client.parseResponseBody<SendGridTemplateData>(response);
84
- const localId = this.generateLocalId();
85
-
86
- return this.converter.convertToLocalTemplate(templateData, localId);
87
- }
88
-
89
- async batchSync(templates: Template[]): Promise<SyncResult[]> {
90
- this.logger?.info(`Starting batch sync of ${templates.length} templates`);
91
-
92
- const results: SyncResult[] = [];
93
-
94
- for (const template of templates) {
95
- const result = await this.syncToProvider(template);
96
- results.push(result);
97
-
98
- // Add delay to respect rate limits
99
- await this.sleep(100);
100
- }
101
-
102
- const successful = results.filter(r => r.success).length;
103
- this.logger?.info(`Batch sync completed: ${successful}/${templates.length} successful`);
104
-
105
- return results;
106
- }
107
-
108
- private generateLocalId(): string {
109
- return `local_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
110
- }
111
-
112
- private sleep(ms: number): Promise<void> {
113
- return new Promise(resolve => setTimeout(resolve, ms));
114
- }
115
- }
@@ -1,240 +0,0 @@
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
- import { SendGridClient } from '../api/sendgrid-client';
10
- import { SendGridApiError, TemplateNotFoundError, TemplateConflictError } from '../api/errors';
11
- import { OwnershipValidator } from '../safety/ownership-validator';
12
- import { PrefixEnforcer } from '../safety/prefix-enforcer';
13
- import { TemplateConverter } from '../converters/template-converter';
14
- import { Template, ProviderTemplate, TemplateVersion } from '../types/plugin-types';
15
- import {
16
- SendGridTemplateData,
17
- SendGridTemplateVersion
18
- } from '../types/sendgrid-types';
19
-
20
- export class TemplateManager {
21
- constructor(
22
- private readonly client: SendGridClient,
23
- private readonly ownershipValidator: OwnershipValidator,
24
- private readonly prefixEnforcer: PrefixEnforcer,
25
- private readonly converter: TemplateConverter,
26
- private readonly logger?: {
27
- info: (message: string) => void;
28
- error: (message: string) => void;
29
- }
30
- ) {}
31
-
32
- async createTemplate(template: Template): Promise<string> {
33
- // Apply template naming convention
34
- const templateName = this.prefixEnforcer.applyPrefix(template.name);
35
-
36
- // Create base template in SendGrid
37
- const createRequest = {
38
- name: templateName,
39
- generation: 'dynamic'
40
- };
41
-
42
- const response = await this.client.request('/templates', {
43
- method: 'POST',
44
- body: JSON.stringify(createRequest)
45
- });
46
-
47
- if (!this.client.isSuccessResponse(response)) {
48
- throw new SendGridApiError(
49
- `Failed to create template: ${response.statusCode}`,
50
- response.statusCode,
51
- response.body
52
- );
53
- }
54
-
55
- const templateData = this.client.parseResponseBody<SendGridTemplateData>(response);
56
- const templateId = templateData.id;
57
-
58
- // Register as app-managed
59
- this.ownershipValidator.registerTemplate(templateId);
60
-
61
- // Create initial version with content
62
- await this.createTemplateVersion(templateId, {
63
- name: 'v1.0.0',
64
- subject: template.subject,
65
- htmlContent: template.htmlContent,
66
- textContent: template.textContent,
67
- active: true
68
- });
69
-
70
- // Log operation
71
- this.logger?.info(`Created SendGrid template: ${templateName} (ID: ${templateId})`);
72
-
73
- return templateId;
74
- }
75
-
76
- async updateTemplate(providerTemplateId: string, template: Template): Promise<void> {
77
- // Validate template ownership
78
- this.ownershipValidator.validateOwnership(providerTemplateId);
79
-
80
- // Get current template state
81
- const currentTemplate = await this.getTemplate(providerTemplateId);
82
-
83
- // Check for conflicts
84
- if (this.hasConflicts(currentTemplate, template)) {
85
- throw new TemplateConflictError(
86
- providerTemplateId,
87
- this.detectConflicts(currentTemplate, template)
88
- );
89
- }
90
-
91
- // Create new version with updated content
92
- await this.createTemplateVersion(providerTemplateId, {
93
- name: `v${template.version}`,
94
- subject: template.subject,
95
- htmlContent: template.htmlContent,
96
- textContent: template.textContent,
97
- active: template.isActive
98
- });
99
-
100
- this.logger?.info(`Updated SendGrid template: ${providerTemplateId}`);
101
- }
102
-
103
- async deleteTemplate(providerTemplateId: string): Promise<void> {
104
- this.ownershipValidator.validateOwnership(providerTemplateId);
105
-
106
- const response = await this.client.request(`/templates/${providerTemplateId}`, {
107
- method: 'DELETE'
108
- });
109
-
110
- if (!this.client.isSuccessResponse(response)) {
111
- throw new SendGridApiError(
112
- `Failed to delete template: ${response.statusCode}`,
113
- response.statusCode,
114
- response.body
115
- );
116
- }
117
-
118
- this.ownershipValidator.unregisterTemplate(providerTemplateId);
119
- this.logger?.info(`Deleted SendGrid template: ${providerTemplateId}`);
120
- }
121
-
122
- async getTemplate(providerTemplateId: string): Promise<ProviderTemplate> {
123
- this.ownershipValidator.validateOwnership(providerTemplateId);
124
-
125
- const response = await this.client.request(`/templates/${providerTemplateId}`, {
126
- method: 'GET'
127
- });
128
-
129
- if (response.statusCode === 404) {
130
- throw new TemplateNotFoundError(providerTemplateId);
131
- }
132
-
133
- if (!this.client.isSuccessResponse(response)) {
134
- throw new SendGridApiError(
135
- `Failed to get template: ${response.statusCode}`,
136
- response.statusCode,
137
- response.body
138
- );
139
- }
140
-
141
- const templateData = this.client.parseResponseBody<SendGridTemplateData>(response);
142
- return this.converter.convertToProviderTemplate(templateData);
143
- }
144
-
145
- async listTemplates(): Promise<ProviderTemplate[]> {
146
- const response = await this.client.request('/templates?generations=dynamic', {
147
- method: 'GET'
148
- });
149
-
150
- if (!this.client.isSuccessResponse(response)) {
151
- throw new SendGridApiError(
152
- `Failed to list templates: ${response.statusCode}`,
153
- response.statusCode,
154
- response.body
155
- );
156
- }
157
-
158
- const data = this.client.parseResponseBody<{ result: SendGridTemplateData[] }>(response);
159
-
160
- // Filter to only app-managed templates
161
- const appTemplates = data.result.filter(template =>
162
- this.prefixEnforcer.hasPrefix(template.name)
163
- );
164
-
165
- // Register all app templates
166
- appTemplates.forEach(template => {
167
- this.ownershipValidator.registerTemplate(template.id);
168
- });
169
-
170
- return appTemplates.map(template =>
171
- this.converter.convertToProviderTemplate(template)
172
- );
173
- }
174
-
175
- async createTemplateVersion(
176
- templateId: string,
177
- version: TemplateVersion
178
- ): Promise<string> {
179
- const versionRequest = {
180
- template_id: templateId,
181
- name: version.name,
182
- subject: version.subject,
183
- html_content: version.htmlContent,
184
- plain_content: version.textContent || '',
185
- active: version.active ? 1 : 0
186
- };
187
-
188
- const response = await this.client.request(`/templates/${templateId}/versions`, {
189
- method: 'POST',
190
- body: JSON.stringify(versionRequest)
191
- });
192
-
193
- if (!this.client.isSuccessResponse(response)) {
194
- throw new SendGridApiError(
195
- `Failed to create template version: ${response.statusCode}`,
196
- response.statusCode,
197
- response.body
198
- );
199
- }
200
-
201
- const versionData = this.client.parseResponseBody<SendGridTemplateVersion>(response);
202
- return versionData.id;
203
- }
204
-
205
- async activateTemplateVersion(templateId: string, versionId: string): Promise<void> {
206
- const response = await this.client.request(
207
- `/templates/${templateId}/versions/${versionId}/activate`,
208
- { method: 'POST' }
209
- );
210
-
211
- if (!this.client.isSuccessResponse(response)) {
212
- throw new SendGridApiError(
213
- `Failed to activate template version: ${response.statusCode}`,
214
- response.statusCode,
215
- response.body
216
- );
217
- }
218
-
219
- this.logger?.info(`Activated template version: ${templateId}/${versionId}`);
220
- }
221
-
222
- private hasConflicts(current: ProviderTemplate, updated: Template): boolean {
223
- // Simple conflict detection - can be enhanced
224
- return current.updatedAt > updated.updatedAt;
225
- }
226
-
227
- private detectConflicts(current: ProviderTemplate, updated: Template): string[] {
228
- const conflicts: string[] = [];
229
-
230
- if (current.subject !== updated.subject) {
231
- conflicts.push('subject modified');
232
- }
233
-
234
- if (current.htmlContent !== updated.htmlContent) {
235
- conflicts.push('HTML content modified');
236
- }
237
-
238
- return conflicts;
239
- }
240
- }
@@ -1,55 +0,0 @@
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
- import { UnauthorizedTemplateAccessError } from '../api/errors';
10
-
11
- export type SafetyMode = 'strict' | 'permissive';
12
-
13
- export class OwnershipValidator {
14
- private appTemplateIds: Set<string> = new Set();
15
- private readonly safetyMode: SafetyMode;
16
- private readonly logger?: {
17
- debug: (message: string) => void;
18
- };
19
-
20
- constructor(safetyMode: SafetyMode, logger?: { debug: (message: string) => void }) {
21
- this.safetyMode = safetyMode;
22
- this.logger = logger;
23
- }
24
-
25
- registerTemplate(templateId: string): void {
26
- this.appTemplateIds.add(templateId);
27
- this.logger?.debug(`Registered app template: ${templateId}`);
28
- }
29
-
30
- unregisterTemplate(templateId: string): void {
31
- this.appTemplateIds.delete(templateId);
32
- this.logger?.debug(`Unregistered app template: ${templateId}`);
33
- }
34
-
35
- validateOwnership(templateId: string): void {
36
- if (this.safetyMode === 'strict' && !this.appTemplateIds.has(templateId)) {
37
- throw new UnauthorizedTemplateAccessError(
38
- `Template ${templateId} is not managed by this application`
39
- );
40
- }
41
- }
42
-
43
- isOwnedTemplate(templateId: string): boolean {
44
- return this.appTemplateIds.has(templateId);
45
- }
46
-
47
- getOwnedTemplates(): string[] {
48
- return Array.from(this.appTemplateIds);
49
- }
50
-
51
- clearRegistrations(): void {
52
- this.appTemplateIds.clear();
53
- this.logger?.debug('Cleared all template registrations');
54
- }
55
- }
@@ -1,37 +0,0 @@
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
- export class PrefixEnforcer {
10
- private readonly prefix: string;
11
-
12
- constructor(prefix: string) {
13
- this.prefix = prefix;
14
- }
15
-
16
- applyPrefix(name: string): string {
17
- if (name.startsWith(this.prefix)) {
18
- return name;
19
- }
20
- return `${this.prefix}${name}`;
21
- }
22
-
23
- stripPrefix(name: string): string {
24
- if (name.startsWith(this.prefix)) {
25
- return name.substring(this.prefix.length);
26
- }
27
- return name;
28
- }
29
-
30
- hasPrefix(name: string): boolean {
31
- return name.startsWith(this.prefix);
32
- }
33
-
34
- getPrefix(): string {
35
- return this.prefix;
36
- }
37
- }
@@ -1,34 +0,0 @@
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
- export interface SendGridPluginConfig {
10
- apiKey: string;
11
- templatePrefix: string;
12
- sharedAccount?: boolean;
13
- safetyMode: 'strict' | 'permissive';
14
- rateLimits?: {
15
- templatesPerMinute: number;
16
- apiCallsPerSecond: number;
17
- };
18
- webhookConfig?: {
19
- secret: string;
20
- eventTypes: string[];
21
- };
22
- }
23
-
24
- export interface ApiRequestOptions {
25
- method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
26
- headers?: Record<string, string>;
27
- body?: string;
28
- }
29
-
30
- export interface ApiResponse {
31
- statusCode: number;
32
- headers: Record<string, string>;
33
- body: string;
34
- }
@@ -1,97 +0,0 @@
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
- export interface PluginConfig {
10
- [key: string]: unknown;
11
- }
12
-
13
- export interface ProviderCapability {
14
- feature: string;
15
- description: string;
16
- methods: string[];
17
- config: Record<string, unknown>;
18
- }
19
-
20
- export interface Template {
21
- id: string;
22
- name: string;
23
- description?: string;
24
- subject: string;
25
- htmlContent: string;
26
- textContent?: string;
27
- category: string;
28
- tags: string[];
29
- version: string;
30
- isActive: boolean;
31
- context?: string;
32
- variables?: Record<string, unknown>;
33
- requiredVariables: string[];
34
- providerTemplateIds?: Record<string, string>;
35
- lastSyncedAt?: Record<string, Date>;
36
- syncStatus?: Record<string, string>;
37
- createdAt: Date;
38
- updatedAt: Date;
39
- createdBy: string;
40
- lastModifiedBy: string;
41
- }
42
-
43
- export interface ProviderTemplate {
44
- id: string;
45
- name: string;
46
- description?: string;
47
- subject: string;
48
- htmlContent: string;
49
- textContent?: string;
50
- version: string;
51
- active: boolean;
52
- createdAt: Date;
53
- updatedAt: Date;
54
- }
55
-
56
- export interface TemplateVersion {
57
- name: string;
58
- subject: string;
59
- htmlContent: string;
60
- textContent?: string;
61
- active: boolean;
62
- }
63
-
64
- export interface SyncResult {
65
- success: boolean;
66
- action: 'created' | 'updated' | 'deleted' | 'failed';
67
- providerTemplateId?: string;
68
- localTemplateId?: string;
69
- error?: string;
70
- metadata: Record<string, unknown>;
71
- }
72
-
73
- export interface ValidationResult {
74
- isValid: boolean;
75
- errors: string[];
76
- }
77
-
78
- export interface RouteConflict {
79
- templateId: string;
80
- conflicts: string[];
81
- }
82
-
83
- export interface DateRange {
84
- start: Date;
85
- end: Date;
86
- }
87
-
88
- export interface TemplateAnalytics {
89
- templateId: string;
90
- opens: number;
91
- clicks: number;
92
- bounces: number;
93
- deliveries: number;
94
- dateRange: DateRange;
95
- }
96
-
97
- export type FeatureConfig = Record<string, unknown>;
@@ -1,98 +0,0 @@
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
- export interface VerifiedSender {
10
- id: string;
11
- email: string;
12
- name: string;
13
- isVerified: boolean;
14
- domain: string;
15
- verificationStatus: 'pending' | 'verified' | 'failed';
16
- createdAt: Date;
17
- lastVerifiedAt?: Date;
18
- }
19
-
20
- export interface SenderValidationResult {
21
- isValid: boolean;
22
- verifiedSender?: VerifiedSender;
23
- domain: string;
24
- domainMatches: VerifiedSender[];
25
- errors: string[];
26
- suggestions: string[];
27
- }
28
-
29
- export interface SendGridTemplateData {
30
- id: string;
31
- name: string;
32
- generation: 'legacy' | 'dynamic';
33
- updated_at: string;
34
- versions?: SendGridTemplateVersion[];
35
- }
36
-
37
- export interface SendGridTemplateVersion {
38
- id: string;
39
- template_id: string;
40
- active: number;
41
- name: string;
42
- html_content: string;
43
- plain_content?: string;
44
- subject: string;
45
- updated_at: string;
46
- editor: string;
47
- }
48
-
49
- export interface SendGridVerifiedSenderData {
50
- id: number;
51
- nickname: string;
52
- from_email: string;
53
- from_name: string;
54
- reply_to: string;
55
- reply_to_name: string;
56
- address: string;
57
- address2: string;
58
- city: string;
59
- state: string;
60
- zip: string;
61
- country: string;
62
- verified: boolean;
63
- locked: boolean;
64
- }
65
-
66
- export interface SendGridApiError extends Error {
67
- statusCode?: number;
68
- response?: unknown;
69
- }
70
-
71
- /**
72
- * Email attachment in the canonical format (matches @bernierllc/email-sender EmailAttachment).
73
- */
74
- export interface EmailAttachment {
75
- filename: string;
76
- content: string; // Base64 encoded
77
- contentType?: string;
78
- disposition?: 'attachment' | 'inline';
79
- }
80
-
81
- /**
82
- * SendGrid's attachment format for the v3 mail send API.
83
- */
84
- export interface SendGridAttachment {
85
- content: string;
86
- filename: string;
87
- type?: string;
88
- disposition?: string;
89
- }
90
-
91
- /**
92
- * SendGrid mail personalization/message payload fields related to
93
- * attachments and custom headers.
94
- */
95
- export interface SendGridMailPayload {
96
- attachments?: SendGridAttachment[];
97
- headers?: Record<string, string>;
98
- }
package/tsconfig.json DELETED
@@ -1,18 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "module": "commonjs",
5
- "lib": ["ES2020"],
6
- "declaration": true,
7
- "outDir": "./dist",
8
- "rootDir": "./src",
9
- "strict": true,
10
- "esModuleInterop": true,
11
- "skipLibCheck": true,
12
- "forceConsistentCasingInFileNames": true,
13
- "resolveJsonModule": true,
14
- "moduleResolution": "node"
15
- },
16
- "include": ["src/**/*"],
17
- "exclude": ["node_modules", "dist", "__tests__"]
18
- }