backend-manager 5.0.159 → 5.0.161

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,21 @@ 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.161] - 2026-03-18
18
+ ### Added
19
+ - Port conflict detection in `serve` command — checks and kills blocking processes before starting Firebase server
20
+
21
+ ### Changed
22
+ - Unblocked common team/role email local parts (`user`, `email`, `mail`, `hello`, `info`, `admin`, `support`, `contact`) from validation blocklist, as these are legitimate addresses
23
+
24
+ # [5.0.160] - 2026-03-18
25
+ ### Added
26
+ - Beehiiv `resolveSegmentIds()` — fetches segments from API, builds name→ID cache (same pattern as SendGrid)
27
+ - Beehiiv segment resolution in `sendCampaign()` — SSOT keys auto-translate to Beehiiv segment IDs
28
+
29
+ ### Changed
30
+ - Beehiiv `createPost()` now receives resolved segment IDs instead of raw SSOT keys
31
+
17
32
  # [5.0.159] - 2026-03-18
18
33
  ### Added
19
34
  - Audience-specific email discount codes: `UPGRADE15`, `COMEBACK20`, `MISSYOU25`, `TRYAGAIN10` with eligibility validation per user
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backend-manager",
3
- "version": "5.0.159",
3
+ "version": "5.0.161",
4
4
  "description": "Quick tools for developing Firebase functions",
5
5
  "main": "src/manager/index.js",
6
6
  "bin": {
@@ -11,6 +11,12 @@ class ServeCommand extends BaseCommand {
11
11
  const port = self.argv.port || self.argv?._?.[1] || '5000';
12
12
  const projectDir = self.firebaseProjectPath;
13
13
 
14
+ // Check for port conflicts before starting server
15
+ const canProceed = await this.checkAndKillBlockingProcesses({ serving: parseInt(port, 10) });
16
+ if (!canProceed) {
17
+ throw new Error('Port conflicts could not be resolved');
18
+ }
19
+
14
20
  // Start BEM watcher in background
15
21
  const watcher = new WatchCommand(self);
16
22
  watcher.startBackground();
@@ -317,7 +317,14 @@ Marketing.prototype.sendCampaign = async function (settings) {
317
317
  };
318
318
  }
319
319
 
320
- // Beehiiv: segment resolution will go here when Beehiiv segments are supported
320
+ if (useProviders.includes('beehiiv') && self.providers.beehiiv) {
321
+ const segmentIdMap = await beehiivProvider.resolveSegmentIds();
322
+
323
+ resolvedSegments.beehiiv = {
324
+ segments: (resolvedSettings.segments || []).map(key => segmentIdMap[key] || key).filter(Boolean),
325
+ excludeSegments: (resolvedSettings.excludeSegments || []).map(key => segmentIdMap[key] || key).filter(Boolean),
326
+ };
327
+ }
321
328
 
322
329
  assistant.log('Marketing.sendCampaign():', {
323
330
  name: resolvedSettings.name,
@@ -349,8 +356,8 @@ Marketing.prototype.sendCampaign = async function (settings) {
349
356
  preheader: resolvedSettings.preheader,
350
357
  content: contentHtml,
351
358
  sendAt: settings.sendAt,
352
- segments: settings.segments,
353
- excludeSegments: settings.excludeSegments,
359
+ segments: resolvedSegments.beehiiv?.segments || [],
360
+ excludeSegments: resolvedSegments.beehiiv?.excludeSegments || [],
354
361
  })
355
362
  .then((r) => { results.beehiiv = r; })
356
363
  .catch((e) => { results.beehiiv = { success: false, error: e.message }; })
@@ -274,6 +274,47 @@ function buildFields(userDoc) {
274
274
  return fields;
275
275
  }
276
276
 
277
+ // Cached segment name → Beehiiv segment ID map
278
+ let _segmentIdCache = null;
279
+
280
+ /**
281
+ * Fetch segment definitions from Beehiiv and build a name → id map.
282
+ * Segments are created by OMEGA with names matching the SSOT keys in constants.js.
283
+ * Cached in memory for the lifetime of the process.
284
+ *
285
+ * @returns {object} Map of segment name → Beehiiv segment ID
286
+ */
287
+ async function resolveSegmentIds() {
288
+ if (_segmentIdCache) {
289
+ return _segmentIdCache;
290
+ }
291
+
292
+ const publicationId = await getPublicationId();
293
+
294
+ if (!publicationId) {
295
+ return {};
296
+ }
297
+
298
+ try {
299
+ const data = await fetch(`${BASE_URL}/publications/${publicationId}/segments?limit=100`, {
300
+ response: 'json',
301
+ headers: headers(),
302
+ timeout: 10000,
303
+ });
304
+
305
+ _segmentIdCache = {};
306
+
307
+ for (const segment of (data.data || [])) {
308
+ _segmentIdCache[segment.name] = segment.id;
309
+ }
310
+
311
+ return _segmentIdCache;
312
+ } catch (e) {
313
+ console.error('Beehiiv resolveSegmentIds error:', e);
314
+ return {};
315
+ }
316
+ }
317
+
277
318
  // --- Campaigns (Posts) ---
278
319
 
279
320
  /**
@@ -362,6 +403,9 @@ async function createPost(options) {
362
403
  }
363
404
 
364
405
  module.exports = {
406
+ // Resolution
407
+ resolveSegmentIds,
408
+
365
409
  // Contacts
366
410
  addContact,
367
411
  removeContact,
@@ -37,10 +37,12 @@ const BLOCKED_LOCAL_PARTS = new Set([
37
37
  'asdf', 'qwerty', 'zxcv', 'asd', 'qwe',
38
38
  'aaa', 'bbb', 'xxx', 'zzz',
39
39
  'abc', 'abc123', 'abcdef',
40
+ // Team
41
+ // 'user', 'email', 'mail', 'hello', 'info',
42
+ // 'admin', 'administrator', 'support',
43
+ // 'contact',
40
44
  // Placeholder
41
- 'user', 'email', 'mail', 'hello', 'info',
42
- 'admin', 'administrator', 'support',
43
- 'contact', 'name', 'firstname', 'lastname',
45
+ 'name', 'firstname', 'lastname',
44
46
  'foo', 'bar', 'baz', 'foobar',
45
47
  'null', 'undefined', 'none', 'anonymous',
46
48
  ]);