backend-manager 5.0.184 → 5.0.186
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/CHANGELOG.md
CHANGED
|
@@ -14,6 +14,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
|
14
14
|
- `Fixed` for any bug fixes.
|
|
15
15
|
- `Security` in case of vulnerabilities.
|
|
16
16
|
|
|
17
|
+
# [5.0.186] - 2026-04-01
|
|
18
|
+
### Fixed
|
|
19
|
+
- Move markdown rendering and UTM link tagging to run after `_.merge()` so caller overrides to `body.message` and `email.body` are properly processed
|
|
20
|
+
|
|
21
|
+
# [5.0.185] - 2026-04-01
|
|
22
|
+
### Changed
|
|
23
|
+
- Use `_.merge` for dynamic template data so callers can override any nested field (e.g. `email.preview`, `personalization.name`, `data.body.*`)
|
|
24
|
+
- Set email schema `template` default to `'default'` instead of `undefined`
|
|
25
|
+
|
|
17
26
|
# [5.0.184] - 2026-03-31
|
|
18
27
|
### Changed
|
|
19
28
|
- Renamed email template shortcuts from `main/` to `core/` prefix across constants and all consumer files
|
package/TODO-2.md
CHANGED
|
@@ -10,10 +10,26 @@ payments/reactivate
|
|
|
10
10
|
payments/upgrade
|
|
11
11
|
* takes a subscription id and a new plan id and upgrades the user's subscription to the new plan. this can only be done if the user has an active subscription.
|
|
12
12
|
|
|
13
|
+
I'd call that cancellation-reversed — it pairs naturally with the existing cancellation-requested transition and clearly communicates the action (they reversed/undid the pending cancellation).
|
|
14
|
+
|
|
15
|
+
on the email we send, for payment recovered what is the category
|
|
16
|
+
Let me check the handler.
|
|
17
|
+
|
|
18
|
+
Read payment-recovered.js
|
|
19
|
+
The category is order/payment-recovered (line 13).
|
|
20
|
+
|
|
21
|
+
TODO-2.md
|
|
22
|
+
waht about when they request a cancel
|
|
23
|
+
Read cancellation-requested.js
|
|
24
|
+
The category is order/cancellation-requested (line 13).
|
|
25
|
+
|
|
13
26
|
---
|
|
14
27
|
GHOSTII REVAMP
|
|
15
28
|
* better logic for generating posts. better model? claude?
|
|
16
29
|
|
|
30
|
+
---- MCP
|
|
31
|
+
* ability for consuming prjec to specify MCP functions
|
|
32
|
+
|
|
17
33
|
-------
|
|
18
34
|
UPSELL
|
|
19
35
|
* products in BEM can have an UPSELL where you link another product ID and it allows you to add it to your cart OR shows you after checkout?
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
https://github.com/disposable-email-domains/disposable-email-domains?tab=readme-ov-file
|
|
2
|
+
https://www.npmjs.com/package/disposable-domains
|
|
3
|
+
https://github.com/tompec/disposable-email-domains
|
|
4
|
+
|
|
5
|
+
Two repos:
|
|
6
|
+
|
|
7
|
+
Repo Domains Approach
|
|
8
|
+
disposable-email-domains/disposable-email-domains 5,359 Curated, conservative, high confidence
|
|
9
|
+
ivolo/disposable-email-domains 121,569 Aggressive, aggregated from many sources, more false positives
|
|
10
|
+
Our current list has 854 — so even the smaller curated list is 6x larger.
|
|
11
|
+
|
|
12
|
+
For our use case (currently only used to skip marketing sync, not blocking signups), I'd recommend the 5,359 curated list — it's comprehensive enough without being overly aggressive. And if we ever do use it for blocking signups, the false positive risk is much lower.
|
|
13
|
+
|
|
14
|
+
Want to swap to that one?
|
package/package.json
CHANGED
|
@@ -188,55 +188,60 @@ Transactional.prototype.build = async function (settings) {
|
|
|
188
188
|
signoff.urlText = signoff.urlText || '@ianwieds';
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
-
//
|
|
192
|
-
if (settings?.data?.body?.message) {
|
|
193
|
-
settings.data.body.message = md.render(settings.data.body.message);
|
|
194
|
-
}
|
|
195
|
-
if (settings?.data?.email?.body) {
|
|
196
|
-
settings.data.email.body = md.render(settings.data.email.body);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Tag links with UTM params for attribution
|
|
200
|
-
const utmOptions = {
|
|
201
|
-
brandUrl: brand?.url,
|
|
202
|
-
brandId: brand?.id,
|
|
203
|
-
campaign: settings.sender || templateId,
|
|
204
|
-
type: 'transactional',
|
|
205
|
-
utm: settings.utm,
|
|
206
|
-
};
|
|
207
|
-
|
|
208
|
-
if (settings?.data?.body?.message) {
|
|
209
|
-
settings.data.body.message = tagLinks(settings.data.body.message, utmOptions);
|
|
210
|
-
}
|
|
211
|
-
if (settings?.data?.email?.body) {
|
|
212
|
-
settings.data.email.body = tagLinks(settings.data.email.body, utmOptions);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Build dynamic template data
|
|
191
|
+
// Build dynamic template data defaults
|
|
216
192
|
const dynamicTemplateData = {
|
|
217
193
|
email: {
|
|
218
194
|
id: Manager.require('uuid').v4(),
|
|
219
|
-
subject
|
|
220
|
-
preview:
|
|
221
|
-
body:
|
|
195
|
+
subject,
|
|
196
|
+
preview: null,
|
|
197
|
+
body: null,
|
|
222
198
|
unsubscribeUrl,
|
|
223
199
|
categories,
|
|
224
200
|
footer: {
|
|
225
|
-
text:
|
|
201
|
+
text: null,
|
|
226
202
|
},
|
|
227
203
|
carbonCopy: copy,
|
|
228
204
|
},
|
|
229
205
|
personalization: {
|
|
230
206
|
email: to[0].email,
|
|
231
207
|
name: to[0].name,
|
|
232
|
-
...settings?.data?.personalization,
|
|
233
208
|
},
|
|
234
209
|
signoff,
|
|
235
210
|
brand: brandData,
|
|
236
211
|
user: userProperties,
|
|
237
|
-
data:
|
|
212
|
+
data: {},
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
// Deep-merge caller's data on top so they can override any field
|
|
216
|
+
// (e.g. email.preview, email.subject, personalization.name, data.body.*, etc.)
|
|
217
|
+
if (settings.data) {
|
|
218
|
+
_.merge(dynamicTemplateData, settings.data);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Process markdown in body fields (after merge so all data paths are resolved)
|
|
222
|
+
if (dynamicTemplateData.data?.body?.message) {
|
|
223
|
+
dynamicTemplateData.data.body.message = md.render(dynamicTemplateData.data.body.message);
|
|
224
|
+
}
|
|
225
|
+
if (dynamicTemplateData.email?.body) {
|
|
226
|
+
dynamicTemplateData.email.body = md.render(dynamicTemplateData.email.body);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Tag links with UTM params for attribution
|
|
230
|
+
const utmOptions = {
|
|
231
|
+
brandUrl: brand?.url,
|
|
232
|
+
brandId: brand?.id,
|
|
233
|
+
campaign: settings.sender || templateId,
|
|
234
|
+
type: 'transactional',
|
|
235
|
+
utm: settings.utm,
|
|
238
236
|
};
|
|
239
237
|
|
|
238
|
+
if (dynamicTemplateData.data?.body?.message) {
|
|
239
|
+
dynamicTemplateData.data.body.message = tagLinks(dynamicTemplateData.data.body.message, utmOptions);
|
|
240
|
+
}
|
|
241
|
+
if (dynamicTemplateData.email?.body) {
|
|
242
|
+
dynamicTemplateData.email.body = tagLinks(dynamicTemplateData.email.body, utmOptions);
|
|
243
|
+
}
|
|
244
|
+
|
|
240
245
|
// Build the email object
|
|
241
246
|
const email = {
|
|
242
247
|
to,
|
|
@@ -15,7 +15,7 @@ module.exports = () => ({
|
|
|
15
15
|
replyTo: { types: ['string'], default: undefined },
|
|
16
16
|
sender: { types: ['string'], default: undefined },
|
|
17
17
|
subject: { types: ['string'], default: undefined },
|
|
18
|
-
template: { types: ['string'], default:
|
|
18
|
+
template: { types: ['string'], default: 'default' },
|
|
19
19
|
group: { types: ['number', 'string'], default: undefined },
|
|
20
20
|
sendAt: { types: ['number', 'string'], default: undefined },
|
|
21
21
|
data: { types: ['object'], default: {} },
|