backend-manager 5.0.163 → 5.0.164

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,10 @@ 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.164] - 2026-03-18
18
+ ### Added
19
+ - Default field backfill in campaign seed setup — missing fields are restored from seed defaults without overwriting user edits
20
+
17
21
  # [5.0.163] - 2026-03-18
18
22
  ### Changed
19
23
  - Refactored campaign POST/PUT routes to generic field passthrough — schema-validated fields flow through automatically via shared `buildCampaignDoc()` utility, no manual field assignments needed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backend-manager",
3
- "version": "5.0.163",
3
+ "version": "5.0.164",
4
4
  "description": "Quick tools for developing Firebase functions",
5
5
  "main": "src/manager/index.js",
6
6
  "bin": {
@@ -25,7 +25,7 @@ class MarketingCampaignsSeededTest extends BaseTest {
25
25
  return false;
26
26
  }
27
27
 
28
- // Check enforced fields
28
+ // Check enforced fields + missing defaults
29
29
  const data = doc.data();
30
30
 
31
31
  for (const [path, expected] of Object.entries(seed.enforced)) {
@@ -35,6 +35,11 @@ class MarketingCampaignsSeededTest extends BaseTest {
35
35
  return false;
36
36
  }
37
37
  }
38
+
39
+ // Check for missing fields that should exist from seed
40
+ if (hasMissingFields(data, seed.doc)) {
41
+ return false;
42
+ }
38
43
  }
39
44
 
40
45
  return true;
@@ -62,10 +67,14 @@ class MarketingCampaignsSeededTest extends BaseTest {
62
67
  continue;
63
68
  }
64
69
 
65
- // Doc exists → check and fix enforced fields
70
+ // Doc exists → fill missing defaults + enforce required fields
66
71
  const data = doc.data();
67
72
  const updates = {};
68
73
 
74
+ // Fill missing fields from seed defaults (never overwrite existing values)
75
+ fillMissing(data, seed.doc, updates, '');
76
+
77
+ // Enforce required fields (always overwrite to match seed)
69
78
  for (const [path, expected] of Object.entries(seed.enforced)) {
70
79
  const actual = _.get(data, path);
71
80
 
@@ -106,4 +115,58 @@ class MarketingCampaignsSeededTest extends BaseTest {
106
115
  }
107
116
  }
108
117
 
118
+ /**
119
+ * Check if the live doc is missing any fields defined in the seed.
120
+ */
121
+ function hasMissingFields(live, seed, prefix) {
122
+ for (const [key, seedValue] of Object.entries(seed)) {
123
+ if (key === 'metadata') {
124
+ continue;
125
+ }
126
+
127
+ const path = prefix ? `${prefix}.${key}` : key;
128
+ const liveValue = _.get(live, path);
129
+
130
+ if (liveValue === undefined) {
131
+ return true;
132
+ }
133
+
134
+ if (_.isPlainObject(seedValue) && _.isPlainObject(liveValue)) {
135
+ if (hasMissingFields(live, seedValue, path)) {
136
+ return true;
137
+ }
138
+ }
139
+ }
140
+
141
+ return false;
142
+ }
143
+
144
+ /**
145
+ * Recursively fill missing fields from seed into updates.
146
+ * Only sets fields that don't exist in the live doc — never overwrites.
147
+ * Skips metadata (managed separately).
148
+ */
149
+ function fillMissing(live, seed, updates, prefix) {
150
+ for (const [key, seedValue] of Object.entries(seed)) {
151
+ if (key === 'metadata') {
152
+ continue;
153
+ }
154
+
155
+ const path = prefix ? `${prefix}.${key}` : key;
156
+ const liveValue = _.get(live, path);
157
+
158
+ // If live doc is missing this field entirely, set it from seed
159
+ if (liveValue === undefined) {
160
+ _.set(updates, path, seedValue);
161
+ console.log(chalk.blue(` + ${path}: ${chalk.dim('(missing)')} → ${chalk.bold(JSON.stringify(seedValue).slice(0, 80))}`));
162
+ continue;
163
+ }
164
+
165
+ // If both are plain objects, recurse to check nested fields
166
+ if (_.isPlainObject(seedValue) && _.isPlainObject(liveValue)) {
167
+ fillMissing(live, seedValue, updates, path);
168
+ }
169
+ }
170
+ }
171
+
109
172
  module.exports = MarketingCampaignsSeededTest;