backend-manager 5.8.2 → 5.8.3

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.8.3] - 2026-06-19
18
+
19
+ ### Fixed
20
+ - **Ghostii feed tracking timestamps.** `trackFeedItem` now uses BEM's standard `metadata.{created,updated}` with `timestamp` (ISO) and `timestampUNIX` (seconds) instead of `FieldValue.serverTimestamp()`, which failed in the emulator context.
21
+
22
+ ### Changed
23
+ - **Firestore collection rename.** `ghostii-feed-items` → `ghostii-sources` for clarity and consistency with the OMEGA naming convention.
24
+ - **Extended feed test coverage.** Added 5 marketing/social feeds (Social Media Examiner, Hootsuite, Sprout Social, Buffer, Digiday) to the real-feed integration test. Threshold bumped from 2/3 to 5/7.
25
+
17
26
  # [5.8.2] - 2026-06-19
18
27
 
19
28
  ### Fixed
package/PROGRESS.md CHANGED
@@ -2,11 +2,11 @@
2
2
  > Agents and maintainers should update this file regularly to reflect the current state of the project.
3
3
 
4
4
  ## 🎯 Current Focus
5
- * **Goal:** Fix setup crash on fresh projects missing config files
6
- * **Current Phase:** Phase 6 in progress (code done, needs docs + publish)
5
+ * **Goal:** Ghostii feed sources cleanup + Somiibo consumer config
6
+ * **Current Phase:** Fixes applied + tested, pending commit + publish
7
7
  * **Priority:** High
8
- * **Last Updated:** 2026-06-18 11:30 PM PDT
9
- * **Notes:** Setup now fully bootstraps a bare project (just package.json + .nvmrc). Scaffolds configs, engines.node, everything stops cleanly at service-account with a Firebase console link. Still needs docs + publish.
8
+ * **Last Updated:** 2026-06-19 3:05 AM PDT
9
+ * **Notes:** Renamed Firestore collection `ghostii-feed-items` `ghostii-sources`. Fixed `FieldValue.serverTimestamp()` → BEM metadata pattern. Added 5 marketing/social feeds to extended tests. All tests passing (20/20 standard, 9/9 extended). Somiibo consumer has feeds configured + `ghostii-sources.md` evaluation doc. Needs BEM publish + consumer deploy.
10
10
 
11
11
  ## 📌 Active Task List
12
12
  * [ ] Phase 6: Setup scaffolds essential configs for fresh projects
package/docs/ghostii.md CHANGED
@@ -46,27 +46,30 @@ Each `ghostii[]` entry has a `sources` array. The cron picks one at random per a
46
46
  ### Feed source flow
47
47
 
48
48
  1. Fetch and parse the RSS/Atom/JSON feed via `feed-parser.parseFeed()`
49
- 2. Query `ghostii-feed-items` in Firestore for already-processed items
49
+ 2. Query `ghostii-sources` in Firestore for already-processed items
50
50
  3. Filter to unprocessed items, pick the newest
51
51
  4. Extract full article content from the item URL via `feed-parser.extractArticleContent()`
52
52
  5. Pass extracted text as `sourceContent` to the Ghostii API (separate from the `description` prompt)
53
- 6. After publish, write a tracking doc to `ghostii-feed-items/{hash}`
53
+ 6. After publish, write a tracking doc to `ghostii-sources/{hash}`
54
54
  7. On failure (feed unreachable, unparseable, exhausted): fall back to `$app` behavior
55
55
 
56
56
  ### Feed item tracking
57
57
 
58
- Collection: `ghostii-feed-items` (consumer project Firestore)
58
+ Collection: `ghostii-sources` (consumer project Firestore)
59
59
 
60
60
  ```js
61
- ghostii-feed-items/{sha256(feedUrl + '::' + itemId).slice(0,20)}: {
61
+ ghostii-sources/{sha256(feedUrl + '::' + itemId).slice(0,20)}: {
62
62
  feedUrl: 'https://...',
63
63
  itemId: 'guid-or-url',
64
64
  itemUrl: 'https://...',
65
65
  itemTitle: '...',
66
- processedAt: Timestamp,
67
66
  brandId: '...',
68
67
  postUrl: '...',
69
68
  postSlug: '...',
69
+ metadata: {
70
+ created: { timestamp: '...ISO...', timestampUNIX: 1234567890 },
71
+ updated: { timestamp: '...ISO...', timestampUNIX: 1234567890 },
72
+ },
70
73
  metadata: { created: Timestamp },
71
74
  }
72
75
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backend-manager",
3
- "version": "5.8.2",
3
+ "version": "5.8.3",
4
4
  "description": "Quick tools for developing Firebase functions",
5
5
  "main": "src/manager/index.js",
6
6
  "bin": {
@@ -12,7 +12,7 @@
12
12
  * 'https://...' — fetch URL content as prompt seed
13
13
  * '<text>' — use directly as prompt seed
14
14
  *
15
- * Feed items are tracked in Firestore (`ghostii-feed-items`) so the same
15
+ * Feed items are tracked in Firestore (`ghostii-sources`) so the same
16
16
  * article is never processed twice. When a feed is unreachable or exhausted,
17
17
  * the entry falls back to $app behavior.
18
18
  */
@@ -381,7 +381,7 @@ async function getProcessedItemIds(admin, feedUrl) {
381
381
  }
382
382
 
383
383
  const snapshot = await admin.firestore()
384
- .collection('ghostii-feed-items')
384
+ .collection('ghostii-sources')
385
385
  .where('feedUrl', '==', feedUrl)
386
386
  .select('itemId', 'itemUrl')
387
387
  .get();
@@ -413,18 +413,20 @@ async function getProcessedItemIds(admin, feedUrl) {
413
413
  */
414
414
  async function trackFeedItem(admin, { feedUrl, item, brandId, postUrl, postSlug }) {
415
415
  const docId = feedItemHash(feedUrl, item.id || item.url);
416
+ const nowISO = new Date().toISOString();
417
+ const nowUNIX = Math.round(Date.now() / 1000);
416
418
 
417
- await admin.firestore().doc(`ghostii-feed-items/${docId}`).set({
419
+ await admin.firestore().doc(`ghostii-sources/${docId}`).set({
418
420
  feedUrl: feedUrl,
419
421
  itemId: item.id || item.url,
420
422
  itemUrl: item.url,
421
423
  itemTitle: item.title,
422
- processedAt: admin.firestore.FieldValue.serverTimestamp(),
423
424
  brandId: brandId,
424
425
  postUrl: postUrl || null,
425
426
  postSlug: postSlug || null,
426
427
  metadata: {
427
- created: admin.firestore.FieldValue.serverTimestamp(),
428
+ created: { timestamp: nowISO, timestampUNIX: nowUNIX },
429
+ updated: { timestamp: nowISO, timestampUNIX: nowUNIX },
428
430
  },
429
431
  });
430
432
  }
@@ -176,7 +176,7 @@ module.exports = {
176
176
  postSlug: 'test-article',
177
177
  });
178
178
 
179
- const doc = await admin.firestore().doc(`ghostii-feed-items/${docId}`).get();
179
+ const doc = await admin.firestore().doc(`ghostii-sources/${docId}`).get();
180
180
  assert.ok(doc.exists, 'tracking doc created');
181
181
 
182
182
  const data = doc.data();
@@ -188,6 +188,13 @@ module.exports = {
188
188
  assert.equal(data.postUrl, 'https://test-brand.com/blog/test-article');
189
189
  assert.equal(data.postSlug, 'test-article');
190
190
  assert.ok(data.metadata, 'has metadata object');
191
+ assert.ok(data.metadata.created, 'has metadata.created');
192
+ assert.ok(data.metadata.updated, 'has metadata.updated');
193
+ assert.equal(typeof data.metadata.created.timestamp, 'string', 'created.timestamp is ISO string');
194
+ assert.equal(typeof data.metadata.created.timestampUNIX, 'number', 'created.timestampUNIX is number');
195
+ assert.equal(typeof data.metadata.updated.timestamp, 'string', 'updated.timestamp is ISO string');
196
+ assert.equal(typeof data.metadata.updated.timestampUNIX, 'number', 'updated.timestampUNIX is number');
197
+ assert.ok(!data.processedAt, 'no legacy processedAt field');
191
198
  },
192
199
  },
193
200
 
@@ -26,22 +26,15 @@ const TEMP_DIR = path.join(BEM_ROOT, '.temp', 'ghostii-feed', `run-${new Date().
26
26
 
27
27
  // --- Real feed URLs for extended tests ---
28
28
  // Chosen for stability and confirmed to work with wonderful-fetch.
29
+ // Includes generic tech feeds + marketing/social feeds used by OMEGA consumers.
29
30
  const REAL_FEEDS = [
30
- {
31
- name: 'Ars Technica',
32
- url: 'https://feeds.arstechnica.com/arstechnica/technology-lab',
33
- format: 'rss',
34
- },
35
- {
36
- name: 'TechCrunch',
37
- url: 'https://techcrunch.com/feed/',
38
- format: 'rss',
39
- },
40
- {
41
- name: 'Politico',
42
- url: 'https://rss.politico.com/politics-news.xml',
43
- format: 'rss',
44
- },
31
+ { name: 'Ars Technica', url: 'https://feeds.arstechnica.com/arstechnica/technology-lab', format: 'rss' },
32
+ { name: 'TechCrunch', url: 'https://techcrunch.com/feed/', format: 'rss' },
33
+ { name: 'Social Media Examiner', url: 'https://www.socialmediaexaminer.com/feed/', format: 'rss' },
34
+ { name: 'Hootsuite Blog', url: 'https://blog.hootsuite.com/feed/', format: 'rss' },
35
+ { name: 'Sprout Social Insights', url: 'https://sproutsocial.com/insights/feed/', format: 'rss' },
36
+ { name: 'Buffer Resources', url: 'https://buffer.com/resources/feed/', format: 'rss' },
37
+ { name: 'Digiday', url: 'https://digiday.com/feed/', format: 'rss' },
45
38
  ];
46
39
 
47
40
  /**
@@ -333,7 +326,7 @@ module.exports = {
333
326
 
334
327
  // Verify the doc was stored with the expected hash ID
335
328
  const expectedDocId = feedItemHash(realFeedUrl, firstItem.id || firstItem.url);
336
- const doc = await admin.firestore().doc(`ghostii-feed-items/${expectedDocId}`).get();
329
+ const doc = await admin.firestore().doc(`ghostii-sources/${expectedDocId}`).get();
337
330
 
338
331
  assert.ok(doc.exists, 'tracking doc exists at expected hash-based ID');
339
332
 
@@ -375,11 +368,11 @@ module.exports = {
375
368
  saveArtifact(`feed-${feed.name.toLowerCase().replace(/\s+/g, '-')}-raw.xml`, text);
376
369
  }
377
370
 
378
- // At least 2 of 3 feeds should parse successfully
371
+ // At least 5 of 7 feeds should parse successfully
379
372
  const successful = results.filter((r) => r.items > 0);
380
373
  assert.ok(
381
- successful.length >= 2,
382
- `at least 2 of ${REAL_FEEDS.length} real feeds parsed successfully: ${JSON.stringify(results)}`,
374
+ successful.length >= 5,
375
+ `at least 5 of ${REAL_FEEDS.length} real feeds parsed successfully: ${JSON.stringify(results)}`,
383
376
  );
384
377
 
385
378
  // Save summary