backend-manager 5.2.6 → 5.2.7

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,12 @@ 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.2.7] - 2026-05-24
18
+
19
+ ### Fixed
20
+
21
+ - **`inferContact` silent-failure logging.** When the AI inference returned an empty result (either the AI call failed and returned null, or `gpt-5-mini` returned a parsed response with all-empty fields, or the response shape was missing `firstName`), the whole flow silently swallowed the failure and the signup's `user.personal.name` got written as `null`/`null`. Confirmed live on Somiibo: signed up `ian.wiedenman.business@gmail.com` twice on the same backend — first signup inferred nothing (empty name written), second signup correctly inferred "Ian Wiedenman". Same email, same code, transient AI hiccup, zero log trail. Added three unconditional diagnostic logs to `src/manager/libraries/infer-contact.js` (AI returned null, AI parsed response had all fields empty, AI response missing firstName) plus a log in `src/manager/routes/user/signup/post.js#inferUserContact` when the helper returns null. Next silent failure will at least leave a breadcrumb.
22
+
17
23
  # [5.2.6] - 2026-05-24
18
24
 
19
25
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backend-manager",
3
- "version": "5.2.6",
3
+ "version": "5.2.7",
4
4
  "description": "Quick tools for developing Firebase functions",
5
5
  "main": "src/manager/index.js",
6
6
  "bin": {
@@ -26,6 +26,9 @@ async function inferContact(email, assistant) {
26
26
  if (aiResult) {
27
27
  return aiResult;
28
28
  }
29
+ assistant.log(`inferContact: AI returned null for ${email} — falling back to empty result`);
30
+ } else {
31
+ assistant.log(`inferContact: BACKEND_MANAGER_OPENAI_API_KEY not set — skipping AI inference for ${email}`);
29
32
  }
30
33
 
31
34
  return { firstName: '', lastName: '', company: '', confidence: 0, method: 'none' };
@@ -57,18 +60,22 @@ async function inferContactWithAI(email, assistant) {
57
60
 
58
61
  const parsed = result?.content;
59
62
  if (parsed?.firstName !== undefined) {
60
- return {
63
+ const inferred = {
61
64
  firstName: capitalize(parsed.firstName || ''),
62
65
  lastName: capitalize(parsed.lastName || ''),
63
66
  company: capitalize(parsed.company || ''),
64
67
  confidence: typeof parsed.confidence === 'number' ? parsed.confidence : 0.5,
65
68
  method: 'ai',
66
69
  };
70
+ if (!inferred.firstName && !inferred.lastName && !inferred.company) {
71
+ assistant.log(`inferContactWithAI: AI parsed response had ALL fields empty for ${email}. Raw:`, parsed);
72
+ }
73
+ return inferred;
67
74
  }
75
+
76
+ assistant.log(`inferContactWithAI: AI response missing firstName for ${email}. Raw result:`, result);
68
77
  } catch (e) {
69
- if (assistant) {
70
- assistant.error('inferContactWithAI: Failed:', e);
71
- }
78
+ assistant.error('inferContactWithAI: Failed:', e);
72
79
  }
73
80
 
74
81
  return null;
@@ -229,6 +229,7 @@ async function inferUserContact(assistant, email) {
229
229
  const inferred = await inferContact(email, assistant);
230
230
 
231
231
  if (!inferred?.firstName && !inferred?.lastName && !inferred?.company) {
232
+ assistant.log(`signup(): inferUserContact returned empty result for ${email} (method=${inferred?.method || 'unknown'})`);
232
233
  return null;
233
234
  }
234
235
 
@@ -239,6 +239,10 @@ module.exports = {
239
239
  // Test 9: Cron resets daily counters for authenticated users
240
240
  {
241
241
  name: 'cron-resets-daily-counters',
242
+ // bm_cronDaily runs all daily jobs serially; reset-usage is at the end of
243
+ // the alphabetical sequence. In EXTENDED mode the real-API jobs ahead of
244
+ // it can take ~50s combined — override the suite's 30s default.
245
+ timeout: 75000,
242
246
  async run({ assert, firestore, state, accounts, waitFor, pubsub }) {
243
247
  // Verify daily counter is > 0 before cron
244
248
  const beforeDoc = await firestore.get(`users/${accounts.basic.uid}`);
@@ -251,19 +255,24 @@ module.exports = {
251
255
  // Trigger cron via PubSub
252
256
  await pubsub.trigger('bm_cronDaily');
253
257
 
254
- // Wait for cron to reset daily counter
258
+ // Wait for cron to reset daily counter.
259
+ // bm_cronDaily executes every registered daily job sequentially. In EXTENDED
260
+ // mode the real-API jobs (marketing-newsletter-generate, expire-paypal-cancellations,
261
+ // ghostii-auto-publisher, etc.) can take 40-50s combined before reset-usage
262
+ // (alphabetical tail) gets its turn. 70s gives that the headroom it needs;
263
+ // the per-test `timeout` below matches.
255
264
  try {
256
265
  await waitFor(
257
266
  async () => {
258
267
  const doc = await firestore.get(`users/${accounts.basic.uid}`);
259
268
  return doc?.usage?.requests?.daily === 0;
260
269
  },
261
- 15000,
270
+ 70000,
262
271
  500
263
272
  );
264
273
  assert.ok(true, 'Daily counter was reset to 0 by cron');
265
274
  } catch (error) {
266
- assert.fail('Daily counter should be reset to 0 within 15s');
275
+ assert.fail('Daily counter should be reset to 0 within 70s');
267
276
  }
268
277
  },
269
278
  },