@peopl-health/nexus 1.2.0 → 1.3.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/MIGRATION_GUIDE.md +71 -379
- package/README.md +80 -559
- package/lib/adapters/BaileysProvider.js +25 -1
- package/lib/adapters/TwilioProvider.js +107 -0
- package/lib/adapters/registry.js +29 -0
- package/lib/config/configLoader.js +38 -0
- package/lib/controllers/assistantController.js +3 -3
- package/lib/controllers/messageController.js +31 -1
- package/lib/core/NexusMessaging.js +151 -35
- package/lib/helpers/assistantHelper.js +6 -6
- package/lib/helpers/baileysHelper.js +3 -3
- package/lib/helpers/twilioHelper.js +3 -3
- package/lib/index.d.ts +1 -1
- package/lib/index.js +84 -9
- package/lib/interactive/index.js +86 -0
- package/lib/interactive/registry.js +31 -0
- package/lib/interactive/twilioMapper.js +60 -0
- package/lib/models/messageModel.js +8 -0
- package/lib/routes/index.js +3 -1
- package/lib/services/airtableService.js +4 -0
- package/lib/services/assistantService.js +16 -2
- package/lib/storage/NoopStorage.js +19 -0
- package/lib/storage/registry.js +31 -0
- package/lib/templates/predefinedTemplates.js +1 -3
- package/lib/utils/defaultLLMProvider.js +1 -1
- package/lib/utils/index.js +3 -5
- package/package.json +2 -1
package/MIGRATION_GUIDE.md
CHANGED
|
@@ -1,388 +1,80 @@
|
|
|
1
|
-
# Migration Guide
|
|
2
|
-
|
|
3
|
-
This guide
|
|
4
|
-
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
##
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
### 3. Migrate Message Handlers
|
|
58
|
-
|
|
59
|
-
**Before:**
|
|
60
|
-
```javascript
|
|
61
|
-
// Scattered across multiple files
|
|
62
|
-
async function processIncomingMessage(twilioClient, message) {
|
|
63
|
-
// Complex processing logic
|
|
64
|
-
}
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
**After:**
|
|
68
|
-
```javascript
|
|
69
|
-
nexus.setHandlers({
|
|
70
|
-
onMessage: async (messageData, nexus) => {
|
|
71
|
-
// Your message logic here
|
|
72
|
-
},
|
|
73
|
-
onCommand: async (messageData, nexus) => {
|
|
74
|
-
// Your command logic here
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
### 4. Migrate Assistant Integration
|
|
80
|
-
|
|
81
|
-
**Before:**
|
|
82
|
-
```javascript
|
|
83
|
-
// src/services/assistantService.js
|
|
84
|
-
const { replyAssistant } = require('./assistantService');
|
|
85
|
-
// Complex assistant management
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
**After:**
|
|
89
|
-
```javascript
|
|
90
|
-
await nexus.initialize({
|
|
91
|
-
assistant: {
|
|
92
|
-
llmClient: openai,
|
|
93
|
-
assistants: {
|
|
94
|
-
'SUPPORT_ASST': 'asst_abc123'
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
// Use assistants
|
|
100
|
-
const response = await nexus.sendToAssistant(userId, message);
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### 5. Migrate Database Models
|
|
104
|
-
|
|
105
|
-
**Before:**
|
|
106
|
-
```javascript
|
|
107
|
-
// src/models/messageModel.js
|
|
108
|
-
const Message = mongoose.model('Message', messageSchema);
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
**After:**
|
|
112
|
-
```javascript
|
|
113
|
-
// Models are handled automatically by the library
|
|
114
|
-
// Access via nexus.getStorage() if needed
|
|
115
|
-
const storage = nexus.getStorage();
|
|
116
|
-
const messages = await storage.getMessages(userId);
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
## Configuration Mapping
|
|
120
|
-
|
|
121
|
-
### Environment Variables
|
|
122
|
-
|
|
123
|
-
**Before:**
|
|
124
|
-
```env
|
|
125
|
-
TWILIO_ACCOUNT_SID=your_sid
|
|
126
|
-
TWILIO_AUTH_TOKEN=your_token
|
|
127
|
-
MONGO_URI=mongodb://localhost:27017/db
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
**After:**
|
|
131
|
-
```javascript
|
|
1
|
+
# Migration Guide
|
|
2
|
+
|
|
3
|
+
This guide summarizes changes introduced in the current Nexus library refresh to help you migrate quickly with minimal breakage.
|
|
4
|
+
|
|
5
|
+
## TL;DR
|
|
6
|
+
- Twilio remains first‑class; Baileys is supported for messaging but not for templates/flows (returns clear errors).
|
|
7
|
+
- Message sends accept both `to` (preferred) and legacy `code` (normalized internally).
|
|
8
|
+
- Templates and flows are supported through Twilio Content API; provider is auto‑injected into template controllers on `Nexus.initialize()` when Twilio is active.
|
|
9
|
+
- New event bus + middleware model; existing handlers continue to work.
|
|
10
|
+
- Storage adapter registry; use built‑in `mongo`/`noop` or plug your own adapter or instance.
|
|
11
|
+
- Interactive/flows now have a provider‑agnostic API with Twilio mapping and event‑driven routing.
|
|
12
|
+
- Assistant registry with optional override for `getAssistantById`.
|
|
13
|
+
|
|
14
|
+
## Messaging API
|
|
15
|
+
- BREAKING (soft): Prefer `to` over `code` in `sendMessage`. Legacy `code` is still accepted and normalized internally.
|
|
16
|
+
|
|
17
|
+
Before:
|
|
18
|
+
```js
|
|
19
|
+
await nexus.sendMessage({ code: 'whatsapp:+521555...', message: 'Hi' });
|
|
20
|
+
```
|
|
21
|
+
After (preferred):
|
|
22
|
+
```js
|
|
23
|
+
await nexus.sendMessage({ to: '+521555...', message: 'Hi' });
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Templates & Flows (Twilio)
|
|
27
|
+
- Twilio provider now implements content operations: `listTemplates`, `getTemplate`, `createTemplate`, `deleteTemplate`, `submitForApproval`, `checkApprovalStatus`.
|
|
28
|
+
- On `Nexus.initialize()` with Twilio, the provider is auto‑injected into template controllers — default routes under `/api/template` work immediately.
|
|
29
|
+
- Baileys: content/template operations are not supported; a clear error is thrown if called.
|
|
30
|
+
|
|
31
|
+
## Interactive & Flows (Provider‑agnostic)
|
|
32
|
+
- New helper APIs:
|
|
33
|
+
- `registerFlow(id, spec)`, `sendInteractive(nexus, { to, id | spec, variables })`.
|
|
34
|
+
- Twilio: spec is converted to Content API payload and sent; if `spec.contentSid` is provided, it is used directly.
|
|
35
|
+
- Baileys: currently unsupported.
|
|
36
|
+
- Event‑driven routing:
|
|
37
|
+
- `registerInteractiveHandler(match, handler)` and `attachInteractiveRouter(nexus)`; handlers are called when interactive messages are received.
|
|
38
|
+
|
|
39
|
+
## Middleware & Events
|
|
40
|
+
- New event bus on `NexusMessaging`: subscribe to `*:received` and `*:handled` (message, media, interactive, command, keyword, flow).
|
|
41
|
+
- New middleware: `nexus.getMessaging().use(type?, async (msg, nexus, next) => { ... })`.
|
|
42
|
+
- Existing `setHandlers` and `onMessage`/`onInteractive`/etc. continue to work.
|
|
43
|
+
|
|
44
|
+
## Storage
|
|
45
|
+
- Storage is now pluggable via a registry:
|
|
46
|
+
- Built‑ins: `mongo` (default), `noop`.
|
|
47
|
+
- Register your adapter: `registerStorage('src', MyStorageClass)` then `storage: 'src'`.
|
|
48
|
+
- Or pass an instance: `storage: new MyStorageClass()`.
|
|
49
|
+
- If the adapter has `connect()`, Nexus calls it automatically.
|
|
50
|
+
- Controllers that rely on your Mongo models can continue to do so — no change required.
|
|
51
|
+
|
|
52
|
+
## Assistants
|
|
53
|
+
- Register assistant classes at init:
|
|
54
|
+
```js
|
|
132
55
|
await nexus.initialize({
|
|
133
56
|
provider: 'twilio',
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
},
|
|
139
|
-
storage: 'mongo',
|
|
140
|
-
storageConfig: {
|
|
141
|
-
mongoUri: process.env.MONGO_URI,
|
|
142
|
-
dbName: 'your_db_name'
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
### Assistant Configuration
|
|
148
|
-
|
|
149
|
-
**Before:**
|
|
150
|
-
```javascript
|
|
151
|
-
// src/config/assistantConfig.js
|
|
152
|
-
module.exports = {
|
|
153
|
-
SALES_ASST: "asst_HjYiVBQ8ye1yl1Iu9mcqF83b",
|
|
154
|
-
DOCTOR_SCHEDULE_ASST: "asst_5cw2tRHI7ze6FHgtlId6ideH"
|
|
155
|
-
};
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
**After:**
|
|
159
|
-
```javascript
|
|
160
|
-
await nexus.initialize({
|
|
161
|
-
assistant: {
|
|
162
|
-
llmClient: openai,
|
|
163
|
-
assistants: {
|
|
164
|
-
'SALES_ASST': process.env.SALES_ASSISTANT_ID,
|
|
165
|
-
'DOCTOR_SCHEDULE_ASST': process.env.DOCTOR_SCHEDULE_ASSISTANT_ID
|
|
166
|
-
}
|
|
57
|
+
llm: 'openai', llmConfig: { apiKey: process.env.OPENAI_API_KEY },
|
|
58
|
+
assistants: {
|
|
59
|
+
registry: { SUPPORT: SupportAssistantClass, SALES: SalesAssistantClass },
|
|
60
|
+
getAssistantById: (id, thread) => null // optional override
|
|
167
61
|
}
|
|
168
62
|
});
|
|
169
63
|
```
|
|
64
|
+
- An internal override for `getAssistantById` is supported; if provided, it is tried first, then fallback to registry.
|
|
170
65
|
|
|
171
|
-
##
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
```javascript
|
|
177
|
-
const { sendMessage } = require('./messaging/messageService');
|
|
178
|
-
await sendMessage(twilioClient, messageData);
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
**After:**
|
|
182
|
-
```javascript
|
|
183
|
-
await nexus.sendMessage({
|
|
184
|
-
to: '+1234567890',
|
|
185
|
-
message: 'Hello World!',
|
|
186
|
-
fileUrl: 'https://example.com/file.pdf',
|
|
187
|
-
fileType: 'document'
|
|
188
|
-
});
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
### 2. Template Messages
|
|
192
|
-
|
|
193
|
-
**Before:**
|
|
194
|
-
```javascript
|
|
195
|
-
await sendMessage(twilioClient, {
|
|
196
|
-
code: phoneNumber,
|
|
197
|
-
contentSid: templateId,
|
|
198
|
-
variables: { '1': 'John', '2': 'Doe' }
|
|
199
|
-
});
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
**After:**
|
|
203
|
-
```javascript
|
|
204
|
-
await nexus.sendMessage({
|
|
205
|
-
to: phoneNumber,
|
|
206
|
-
contentSid: templateId,
|
|
207
|
-
variables: { '1': 'John', '2': 'Doe' }
|
|
208
|
-
});
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
### 3. Interactive Messages
|
|
212
|
-
|
|
213
|
-
**Before:**
|
|
214
|
-
```javascript
|
|
215
|
-
// src/adapters/interactive/processButton.js
|
|
216
|
-
async function processButtonPayload(twilioClient, message) {
|
|
217
|
-
// Complex button handling
|
|
218
|
-
}
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
**After:**
|
|
222
|
-
```javascript
|
|
223
|
-
nexus.setHandlers({
|
|
224
|
-
onInteractive: async (messageData, nexus) => {
|
|
225
|
-
const { type, payload } = messageData.interactive;
|
|
226
|
-
|
|
227
|
-
if (type === 'button') {
|
|
228
|
-
// Handle button click
|
|
229
|
-
await nexus.sendMessage({
|
|
230
|
-
to: messageData.from,
|
|
231
|
-
message: `You clicked: ${payload}`
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
});
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
### 4. Command Processing
|
|
239
|
-
|
|
240
|
-
**Before:**
|
|
241
|
-
```javascript
|
|
242
|
-
// Hardcoded command processing in multiple files
|
|
243
|
-
if (message.Body.startsWith('/help')) {
|
|
244
|
-
// Handle help command
|
|
245
|
-
}
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
**After:**
|
|
249
|
-
```javascript
|
|
250
|
-
nexus.setHandlers({
|
|
251
|
-
onCommand: async (messageData, nexus) => {
|
|
252
|
-
const { command, args } = messageData.command;
|
|
253
|
-
|
|
254
|
-
switch (command) {
|
|
255
|
-
case 'help':
|
|
256
|
-
await nexus.sendMessage({
|
|
257
|
-
to: messageData.from,
|
|
258
|
-
message: 'Available commands: /help, /status'
|
|
259
|
-
});
|
|
260
|
-
break;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
});
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
### 5. Keyword Detection
|
|
267
|
-
|
|
268
|
-
**Before:**
|
|
269
|
-
```javascript
|
|
270
|
-
// Hardcoded keyword detection
|
|
271
|
-
const keywords = ['hola', 'dr.', 'bienvenida'];
|
|
272
|
-
if (keywords.some(k => message.Body.includes(k))) {
|
|
273
|
-
// Handle keywords
|
|
274
|
-
}
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
**After:**
|
|
278
|
-
```javascript
|
|
279
|
-
await nexus.initialize({
|
|
280
|
-
parserConfig: {
|
|
281
|
-
keywords: ['hola', 'dr.', 'bienvenida', 'help', 'support']
|
|
282
|
-
}
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
nexus.setHandlers({
|
|
286
|
-
onKeyword: async (messageData, nexus) => {
|
|
287
|
-
const keyword = messageData.keyword;
|
|
288
|
-
// Handle keyword detection
|
|
289
|
-
}
|
|
290
|
-
});
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
## Webhook Migration
|
|
294
|
-
|
|
295
|
-
### Before (Express Routes)
|
|
296
|
-
```javascript
|
|
297
|
-
// src/routes/index.js
|
|
298
|
-
app.post('/twilio/webhook', async (req, res) => {
|
|
299
|
-
await processIncomingMessage(twilioClient, req.body);
|
|
300
|
-
res.sendStatus(200);
|
|
301
|
-
});
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
### After (Simplified)
|
|
305
|
-
```javascript
|
|
306
|
-
app.post('/webhook/twilio', async (req, res) => {
|
|
307
|
-
await nexus.processMessage(req.body);
|
|
308
|
-
res.sendStatus(200);
|
|
309
|
-
});
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
## Benefits After Migration
|
|
313
|
-
|
|
314
|
-
### ✅ Reduced Code Complexity
|
|
315
|
-
- **Before**: ~2000+ lines across multiple files
|
|
316
|
-
- **After**: ~200 lines for equivalent functionality
|
|
317
|
-
|
|
318
|
-
### ✅ Better Maintainability
|
|
319
|
-
- Centralized configuration
|
|
320
|
-
- Clear separation of concerns
|
|
321
|
-
- Standardized error handling
|
|
322
|
-
|
|
323
|
-
### ✅ Enhanced Flexibility
|
|
324
|
-
- Easy provider switching (Twilio ↔ Baileys)
|
|
325
|
-
- Configurable message parsing
|
|
326
|
-
- Pluggable storage backends
|
|
327
|
-
|
|
328
|
-
### ✅ Improved Testing
|
|
329
|
-
- Mockable components
|
|
330
|
-
- Isolated business logic
|
|
331
|
-
- Clear interfaces
|
|
332
|
-
|
|
333
|
-
## Common Migration Issues
|
|
334
|
-
|
|
335
|
-
### 1. **Missing Dependencies**
|
|
336
|
-
```bash
|
|
337
|
-
# Install missing peer dependencies
|
|
338
|
-
npm install twilio baileys openai
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
### 2. **Configuration Errors**
|
|
342
|
-
```javascript
|
|
343
|
-
// Ensure all required config is provided
|
|
344
|
-
await nexus.initialize({
|
|
345
|
-
provider: 'twilio',
|
|
346
|
-
providerConfig: {
|
|
347
|
-
accountSid: 'required',
|
|
348
|
-
authToken: 'required',
|
|
349
|
-
whatsappNumber: 'required'
|
|
350
|
-
}
|
|
351
|
-
});
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
### 3. **Handler Registration**
|
|
355
|
-
```javascript
|
|
356
|
-
// Set handlers AFTER initialization
|
|
357
|
-
await nexus.initialize(config);
|
|
358
|
-
nexus.setHandlers(handlers); // ✅ Correct order
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
### 4. **Database Connection**
|
|
362
|
-
```javascript
|
|
363
|
-
// Storage is handled automatically
|
|
364
|
-
await nexus.initialize({
|
|
365
|
-
storage: 'mongo',
|
|
366
|
-
storageConfig: {
|
|
367
|
-
mongoUri: 'mongodb://localhost:27017/db',
|
|
368
|
-
dbName: 'your_db'
|
|
369
|
-
}
|
|
370
|
-
});
|
|
371
|
-
// No need to call connectMongoose() manually
|
|
372
|
-
```
|
|
373
|
-
|
|
374
|
-
## Next Steps
|
|
375
|
-
|
|
376
|
-
1. **Start Small**: Migrate one feature at a time
|
|
377
|
-
2. **Test Thoroughly**: Verify each migrated component works
|
|
378
|
-
3. **Remove Old Code**: Clean up original files after successful migration
|
|
379
|
-
4. **Optimize**: Use library features to simplify your code further
|
|
66
|
+
## Utilities & Fixes
|
|
67
|
+
- Utils index corrected to export existing files: `{ DefaultLLMProvider, MessageParser, logger }`.
|
|
68
|
+
- Default OpenAI import fixed for CommonJS: `const OpenAI = require('openai');`.
|
|
69
|
+
- Message model helpers unified; `LegacyMessage` references replaced with `Message` and a new exported `insertMessage`.
|
|
70
|
+
- Types `declare module` now matches package name: `@peopl-health/nexus`.
|
|
380
71
|
|
|
381
|
-
##
|
|
72
|
+
## Routes
|
|
73
|
+
- Built‑in route bundles remain available and importable via `setupDefaultRoutes(app)` or per‑group via `routes` + `createRouter()`.
|
|
382
74
|
|
|
383
|
-
|
|
384
|
-
-
|
|
385
|
-
- Review [examples/](./examples/) for complete implementations
|
|
386
|
-
- Refer to the original codebase for complex business logic patterns
|
|
75
|
+
## Testing
|
|
76
|
+
- If your environment restricts forking, run Jest in‑band: `jest --runInBand`.
|
|
387
77
|
|
|
388
|
-
|
|
78
|
+
## Notes
|
|
79
|
+
- Twilio approvals data shape varies by account/region. The provider looks up `content.links` and falls back to the documented REST path; data is normalized where possible.
|
|
80
|
+
- Baileys: templates/flows remain unsupported; messaging and media send/receive are supported.
|