@memberjunction/communication-engine 3.4.0 → 4.1.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/README.md +198 -0
- package/dist/BaseProvider.js +13 -17
- package/dist/BaseProvider.js.map +1 -1
- package/dist/Engine.js +12 -16
- package/dist/Engine.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -18
- package/dist/index.js.map +1 -1
- package/package.json +10 -9
- package/readme.md +129 -270
package/README.md
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# @memberjunction/communication-engine
|
|
2
|
+
|
|
3
|
+
Server-side communication engine for MemberJunction. This package extends `CommunicationEngineBase` from `@memberjunction/communication-types` to provide the concrete implementation for sending messages, creating drafts, and managing communication runs through registered providers.
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
```mermaid
|
|
8
|
+
graph TD
|
|
9
|
+
subgraph engine["@memberjunction/communication-engine"]
|
|
10
|
+
CE["CommunicationEngine\n(Singleton)"]
|
|
11
|
+
PMS["ProcessedMessageServer"]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
subgraph base["@memberjunction/communication-types"]
|
|
15
|
+
CEB["CommunicationEngineBase"]
|
|
16
|
+
BCP["BaseCommunicationProvider"]
|
|
17
|
+
MSG["Message"]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
subgraph providers["Registered Providers"]
|
|
21
|
+
SG["SendGrid"]
|
|
22
|
+
GM["Gmail"]
|
|
23
|
+
TW["Twilio"]
|
|
24
|
+
MSP["MS Graph"]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
subgraph templates["@memberjunction/templates"]
|
|
28
|
+
TE["TemplateEngineServer"]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
CEB --> CE
|
|
32
|
+
CE -->|GetProvider| BCP
|
|
33
|
+
CE -->|processes| MSG
|
|
34
|
+
MSG --> PMS
|
|
35
|
+
PMS -->|renders via| TE
|
|
36
|
+
BCP --> SG
|
|
37
|
+
BCP --> GM
|
|
38
|
+
BCP --> TW
|
|
39
|
+
BCP --> MSP
|
|
40
|
+
|
|
41
|
+
style engine fill:#7c5295,stroke:#563a6b,color:#fff
|
|
42
|
+
style base fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
43
|
+
style providers fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
44
|
+
style templates fill:#b8762f,stroke:#8a5722,color:#fff
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm install @memberjunction/communication-engine
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Key Classes
|
|
54
|
+
|
|
55
|
+
### CommunicationEngine
|
|
56
|
+
|
|
57
|
+
Singleton engine that orchestrates message sending across all registered providers. Handles provider lookup via the MJGlobal class factory, message processing (template rendering), communication run lifecycle, and logging.
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { CommunicationEngine } from '@memberjunction/communication-engine';
|
|
61
|
+
import { Message, MessageRecipient } from '@memberjunction/communication-types';
|
|
62
|
+
|
|
63
|
+
const engine = CommunicationEngine.Instance;
|
|
64
|
+
await engine.Config(false, contextUser);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Sending a Single Message
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
const message = new Message();
|
|
71
|
+
message.From = 'sender@example.com';
|
|
72
|
+
message.To = 'recipient@example.com';
|
|
73
|
+
message.Subject = 'Welcome';
|
|
74
|
+
message.HTMLBody = '<h1>Hello</h1>';
|
|
75
|
+
|
|
76
|
+
const result = await engine.SendSingleMessage(
|
|
77
|
+
'SendGrid', // provider name
|
|
78
|
+
'Email', // provider message type name
|
|
79
|
+
message,
|
|
80
|
+
undefined, // optional CommunicationRunEntity
|
|
81
|
+
false // previewOnly
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
if (result.Success) {
|
|
85
|
+
console.log('Message sent');
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Sending to Multiple Recipients
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
const recipients: MessageRecipient[] = [
|
|
93
|
+
{ To: 'alice@example.com', FullName: 'Alice', ContextData: { role: 'admin' } },
|
|
94
|
+
{ To: 'bob@example.com', FullName: 'Bob', ContextData: { role: 'user' } }
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
const message = new Message();
|
|
98
|
+
message.From = 'noreply@example.com';
|
|
99
|
+
message.BodyTemplate = templateEntity; // uses template for personalization
|
|
100
|
+
message.Subject = 'Update';
|
|
101
|
+
|
|
102
|
+
const results = await engine.SendMessages(
|
|
103
|
+
'SendGrid',
|
|
104
|
+
'Email',
|
|
105
|
+
message,
|
|
106
|
+
recipients,
|
|
107
|
+
false // previewOnly
|
|
108
|
+
);
|
|
109
|
+
// results is MessageResult[] - one per recipient
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Creating a Draft
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
const message = new Message();
|
|
116
|
+
message.From = 'user@example.com';
|
|
117
|
+
message.To = 'recipient@example.com';
|
|
118
|
+
message.Subject = 'Draft Email';
|
|
119
|
+
message.HTMLBody = '<p>Content here</p>';
|
|
120
|
+
|
|
121
|
+
const result = await engine.CreateDraft(
|
|
122
|
+
message,
|
|
123
|
+
'Microsoft Graph', // only providers with SupportsDrafts
|
|
124
|
+
contextUser
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
if (result.Success) {
|
|
128
|
+
console.log(`Draft ID: ${result.DraftID}`);
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Per-Request Credentials
|
|
133
|
+
|
|
134
|
+
All send methods accept an optional `credentials` parameter for per-request credential overrides:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import { SendGridCredentials } from '@memberjunction/communication-sendgrid';
|
|
138
|
+
|
|
139
|
+
const result = await engine.SendSingleMessage(
|
|
140
|
+
'SendGrid',
|
|
141
|
+
'Email',
|
|
142
|
+
message,
|
|
143
|
+
undefined,
|
|
144
|
+
false,
|
|
145
|
+
{ apiKey: 'SG.customer-specific-key' } // per-request credentials
|
|
146
|
+
);
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### ProcessedMessageServer
|
|
150
|
+
|
|
151
|
+
Server-side implementation of `ProcessedMessage` that renders templates using `TemplateEngineServer`. Automatically processes body, HTML body, and subject templates with the provided context data.
|
|
152
|
+
|
|
153
|
+
```mermaid
|
|
154
|
+
sequenceDiagram
|
|
155
|
+
participant App as Application
|
|
156
|
+
participant CE as CommunicationEngine
|
|
157
|
+
participant PMS as ProcessedMessageServer
|
|
158
|
+
participant TE as TemplateEngineServer
|
|
159
|
+
participant P as Provider
|
|
160
|
+
|
|
161
|
+
App->>CE: SendSingleMessage(providerName, messageType, message)
|
|
162
|
+
CE->>CE: GetProvider(providerName)
|
|
163
|
+
CE->>PMS: new ProcessedMessageServer(message)
|
|
164
|
+
CE->>PMS: Process()
|
|
165
|
+
PMS->>TE: RenderTemplate(bodyTemplate, contextData)
|
|
166
|
+
TE-->>PMS: rendered content
|
|
167
|
+
PMS-->>CE: ProcessResult
|
|
168
|
+
CE->>P: SendSingleMessage(processedMessage, credentials)
|
|
169
|
+
P-->>CE: MessageResult
|
|
170
|
+
CE-->>App: MessageResult
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## API Reference
|
|
174
|
+
|
|
175
|
+
| Method | Description |
|
|
176
|
+
|--------|-------------|
|
|
177
|
+
| `Config(forceRefresh, contextUser, provider)` | Initialize the engine and load metadata |
|
|
178
|
+
| `GetProvider(providerName)` | Retrieve a provider instance from the class factory |
|
|
179
|
+
| `SendSingleMessage(provider, type, message, run?, preview?, credentials?)` | Send one message |
|
|
180
|
+
| `SendMessages(provider, type, message, recipients, preview?, credentials?)` | Send to multiple recipients |
|
|
181
|
+
| `CreateDraft(message, providerName, contextUser?, credentials?)` | Create a draft message |
|
|
182
|
+
|
|
183
|
+
## Dependencies
|
|
184
|
+
|
|
185
|
+
| Package | Purpose |
|
|
186
|
+
|---------|---------|
|
|
187
|
+
| `@memberjunction/communication-types` | Base engine, provider, and message types |
|
|
188
|
+
| `@memberjunction/core` | UserInfo, logging, metadata access |
|
|
189
|
+
| `@memberjunction/core-entities` | CommunicationRunEntity and related entities |
|
|
190
|
+
| `@memberjunction/global` | MJGlobal class factory for provider instantiation |
|
|
191
|
+
| `@memberjunction/templates` | Server-side template rendering engine |
|
|
192
|
+
|
|
193
|
+
## Development
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
npm run build # Compile TypeScript
|
|
197
|
+
npm start # Watch mode
|
|
198
|
+
```
|
package/dist/BaseProvider.js
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const communication_types_1 = require("@memberjunction/communication-types");
|
|
5
|
-
const core_1 = require("@memberjunction/core");
|
|
6
|
-
const templates_1 = require("@memberjunction/templates");
|
|
1
|
+
import { ProcessedMessage } from "@memberjunction/communication-types";
|
|
2
|
+
import { LogError } from "@memberjunction/core";
|
|
3
|
+
import { TemplateEngineServer } from '@memberjunction/templates';
|
|
7
4
|
/**
|
|
8
5
|
* Server side implementation that calls the templating engine to process the message
|
|
9
6
|
*/
|
|
10
|
-
class ProcessedMessageServer extends
|
|
7
|
+
export class ProcessedMessageServer extends ProcessedMessage {
|
|
11
8
|
async Process(forceTemplateRefresh, contextUser) {
|
|
12
9
|
if (this.BodyTemplate || this.SubjectTemplate || this.HTMLBodyTemplate)
|
|
13
|
-
await
|
|
10
|
+
await TemplateEngineServer.Instance.Config(forceTemplateRefresh, contextUser); // make sure the template engine is configured if we are using either template
|
|
14
11
|
if (this.BodyTemplate) {
|
|
15
12
|
// process the body template
|
|
16
13
|
const regularContent = this.BodyTemplate.GetHighestPriorityContent('Text');
|
|
@@ -19,12 +16,12 @@ class ProcessedMessageServer extends communication_types_1.ProcessedMessage {
|
|
|
19
16
|
Success: false,
|
|
20
17
|
Message: 'BodyTemplate does not have a Text option and this is required for processing the body of the message.'
|
|
21
18
|
};
|
|
22
|
-
const result = await
|
|
19
|
+
const result = await TemplateEngineServer.Instance.RenderTemplate(this.BodyTemplate, regularContent, this.ContextData);
|
|
23
20
|
if (result && result.Success) {
|
|
24
21
|
this.ProcessedBody = result.Output;
|
|
25
22
|
}
|
|
26
23
|
else {
|
|
27
|
-
|
|
24
|
+
LogError(`Failed to render template for body: ${result.Message}`);
|
|
28
25
|
return {
|
|
29
26
|
Success: false,
|
|
30
27
|
Message: result.Message
|
|
@@ -33,12 +30,12 @@ class ProcessedMessageServer extends communication_types_1.ProcessedMessage {
|
|
|
33
30
|
if (!this.HTMLBodyTemplate && !this.HTMLBody) { // if we have an HTMLBodyTemplate, we will process it separately below
|
|
34
31
|
const htmlContent = this.BodyTemplate.GetHighestPriorityContent('HTML');
|
|
35
32
|
if (htmlContent) {
|
|
36
|
-
const result = await
|
|
33
|
+
const result = await TemplateEngineServer.Instance.RenderTemplate(this.BodyTemplate, htmlContent, this.ContextData);
|
|
37
34
|
if (result && result.Success) {
|
|
38
35
|
this.ProcessedHTMLBody = result.Output;
|
|
39
36
|
}
|
|
40
37
|
else {
|
|
41
|
-
|
|
38
|
+
LogError(`Failed to render template for html body: ${result.Message}`);
|
|
42
39
|
return {
|
|
43
40
|
Success: false,
|
|
44
41
|
Message: result.Message
|
|
@@ -58,12 +55,12 @@ class ProcessedMessageServer extends communication_types_1.ProcessedMessage {
|
|
|
58
55
|
// process the subject template
|
|
59
56
|
const htmlContent = this.HTMLBodyTemplate.GetHighestPriorityContent('HTML');
|
|
60
57
|
if (htmlContent) {
|
|
61
|
-
const result = await
|
|
58
|
+
const result = await TemplateEngineServer.Instance.RenderTemplate(this.HTMLBodyTemplate, htmlContent, this.ContextData);
|
|
62
59
|
if (result && result.Success) {
|
|
63
60
|
this.ProcessedHTMLBody = result.Output;
|
|
64
61
|
}
|
|
65
62
|
else {
|
|
66
|
-
|
|
63
|
+
LogError(`Failed to render template for html body 2: ${result.Message}`);
|
|
67
64
|
return {
|
|
68
65
|
Success: false,
|
|
69
66
|
Message: result.Message
|
|
@@ -84,12 +81,12 @@ class ProcessedMessageServer extends communication_types_1.ProcessedMessage {
|
|
|
84
81
|
// process the subject template
|
|
85
82
|
const subjectContent = this.SubjectTemplate.GetHighestPriorityContent('HTML');
|
|
86
83
|
if (subjectContent) {
|
|
87
|
-
const result = await
|
|
84
|
+
const result = await TemplateEngineServer.Instance.RenderTemplate(this.SubjectTemplate, subjectContent, this.ContextData);
|
|
88
85
|
if (result && result.Success) {
|
|
89
86
|
this.ProcessedSubject = result.Output;
|
|
90
87
|
}
|
|
91
88
|
else {
|
|
92
|
-
|
|
89
|
+
LogError(`Failed to render template for subject: ${result.Message}`);
|
|
93
90
|
return {
|
|
94
91
|
Success: false,
|
|
95
92
|
Message: result.Message
|
|
@@ -111,5 +108,4 @@ class ProcessedMessageServer extends communication_types_1.ProcessedMessage {
|
|
|
111
108
|
};
|
|
112
109
|
}
|
|
113
110
|
}
|
|
114
|
-
exports.ProcessedMessageServer = ProcessedMessageServer;
|
|
115
111
|
//# sourceMappingURL=BaseProvider.js.map
|
package/dist/BaseProvider.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseProvider.js","sourceRoot":"","sources":["../src/BaseProvider.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"BaseProvider.js","sourceRoot":"","sources":["../src/BaseProvider.ts"],"names":[],"mappings":"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"}
|
package/dist/Engine.js
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const core_1 = require("@memberjunction/core");
|
|
6
|
-
const global_1 = require("@memberjunction/global");
|
|
7
|
-
const BaseProvider_1 = require("./BaseProvider");
|
|
1
|
+
import { BaseCommunicationProvider, CommunicationEngineBase, Message } from "@memberjunction/communication-types";
|
|
2
|
+
import { LogError, LogStatus } from "@memberjunction/core";
|
|
3
|
+
import { MJGlobal } from "@memberjunction/global";
|
|
4
|
+
import { ProcessedMessageServer } from "./BaseProvider.js";
|
|
8
5
|
/**
|
|
9
6
|
* Base class for communications. This class can be sub-classed if desired if you would like to modify the logic across ALL actions. To do so, sub-class this class and use the
|
|
10
7
|
* @RegisterClass decorator from the @memberjunction/global package to register your sub-class with the ClassFactory. This will cause your sub-class to be used instead of this base class when the Metadata object insantiates the ActionEngine.
|
|
11
8
|
*/
|
|
12
|
-
class CommunicationEngine extends
|
|
9
|
+
export class CommunicationEngine extends CommunicationEngineBase {
|
|
13
10
|
static get Instance() {
|
|
14
11
|
return super.getInstance();
|
|
15
12
|
}
|
|
@@ -22,7 +19,7 @@ class CommunicationEngine extends communication_types_1.CommunicationEngineBase
|
|
|
22
19
|
if (!this.Loaded) {
|
|
23
20
|
throw new Error(`Metadata not loaded. Call Config() before accessing metadata.`);
|
|
24
21
|
}
|
|
25
|
-
const instance =
|
|
22
|
+
const instance = MJGlobal.Instance.ClassFactory.CreateInstance(BaseCommunicationProvider, providerName);
|
|
26
23
|
if (instance) {
|
|
27
24
|
// make sure the class we got back is NOT an instance of the base class, that is the default behavior of CreateInstance if we
|
|
28
25
|
// dont have a registration for the class we are looking for
|
|
@@ -56,7 +53,7 @@ class CommunicationEngine extends communication_types_1.CommunicationEngineBase
|
|
|
56
53
|
throw new Error(`Failed to start communication run.`);
|
|
57
54
|
const results = [];
|
|
58
55
|
for (const r of recipients) {
|
|
59
|
-
const messageCopy = new
|
|
56
|
+
const messageCopy = new Message(message);
|
|
60
57
|
messageCopy.To = r.To;
|
|
61
58
|
messageCopy.ContextData = r.ContextData;
|
|
62
59
|
const result = await this.SendSingleMessage(providerName, providerMessageTypeName, messageCopy, run, previewOnly, credentials);
|
|
@@ -99,7 +96,7 @@ class CommunicationEngine extends communication_types_1.CommunicationEngineBase
|
|
|
99
96
|
message.MessageType = providerMessageType;
|
|
100
97
|
}
|
|
101
98
|
// now, process the message
|
|
102
|
-
const processedMessage = new
|
|
99
|
+
const processedMessage = new ProcessedMessageServer(message);
|
|
103
100
|
const processResult = await processedMessage.Process(false, this.ContextUser);
|
|
104
101
|
if (processResult.Success) {
|
|
105
102
|
if (previewOnly) {
|
|
@@ -163,7 +160,7 @@ class CommunicationEngine extends communication_types_1.CommunicationEngineBase
|
|
|
163
160
|
};
|
|
164
161
|
}
|
|
165
162
|
// Process message (render templates)
|
|
166
|
-
const processedMessage = new
|
|
163
|
+
const processedMessage = new ProcessedMessageServer(message);
|
|
167
164
|
const processResult = await processedMessage.Process(false, contextUser || this.ContextUser);
|
|
168
165
|
if (!processResult.Success) {
|
|
169
166
|
return {
|
|
@@ -177,16 +174,16 @@ class CommunicationEngine extends communication_types_1.CommunicationEngineBase
|
|
|
177
174
|
ContextData: message.ContextData
|
|
178
175
|
}, credentials);
|
|
179
176
|
if (result.Success) {
|
|
180
|
-
|
|
177
|
+
LogStatus(`Draft created successfully via ${providerName}. Draft ID: ${result.DraftID}`);
|
|
181
178
|
}
|
|
182
179
|
else {
|
|
183
|
-
|
|
180
|
+
LogError(`Failed to create draft via ${providerName}`, undefined, result.ErrorMessage);
|
|
184
181
|
}
|
|
185
182
|
return result;
|
|
186
183
|
}
|
|
187
184
|
catch (error) {
|
|
188
185
|
const errorMessage = error instanceof Error ? error.message : 'Error creating draft';
|
|
189
|
-
|
|
186
|
+
LogError('Error creating draft', undefined, error);
|
|
190
187
|
return {
|
|
191
188
|
Success: false,
|
|
192
189
|
ErrorMessage: errorMessage
|
|
@@ -194,5 +191,4 @@ class CommunicationEngine extends communication_types_1.CommunicationEngineBase
|
|
|
194
191
|
}
|
|
195
192
|
}
|
|
196
193
|
}
|
|
197
|
-
exports.CommunicationEngine = CommunicationEngine;
|
|
198
194
|
//# sourceMappingURL=Engine.js.map
|
package/dist/Engine.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Engine.js","sourceRoot":"","sources":["../src/Engine.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Engine.js","sourceRoot":"","sources":["../src/Engine.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from './Engine';
|
|
2
|
-
export * from './BaseProvider';
|
|
1
|
+
export * from './Engine.js';
|
|
2
|
+
export * from './BaseProvider.js';
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,20 +1,4 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
1
|
// PUBLIC API SURFACE AREA
|
|
18
|
-
|
|
19
|
-
|
|
2
|
+
export * from './Engine.js';
|
|
3
|
+
export * from './BaseProvider.js';
|
|
20
4
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0BAA0B;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/communication-engine",
|
|
3
|
-
"
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "4.1.0",
|
|
4
5
|
"description": "MemberJunction: Core Communication Framework Library",
|
|
5
6
|
"main": "dist/index.js",
|
|
6
7
|
"types": "dist/index.d.ts",
|
|
@@ -9,22 +10,22 @@
|
|
|
9
10
|
],
|
|
10
11
|
"scripts": {
|
|
11
12
|
"start": "ts-node-dev src/index.ts",
|
|
12
|
-
"build": "tsc",
|
|
13
|
+
"build": "tsc && tsc-alias -f",
|
|
13
14
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
14
15
|
},
|
|
15
16
|
"author": "MemberJunction.com",
|
|
16
17
|
"license": "ISC",
|
|
17
18
|
"devDependencies": {
|
|
18
19
|
"ts-node-dev": "^2.0.0",
|
|
19
|
-
"typescript": "^5.
|
|
20
|
+
"typescript": "^5.9.3"
|
|
20
21
|
},
|
|
21
22
|
"dependencies": {
|
|
22
|
-
"@memberjunction/global": "
|
|
23
|
-
"@memberjunction/core": "
|
|
24
|
-
"@memberjunction/templates": "
|
|
25
|
-
"@memberjunction/core-entities": "
|
|
26
|
-
"@memberjunction/communication-types": "
|
|
27
|
-
"rxjs": "^7.8.
|
|
23
|
+
"@memberjunction/global": "4.1.0",
|
|
24
|
+
"@memberjunction/core": "4.1.0",
|
|
25
|
+
"@memberjunction/templates": "4.1.0",
|
|
26
|
+
"@memberjunction/core-entities": "4.1.0",
|
|
27
|
+
"@memberjunction/communication-types": "4.1.0",
|
|
28
|
+
"rxjs": "^7.8.2"
|
|
28
29
|
},
|
|
29
30
|
"repository": {
|
|
30
31
|
"type": "git",
|
package/readme.md
CHANGED
|
@@ -1,16 +1,48 @@
|
|
|
1
1
|
# @memberjunction/communication-engine
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
Server-side communication engine for MemberJunction. This package extends `CommunicationEngineBase` from `@memberjunction/communication-types` to provide the concrete implementation for sending messages, creating drafts, and managing communication runs through registered providers.
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
```mermaid
|
|
8
|
+
graph TD
|
|
9
|
+
subgraph engine["@memberjunction/communication-engine"]
|
|
10
|
+
CE["CommunicationEngine\n(Singleton)"]
|
|
11
|
+
PMS["ProcessedMessageServer"]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
subgraph base["@memberjunction/communication-types"]
|
|
15
|
+
CEB["CommunicationEngineBase"]
|
|
16
|
+
BCP["BaseCommunicationProvider"]
|
|
17
|
+
MSG["Message"]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
subgraph providers["Registered Providers"]
|
|
21
|
+
SG["SendGrid"]
|
|
22
|
+
GM["Gmail"]
|
|
23
|
+
TW["Twilio"]
|
|
24
|
+
MSP["MS Graph"]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
subgraph templates["@memberjunction/templates"]
|
|
28
|
+
TE["TemplateEngineServer"]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
CEB --> CE
|
|
32
|
+
CE -->|GetProvider| BCP
|
|
33
|
+
CE -->|processes| MSG
|
|
34
|
+
MSG --> PMS
|
|
35
|
+
PMS -->|renders via| TE
|
|
36
|
+
BCP --> SG
|
|
37
|
+
BCP --> GM
|
|
38
|
+
BCP --> TW
|
|
39
|
+
BCP --> MSP
|
|
40
|
+
|
|
41
|
+
style engine fill:#7c5295,stroke:#563a6b,color:#fff
|
|
42
|
+
style base fill:#2d6a9f,stroke:#1a4971,color:#fff
|
|
43
|
+
style providers fill:#2d8659,stroke:#1a5c3a,color:#fff
|
|
44
|
+
style templates fill:#b8762f,stroke:#8a5722,color:#fff
|
|
45
|
+
```
|
|
14
46
|
|
|
15
47
|
## Installation
|
|
16
48
|
|
|
@@ -18,322 +50,149 @@ This package serves as the core engine for the MemberJunction communication syst
|
|
|
18
50
|
npm install @memberjunction/communication-engine
|
|
19
51
|
```
|
|
20
52
|
|
|
21
|
-
##
|
|
53
|
+
## Key Classes
|
|
22
54
|
|
|
23
55
|
### CommunicationEngine
|
|
24
56
|
|
|
25
|
-
|
|
57
|
+
Singleton engine that orchestrates message sending across all registered providers. Handles provider lookup via the MJGlobal class factory, message processing (template rendering), communication run lifecycle, and logging.
|
|
26
58
|
|
|
27
59
|
```typescript
|
|
28
60
|
import { CommunicationEngine } from '@memberjunction/communication-engine';
|
|
61
|
+
import { Message, MessageRecipient } from '@memberjunction/communication-types';
|
|
29
62
|
|
|
30
|
-
// Get the singleton instance
|
|
31
63
|
const engine = CommunicationEngine.Instance;
|
|
32
|
-
|
|
33
|
-
// Configuration must be called before use
|
|
34
|
-
await engine.Config();
|
|
64
|
+
await engine.Config(false, contextUser);
|
|
35
65
|
```
|
|
36
66
|
|
|
37
|
-
### ProcessedMessageServer
|
|
38
|
-
|
|
39
|
-
Server-side message processor that handles template rendering for message bodies, HTML bodies, and subjects.
|
|
40
|
-
|
|
41
|
-
## Usage Examples
|
|
42
|
-
|
|
43
67
|
### Sending a Single Message
|
|
44
68
|
|
|
45
69
|
```typescript
|
|
46
|
-
import { CommunicationEngine } from '@memberjunction/communication-engine';
|
|
47
|
-
import { Message } from '@memberjunction/communication-types';
|
|
48
|
-
|
|
49
|
-
// Create a message
|
|
50
70
|
const message = new Message();
|
|
51
|
-
message.
|
|
52
|
-
message.
|
|
53
|
-
message.
|
|
54
|
-
message.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
'
|
|
59
|
-
|
|
60
|
-
|
|
71
|
+
message.From = 'sender@example.com';
|
|
72
|
+
message.To = 'recipient@example.com';
|
|
73
|
+
message.Subject = 'Welcome';
|
|
74
|
+
message.HTMLBody = '<h1>Hello</h1>';
|
|
75
|
+
|
|
76
|
+
const result = await engine.SendSingleMessage(
|
|
77
|
+
'SendGrid', // provider name
|
|
78
|
+
'Email', // provider message type name
|
|
79
|
+
message,
|
|
80
|
+
undefined, // optional CommunicationRunEntity
|
|
81
|
+
false // previewOnly
|
|
61
82
|
);
|
|
62
83
|
|
|
63
84
|
if (result.Success) {
|
|
64
|
-
console.log('Message sent
|
|
65
|
-
} else {
|
|
66
|
-
console.error('Failed to send:', result.Error);
|
|
85
|
+
console.log('Message sent');
|
|
67
86
|
}
|
|
68
87
|
```
|
|
69
88
|
|
|
70
|
-
### Sending
|
|
89
|
+
### Sending to Multiple Recipients
|
|
71
90
|
|
|
72
91
|
```typescript
|
|
73
|
-
import { CommunicationEngine } from '@memberjunction/communication-engine';
|
|
74
|
-
import { Message } from '@memberjunction/communication-types';
|
|
75
|
-
import { Metadata } from '@memberjunction/core';
|
|
76
|
-
|
|
77
|
-
// Get template from metadata
|
|
78
|
-
const md = new Metadata();
|
|
79
|
-
const template = await md.GetEntityObject('Templates');
|
|
80
|
-
await template.LoadByName('Welcome Email');
|
|
81
|
-
|
|
82
|
-
// Create message with template
|
|
83
|
-
const message = new Message();
|
|
84
|
-
message.BodyTemplate = template;
|
|
85
|
-
message.To = 'user@example.com';
|
|
86
|
-
message.From = 'noreply@memberjunction.com';
|
|
87
|
-
message.ContextData = {
|
|
88
|
-
firstName: 'John',
|
|
89
|
-
accountType: 'Premium',
|
|
90
|
-
activationDate: new Date()
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
// Send the message - templates will be automatically processed
|
|
94
|
-
const result = await CommunicationEngine.Instance.SendSingleMessage(
|
|
95
|
-
'SendGrid',
|
|
96
|
-
'Email',
|
|
97
|
-
message
|
|
98
|
-
);
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### Batch Message Sending
|
|
102
|
-
|
|
103
|
-
```typescript
|
|
104
|
-
import { CommunicationEngine } from '@memberjunction/communication-engine';
|
|
105
|
-
import { Message, MessageRecipient } from '@memberjunction/communication-types';
|
|
106
|
-
|
|
107
|
-
// Create base message
|
|
108
|
-
const baseMessage = new Message();
|
|
109
|
-
baseMessage.Subject = 'Monthly Newsletter';
|
|
110
|
-
baseMessage.BodyTemplate = newsletterTemplate;
|
|
111
|
-
baseMessage.From = 'newsletter@memberjunction.com';
|
|
112
|
-
|
|
113
|
-
// Create recipient list with individual context data
|
|
114
92
|
const recipients: MessageRecipient[] = [
|
|
115
|
-
{
|
|
116
|
-
|
|
117
|
-
ContextData: { name: 'Alice', memberSince: '2023' }
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
To: 'user2@example.com',
|
|
121
|
-
ContextData: { name: 'Bob', memberSince: '2022' }
|
|
122
|
-
}
|
|
93
|
+
{ To: 'alice@example.com', FullName: 'Alice', ContextData: { role: 'admin' } },
|
|
94
|
+
{ To: 'bob@example.com', FullName: 'Bob', ContextData: { role: 'user' } }
|
|
123
95
|
];
|
|
124
96
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
baseMessage,
|
|
130
|
-
recipients
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
// Check results
|
|
134
|
-
results.forEach((result, index) => {
|
|
135
|
-
if (result.Success) {
|
|
136
|
-
console.log(`Sent to ${recipients[index].To}`);
|
|
137
|
-
} else {
|
|
138
|
-
console.error(`Failed for ${recipients[index].To}: ${result.Error}`);
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
### Preview Mode
|
|
144
|
-
|
|
145
|
-
You can preview processed messages without actually sending them:
|
|
97
|
+
const message = new Message();
|
|
98
|
+
message.From = 'noreply@example.com';
|
|
99
|
+
message.BodyTemplate = templateEntity; // uses template for personalization
|
|
100
|
+
message.Subject = 'Update';
|
|
146
101
|
|
|
147
|
-
|
|
148
|
-
const result = await CommunicationEngine.Instance.SendSingleMessage(
|
|
102
|
+
const results = await engine.SendMessages(
|
|
149
103
|
'SendGrid',
|
|
150
104
|
'Email',
|
|
151
105
|
message,
|
|
152
|
-
|
|
153
|
-
|
|
106
|
+
recipients,
|
|
107
|
+
false // previewOnly
|
|
154
108
|
);
|
|
155
|
-
|
|
156
|
-
if (result.Success) {
|
|
157
|
-
const processedMessage = result.Message;
|
|
158
|
-
console.log('Subject:', processedMessage.ProcessedSubject);
|
|
159
|
-
console.log('Body:', processedMessage.ProcessedBody);
|
|
160
|
-
console.log('HTML Body:', processedMessage.ProcessedHTMLBody);
|
|
161
|
-
}
|
|
109
|
+
// results is MessageResult[] - one per recipient
|
|
162
110
|
```
|
|
163
111
|
|
|
164
|
-
### Creating Draft
|
|
165
|
-
|
|
166
|
-
Create draft messages that can be edited and sent later (only supported by providers with mailbox access):
|
|
112
|
+
### Creating a Draft
|
|
167
113
|
|
|
168
114
|
```typescript
|
|
169
|
-
import { CommunicationEngine } from '@memberjunction/communication-engine';
|
|
170
|
-
import { Message } from '@memberjunction/communication-types';
|
|
171
|
-
|
|
172
|
-
// Get the engine instance
|
|
173
|
-
const engine = CommunicationEngine.Instance;
|
|
174
|
-
await engine.Config();
|
|
175
|
-
|
|
176
|
-
// Create a message
|
|
177
115
|
const message = new Message();
|
|
116
|
+
message.From = 'user@example.com';
|
|
178
117
|
message.To = 'recipient@example.com';
|
|
179
|
-
message.
|
|
180
|
-
message.
|
|
181
|
-
message.Body = 'This is a draft message that can be edited later';
|
|
118
|
+
message.Subject = 'Draft Email';
|
|
119
|
+
message.HTMLBody = '<p>Content here</p>';
|
|
182
120
|
|
|
183
|
-
// Create draft using Gmail (or 'Microsoft Graph')
|
|
184
121
|
const result = await engine.CreateDraft(
|
|
185
122
|
message,
|
|
186
|
-
'
|
|
123
|
+
'Microsoft Graph', // only providers with SupportsDrafts
|
|
187
124
|
contextUser
|
|
188
125
|
);
|
|
189
126
|
|
|
190
127
|
if (result.Success) {
|
|
191
|
-
console.log(`Draft
|
|
192
|
-
// Draft can now be edited or sent through the provider's native interface
|
|
193
|
-
} else {
|
|
194
|
-
console.error(`Failed to create draft: ${result.ErrorMessage}`);
|
|
128
|
+
console.log(`Draft ID: ${result.DraftID}`);
|
|
195
129
|
}
|
|
196
130
|
```
|
|
197
131
|
|
|
198
|
-
|
|
199
|
-
- **Gmail**: Drafts created in Gmail drafts folder
|
|
200
|
-
- **Microsoft Graph**: Drafts created in Outlook/Exchange drafts folder
|
|
201
|
-
|
|
202
|
-
**Providers NOT Supporting Drafts**:
|
|
203
|
-
- **SendGrid**: Service-based email, no mailbox
|
|
204
|
-
- **Twilio**: SMS/messaging service, no draft concept
|
|
132
|
+
### Per-Request Credentials
|
|
205
133
|
|
|
206
|
-
|
|
134
|
+
All send methods accept an optional `credentials` parameter for per-request credential overrides:
|
|
207
135
|
|
|
208
136
|
```typescript
|
|
209
|
-
|
|
210
|
-
const provider = CommunicationEngine.Instance.GetProvider('SendGrid');
|
|
211
|
-
|
|
212
|
-
// List all available providers
|
|
213
|
-
const providers = CommunicationEngine.Instance.Providers;
|
|
214
|
-
providers.forEach(p => {
|
|
215
|
-
console.log(`Provider: ${p.Name}`);
|
|
216
|
-
p.MessageTypes.forEach(mt => {
|
|
217
|
-
console.log(` - ${mt.Name}`);
|
|
218
|
-
});
|
|
219
|
-
});
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
## API Reference
|
|
223
|
-
|
|
224
|
-
### CommunicationEngine
|
|
225
|
-
|
|
226
|
-
#### Properties
|
|
227
|
-
|
|
228
|
-
- `Instance: CommunicationEngine` - Static singleton instance
|
|
229
|
-
- `Providers: CommunicationProviderEntity[]` - List of configured providers
|
|
230
|
-
- `Loaded: boolean` - Whether metadata has been loaded
|
|
231
|
-
|
|
232
|
-
#### Methods
|
|
233
|
-
|
|
234
|
-
##### `Config(contextUser?: UserInfo): Promise<void>`
|
|
235
|
-
Initializes the engine with metadata. Must be called before using other methods.
|
|
137
|
+
import { SendGridCredentials } from '@memberjunction/communication-sendgrid';
|
|
236
138
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
- `message`: The message to send
|
|
247
|
-
- `run`: Optional communication run for grouping messages
|
|
248
|
-
- `previewOnly`: If true, processes templates but doesn't send
|
|
249
|
-
|
|
250
|
-
##### `SendMessages(providerName: string, providerMessageTypeName: string, message: Message, recipients: MessageRecipient[], previewOnly?: boolean): Promise<MessageResult[]>`
|
|
251
|
-
Sends messages to multiple recipients in a single run.
|
|
252
|
-
|
|
253
|
-
##### `CreateDraft(message: Message, providerName: string, contextUser?: UserInfo): Promise<CreateDraftResult>`
|
|
254
|
-
Creates a draft message using the specified provider.
|
|
255
|
-
|
|
256
|
-
Parameters:
|
|
257
|
-
- `message`: The message to save as a draft
|
|
258
|
-
- `providerName`: Name of the provider (must support drafts, e.g., 'Gmail', 'Microsoft Graph')
|
|
259
|
-
- `contextUser`: Optional user context for server-side operations
|
|
260
|
-
|
|
261
|
-
Returns a `CreateDraftResult` with:
|
|
262
|
-
- `Success`: Whether the draft was created successfully
|
|
263
|
-
- `DraftID`: The provider-specific draft identifier (if successful)
|
|
264
|
-
- `ErrorMessage`: Error details (if failed)
|
|
265
|
-
|
|
266
|
-
**Note**: Only providers with mailbox access support drafts (Gmail, MS Graph). Service-based providers (SendGrid, Twilio) will return an error.
|
|
139
|
+
const result = await engine.SendSingleMessage(
|
|
140
|
+
'SendGrid',
|
|
141
|
+
'Email',
|
|
142
|
+
message,
|
|
143
|
+
undefined,
|
|
144
|
+
false,
|
|
145
|
+
{ apiKey: 'SG.customer-specific-key' } // per-request credentials
|
|
146
|
+
);
|
|
147
|
+
```
|
|
267
148
|
|
|
268
149
|
### ProcessedMessageServer
|
|
269
150
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
## Error Handling
|
|
292
|
-
|
|
293
|
-
The engine provides detailed error messages for common scenarios:
|
|
294
|
-
|
|
295
|
-
- Provider not found
|
|
296
|
-
- Provider message type not found
|
|
297
|
-
- Template rendering failures
|
|
298
|
-
- Communication run failures
|
|
299
|
-
- Missing required template content types
|
|
300
|
-
|
|
301
|
-
Always wrap communication calls in try-catch blocks:
|
|
302
|
-
|
|
303
|
-
```typescript
|
|
304
|
-
try {
|
|
305
|
-
const result = await CommunicationEngine.Instance.SendSingleMessage(...);
|
|
306
|
-
if (!result.Success) {
|
|
307
|
-
// Handle send failure
|
|
308
|
-
console.error(result.Error);
|
|
309
|
-
}
|
|
310
|
-
} catch (error) {
|
|
311
|
-
// Handle engine errors
|
|
312
|
-
console.error('Engine error:', error.message);
|
|
313
|
-
}
|
|
151
|
+
Server-side implementation of `ProcessedMessage` that renders templates using `TemplateEngineServer`. Automatically processes body, HTML body, and subject templates with the provided context data.
|
|
152
|
+
|
|
153
|
+
```mermaid
|
|
154
|
+
sequenceDiagram
|
|
155
|
+
participant App as Application
|
|
156
|
+
participant CE as CommunicationEngine
|
|
157
|
+
participant PMS as ProcessedMessageServer
|
|
158
|
+
participant TE as TemplateEngineServer
|
|
159
|
+
participant P as Provider
|
|
160
|
+
|
|
161
|
+
App->>CE: SendSingleMessage(providerName, messageType, message)
|
|
162
|
+
CE->>CE: GetProvider(providerName)
|
|
163
|
+
CE->>PMS: new ProcessedMessageServer(message)
|
|
164
|
+
CE->>PMS: Process()
|
|
165
|
+
PMS->>TE: RenderTemplate(bodyTemplate, contextData)
|
|
166
|
+
TE-->>PMS: rendered content
|
|
167
|
+
PMS-->>CE: ProcessResult
|
|
168
|
+
CE->>P: SendSingleMessage(processedMessage, credentials)
|
|
169
|
+
P-->>CE: MessageResult
|
|
170
|
+
CE-->>App: MessageResult
|
|
314
171
|
```
|
|
315
172
|
|
|
316
|
-
##
|
|
317
|
-
|
|
318
|
-
- `@memberjunction/global`: Core MemberJunction utilities
|
|
319
|
-
- `@memberjunction/core`: Core MemberJunction functionality
|
|
320
|
-
- `@memberjunction/templates`: Template engine integration
|
|
321
|
-
- `@memberjunction/core-entities`: Entity definitions
|
|
322
|
-
- `@memberjunction/communication-types`: Type definitions
|
|
323
|
-
- `rxjs`: Reactive Extensions for JavaScript
|
|
324
|
-
|
|
325
|
-
## Integration with Other MJ Packages
|
|
326
|
-
|
|
327
|
-
This package integrates seamlessly with:
|
|
173
|
+
## API Reference
|
|
328
174
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
175
|
+
| Method | Description |
|
|
176
|
+
|--------|-------------|
|
|
177
|
+
| `Config(forceRefresh, contextUser, provider)` | Initialize the engine and load metadata |
|
|
178
|
+
| `GetProvider(providerName)` | Retrieve a provider instance from the class factory |
|
|
179
|
+
| `SendSingleMessage(provider, type, message, run?, preview?, credentials?)` | Send one message |
|
|
180
|
+
| `SendMessages(provider, type, message, recipients, preview?, credentials?)` | Send to multiple recipients |
|
|
181
|
+
| `CreateDraft(message, providerName, contextUser?, credentials?)` | Create a draft message |
|
|
332
182
|
|
|
333
|
-
##
|
|
183
|
+
## Dependencies
|
|
334
184
|
|
|
335
|
-
|
|
185
|
+
| Package | Purpose |
|
|
186
|
+
|---------|---------|
|
|
187
|
+
| `@memberjunction/communication-types` | Base engine, provider, and message types |
|
|
188
|
+
| `@memberjunction/core` | UserInfo, logging, metadata access |
|
|
189
|
+
| `@memberjunction/core-entities` | CommunicationRunEntity and related entities |
|
|
190
|
+
| `@memberjunction/global` | MJGlobal class factory for provider instantiation |
|
|
191
|
+
| `@memberjunction/templates` | Server-side template rendering engine |
|
|
336
192
|
|
|
337
|
-
##
|
|
193
|
+
## Development
|
|
338
194
|
|
|
339
|
-
|
|
195
|
+
```bash
|
|
196
|
+
npm run build # Compile TypeScript
|
|
197
|
+
npm start # Watch mode
|
|
198
|
+
```
|