bulltrackers-module 1.0.548 → 1.0.549

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.
@@ -15,6 +15,7 @@ const {
15
15
  loadPIPageViews, // [NEW]
16
16
  loadWatchlistMembership: loadWatchlistMembershipData, // [NEW] Renamed to avoid conflict
17
17
  loadPIAlertHistory, // [NEW]
18
+ loadPIWatchlistData, // [NEW] PI-centric watchlist data
18
19
  loadPopularInvestorMasterList // [NEW]
19
20
  } = require('../utils/data_loader');
20
21
  const zlib = require('zlib');
@@ -33,6 +34,7 @@ class CachedDataLoader {
33
34
  pageViews: new Map(), // [NEW]
34
35
  watchlistMembership: new Map(), // [NEW]
35
36
  alertHistory: new Map(),// [NEW]
37
+ piWatchlistData: new Map(), // [NEW] PI-centric watchlist data cache (keyed by piCid)
36
38
  piMasterList: null // [NEW] Singleton cache (not date dependent)
37
39
  };
38
40
  }
@@ -125,6 +127,17 @@ class CachedDataLoader {
125
127
  return promise;
126
128
  }
127
129
 
130
+ // [NEW] Load PI-Centric Watchlist Data
131
+ // Loads watchlist data from PopularInvestors/{piCid}/watchlistData/current
132
+ // [FIX] Cache promises to prevent race conditions when multiple users request same data
133
+ async loadPIWatchlistData(piCid) {
134
+ const piCidStr = String(piCid);
135
+ if (this.cache.piWatchlistData.has(piCidStr)) return this.cache.piWatchlistData.get(piCidStr);
136
+ const promise = loadPIWatchlistData(this.config, this.deps, piCidStr);
137
+ this.cache.piWatchlistData.set(piCidStr, promise);
138
+ return promise;
139
+ }
140
+
128
141
  async getPriceShardReferences() {
129
142
  return getPriceShardRefs(this.config, this.deps);
130
143
  }
@@ -808,6 +808,102 @@ class WatchlistMembershipExtractor {
808
808
  }
809
809
  }
810
810
 
811
+ /**
812
+ * [NEW] Extractor for PI-Centric Watchlist Data
813
+ * Access via: loader.loadPIWatchlistData(piCid) or context-specific loading
814
+ * Schema: { totalUsers, userCids: [], dailyAdditions: { date: { count, userCids: [], timestamp } }, lastUpdated }
815
+ */
816
+ class PIWatchlistDataExtractor {
817
+ /**
818
+ * Get total users who have this PI in their watchlist
819
+ * @param {Object} piWatchlistData - The PI watchlist data from PopularInvestors/{piCid}/watchlistData/current
820
+ * @returns {number} Total users count or 0 if not available
821
+ */
822
+ static getTotalUsers(piWatchlistData) {
823
+ if (!piWatchlistData) return 0;
824
+ return piWatchlistData.totalUsers || 0;
825
+ }
826
+
827
+ /**
828
+ * Get array of user CIDs who have this PI in their watchlist
829
+ * @param {Object} piWatchlistData - The PI watchlist data from PopularInvestors/{piCid}/watchlistData/current
830
+ * @returns {string[]} Array of user CIDs, or empty array if not available
831
+ */
832
+ static getUserCids(piWatchlistData) {
833
+ if (!piWatchlistData || !Array.isArray(piWatchlistData.userCids)) return [];
834
+ return piWatchlistData.userCids.map(cid => String(cid));
835
+ }
836
+
837
+ /**
838
+ * Check if a specific user has this PI in their watchlist
839
+ * @param {Object} piWatchlistData - The PI watchlist data from PopularInvestors/{piCid}/watchlistData/current
840
+ * @param {string|number} userId - User CID
841
+ * @returns {boolean} True if user has PI in watchlist
842
+ */
843
+ static hasUser(piWatchlistData, userId) {
844
+ const userCids = this.getUserCids(piWatchlistData);
845
+ return userCids.includes(String(userId));
846
+ }
847
+
848
+ /**
849
+ * Get daily additions data for a specific date
850
+ * @param {Object} piWatchlistData - The PI watchlist data from PopularInvestors/{piCid}/watchlistData/current
851
+ * @param {string} date - Date string in YYYY-MM-DD format
852
+ * @returns {Object|null} Daily addition data { count, userCids: [], timestamp } or null if not found
853
+ */
854
+ static getDailyAdditions(piWatchlistData, date) {
855
+ if (!piWatchlistData || !piWatchlistData.dailyAdditions || typeof piWatchlistData.dailyAdditions !== 'object') {
856
+ return null;
857
+ }
858
+ return piWatchlistData.dailyAdditions[date] || null;
859
+ }
860
+
861
+ /**
862
+ * Get count of users who added this PI to their watchlist on a specific date
863
+ * @param {Object} piWatchlistData - The PI watchlist data from PopularInvestors/{piCid}/watchlistData/current
864
+ * @param {string} date - Date string in YYYY-MM-DD format
865
+ * @returns {number} Count of users who added on this date, or 0 if not available
866
+ */
867
+ static getDailyAdditionCount(piWatchlistData, date) {
868
+ const dailyData = this.getDailyAdditions(piWatchlistData, date);
869
+ return dailyData?.count || 0;
870
+ }
871
+
872
+ /**
873
+ * Get user CIDs who added this PI to their watchlist on a specific date
874
+ * @param {Object} piWatchlistData - The PI watchlist data from PopularInvestors/{piCid}/watchlistData/current
875
+ * @param {string} date - Date string in YYYY-MM-DD format
876
+ * @returns {string[]} Array of user CIDs who added on this date, or empty array if not available
877
+ */
878
+ static getDailyAdditionUserCids(piWatchlistData, date) {
879
+ const dailyData = this.getDailyAdditions(piWatchlistData, date);
880
+ if (!dailyData || !Array.isArray(dailyData.userCids)) return [];
881
+ return dailyData.userCids.map(cid => String(cid));
882
+ }
883
+
884
+ /**
885
+ * Get all dates with daily addition data
886
+ * @param {Object} piWatchlistData - The PI watchlist data from PopularInvestors/{piCid}/watchlistData/current
887
+ * @returns {string[]} Array of date strings (YYYY-MM-DD format), or empty array if not available
888
+ */
889
+ static getAllDates(piWatchlistData) {
890
+ if (!piWatchlistData || !piWatchlistData.dailyAdditions || typeof piWatchlistData.dailyAdditions !== 'object') {
891
+ return [];
892
+ }
893
+ return Object.keys(piWatchlistData.dailyAdditions);
894
+ }
895
+
896
+ /**
897
+ * Get last updated timestamp
898
+ * @param {Object} piWatchlistData - The PI watchlist data from PopularInvestors/{piCid}/watchlistData/current
899
+ * @returns {Object|null} Timestamp object or null if not available
900
+ */
901
+ static getLastUpdated(piWatchlistData) {
902
+ if (!piWatchlistData) return null;
903
+ return piWatchlistData.lastUpdated || null;
904
+ }
905
+ }
906
+
811
907
  /**
812
908
  * [NEW] Extractor for PI Alert History Data
813
909
  * Access via: context.globalData.alertHistory
@@ -995,6 +1091,7 @@ module.exports = {
995
1091
  RatingsExtractor,
996
1092
  PageViewsExtractor,
997
1093
  WatchlistMembershipExtractor,
1094
+ PIWatchlistDataExtractor,
998
1095
  AlertHistoryExtractor,
999
1096
  PIMasterListExtractor
1000
1097
  };
@@ -774,6 +774,38 @@ async function loadPIAlertHistory(config, deps, dateString) {
774
774
  }
775
775
  }
776
776
 
777
+ /** Stage 17: Load PI-Centric Watchlist Data
778
+ * Loads watchlist data from PopularInvestors/{piCid}/watchlistData/current
779
+ * This provides time-series data of watchlist additions per PI
780
+ */
781
+ async function loadPIWatchlistData(config, deps, piCid) {
782
+ const { db, logger, calculationUtils } = deps;
783
+ const { withRetry } = calculationUtils;
784
+ const piCidStr = String(piCid);
785
+
786
+ logger.log('INFO', `Loading PI Watchlist Data for PI ${piCid}`);
787
+
788
+ try {
789
+ const docRef = db.collection('PopularInvestors')
790
+ .doc(piCidStr)
791
+ .collection('watchlistData')
792
+ .doc('current');
793
+
794
+ const docSnap = await withRetry(() => docRef.get(), `getPIWatchlistData(${piCidStr})`);
795
+
796
+ if (!docSnap.exists) {
797
+ logger.log('WARN', `PI Watchlist Data not found for PI ${piCidStr}`);
798
+ return null;
799
+ }
800
+
801
+ const data = tryDecompress(docSnap.data());
802
+ return data; // Returns { totalUsers, userCids: [], dailyAdditions: { date: { count, userCids, timestamp } }, lastUpdated }
803
+ } catch (error) {
804
+ logger.log('ERROR', `Failed to load PI Watchlist Data for PI ${piCidStr}: ${error.message}`);
805
+ return null;
806
+ }
807
+ }
808
+
777
809
  // [NEW] Load Popular Investor Master List
778
810
  async function loadPopularInvestorMasterList(config, deps) {
779
811
  const { db, logger, calculationUtils } = deps;
@@ -823,5 +855,6 @@ module.exports = {
823
855
  loadPIPageViews,
824
856
  loadWatchlistMembership,
825
857
  loadPIAlertHistory,
826
- loadPopularInvestorMasterList // [NEW]
858
+ loadPopularInvestorMasterList, // [NEW]
859
+ loadPIWatchlistData,
827
860
  };
@@ -116,6 +116,7 @@ async function updatePageViewsRootData(db, logger, piCid, userCid, date) {
116
116
  * Update Watchlist Membership rootdata collection
117
117
  * Aggregates watchlist membership from SignedInUsers/{cid}/watchlists into WatchlistMembershipData/{date}
118
118
  * This should be called when a PI is added/removed from a watchlist
119
+ * This provides a single point of reference for all watchlists made in that day
119
120
  */
120
121
  async function updateWatchlistMembershipRootData(db, logger, piCid, userCid, isPublic, date, action = 'add') {
121
122
  try {
@@ -161,8 +162,9 @@ async function updateWatchlistMembershipRootData(db, logger, piCid, userCid, isP
161
162
  }
162
163
  }
163
164
 
164
- // Update the data
165
+ // Update the data - ensure date field is set for rootdata indexer
165
166
  const updateData = {
167
+ date: date, // Ensure date field exists for rootdata indexer
166
168
  [piCidStr]: {
167
169
  totalUsers: piMembership.totalUsers,
168
170
  users: piMembership.users,
@@ -182,6 +184,89 @@ async function updateWatchlistMembershipRootData(db, logger, piCid, userCid, isP
182
184
  }
183
185
  }
184
186
 
187
+ /**
188
+ * Update Popular Investor watchlist data
189
+ * Tracks watchlist additions per PI in PopularInvestors/{piCid}/watchlistData
190
+ * This provides a time series of watchlist additions over time per popular investor
191
+ */
192
+ async function updatePIWatchlistData(db, logger, piCid, userCid, date, action = 'add') {
193
+ try {
194
+ const piCidStr = String(piCid);
195
+ const userCidStr = String(userCid);
196
+ const piWatchlistRef = db.collection('PopularInvestors').doc(piCidStr).collection('watchlistData').doc('current');
197
+
198
+ const piWatchlistDoc = await piWatchlistRef.get();
199
+ const existingData = piWatchlistDoc.exists ? piWatchlistDoc.data() : {};
200
+
201
+ // Initialize structure if needed
202
+ const watchlistData = existingData || {
203
+ totalUsers: 0,
204
+ userCids: [],
205
+ dailyAdditions: {},
206
+ lastUpdated: FieldValue.serverTimestamp()
207
+ };
208
+
209
+ if (action === 'add') {
210
+ // Add user CID if not already present
211
+ if (!watchlistData.userCids || !Array.isArray(watchlistData.userCids)) {
212
+ watchlistData.userCids = [];
213
+ }
214
+ if (!watchlistData.userCids.includes(userCidStr)) {
215
+ watchlistData.userCids.push(userCidStr);
216
+ watchlistData.totalUsers = watchlistData.userCids.length;
217
+ }
218
+
219
+ // Track daily additions with timestamp
220
+ if (!watchlistData.dailyAdditions || typeof watchlistData.dailyAdditions !== 'object') {
221
+ watchlistData.dailyAdditions = {};
222
+ }
223
+
224
+ // Initialize date entry if needed
225
+ if (!watchlistData.dailyAdditions[date]) {
226
+ watchlistData.dailyAdditions[date] = {
227
+ count: 0,
228
+ userCids: [],
229
+ timestamp: FieldValue.serverTimestamp()
230
+ };
231
+ }
232
+
233
+ // Add user to this date's additions if not already present
234
+ const dateEntry = watchlistData.dailyAdditions[date];
235
+ if (!dateEntry.userCids.includes(userCidStr)) {
236
+ dateEntry.userCids.push(userCidStr);
237
+ dateEntry.count = dateEntry.userCids.length;
238
+ dateEntry.timestamp = FieldValue.serverTimestamp();
239
+ }
240
+
241
+ } else if (action === 'remove') {
242
+ // Remove user CID
243
+ if (watchlistData.userCids && Array.isArray(watchlistData.userCids)) {
244
+ watchlistData.userCids = watchlistData.userCids.filter(cid => cid !== userCidStr);
245
+ watchlistData.totalUsers = watchlistData.userCids.length;
246
+ }
247
+
248
+ // Update daily additions for this date
249
+ if (watchlistData.dailyAdditions && watchlistData.dailyAdditions[date]) {
250
+ const dateEntry = watchlistData.dailyAdditions[date];
251
+ if (dateEntry.userCids && Array.isArray(dateEntry.userCids)) {
252
+ dateEntry.userCids = dateEntry.userCids.filter(cid => cid !== userCidStr);
253
+ dateEntry.count = dateEntry.userCids.length;
254
+ dateEntry.timestamp = FieldValue.serverTimestamp();
255
+ }
256
+ }
257
+ }
258
+
259
+ // Update the document
260
+ watchlistData.lastUpdated = FieldValue.serverTimestamp();
261
+ await piWatchlistRef.set(watchlistData, { merge: true });
262
+
263
+ logger.log('INFO', `[updatePIWatchlistData] Updated watchlist data for PI ${piCid} (action: ${action}, user: ${userCid})`);
264
+ } catch (error) {
265
+ logger.log('ERROR', `[updatePIWatchlistData] Error updating PI watchlist data for PI ${piCid}:`, error);
266
+ // Don't throw - this is a non-critical aggregation
267
+ }
268
+ }
269
+
185
270
  /**
186
271
  * Update PI Alert History rootdata collection
187
272
  * Records alert triggers in PIAlertHistoryData/{date}
@@ -286,6 +371,7 @@ module.exports = {
286
371
  updateRatingsRootData,
287
372
  updatePageViewsRootData,
288
373
  updateWatchlistMembershipRootData,
374
+ updatePIWatchlistData,
289
375
  updateAlertHistoryRootData,
290
376
  updateAllClearAlertHistory
291
377
  };
@@ -272,6 +272,21 @@ async function autoGenerateWatchlist(req, res, dependencies, config) {
272
272
  }
273
273
  );
274
274
 
275
+ // Update global rootdata collections for computation system
276
+ if (watchlistItems && watchlistItems.length > 0) {
277
+ const { updateWatchlistMembershipRootData, updatePIWatchlistData } = require('../rootdata/rootdata_aggregation_helpers');
278
+ const today = new Date().toISOString().split('T')[0];
279
+ const isPublic = false; // Auto-generated watchlists are always private
280
+
281
+ for (const item of watchlistItems) {
282
+ const piCid = String(item.cid);
283
+ // Update global WatchlistMembershipData/{date} document
284
+ await updateWatchlistMembershipRootData(db, logger, piCid, userCid, isPublic, today, 'add');
285
+ // Update PI-centric PopularInvestors/{piCid}/watchlistData
286
+ await updatePIWatchlistData(db, logger, piCid, userCid, today, 'add');
287
+ }
288
+ }
289
+
275
290
  logger.log('SUCCESS', `[autoGenerateWatchlist] Created auto-generated watchlist ${watchlistId} with ${watchlistItems.length} items for user ${userCid}`);
276
291
 
277
292
  return res.status(200).json({
@@ -141,22 +141,37 @@ async function createWatchlist(req, res, dependencies, config) {
141
141
  watchlistData.dynamicConfig = dynamicConfig;
142
142
  }
143
143
 
144
- const watchlistRef = db.collection(watchlistsCollection)
145
- .doc(String(userCid))
146
- .collection('lists')
147
- .doc(watchlistId);
148
-
149
- await watchlistRef.set(watchlistData);
144
+ // Write to new SignedInUsers path with dual-write to legacy path
145
+ const { writeWithMigration } = require('../core/path_resolution_helpers');
146
+ await writeWithMigration(
147
+ db,
148
+ 'signedInUsers',
149
+ 'watchlists',
150
+ { cid: userCid },
151
+ watchlistData,
152
+ {
153
+ isCollection: true,
154
+ merge: false,
155
+ dataType: 'watchlists',
156
+ config,
157
+ collectionRegistry: dependencies.collectionRegistry,
158
+ documentId: watchlistId,
159
+ dualWrite: true
160
+ }
161
+ );
150
162
 
151
163
  // Update global rootdata collection for computation system if static watchlist has items
152
164
  if (type === 'static' && items && items.length > 0) {
153
- const { updateWatchlistMembershipRootData } = require('../rootdata/rootdata_aggregation_helpers');
165
+ const { updateWatchlistMembershipRootData, updatePIWatchlistData } = require('../rootdata/rootdata_aggregation_helpers');
154
166
  const today = new Date().toISOString().split('T')[0];
155
167
  const isPublic = visibility === 'public';
156
168
 
157
169
  for (const item of items) {
158
170
  const piCid = String(item.cid);
171
+ // Update global WatchlistMembershipData/{date} document
159
172
  await updateWatchlistMembershipRootData(db, logger, piCid, userCid, isPublic, today, 'add');
173
+ // Update PI-centric PopularInvestors/{piCid}/watchlistData
174
+ await updatePIWatchlistData(db, logger, piCid, userCid, today, 'add');
160
175
  }
161
176
  }
162
177
 
@@ -193,19 +208,28 @@ async function updateWatchlist(req, res, dependencies, config) {
193
208
  }
194
209
 
195
210
  try {
196
- const watchlistsCollection = config.watchlistsCollection || 'watchlists';
197
- const watchlistRef = db.collection(watchlistsCollection)
198
- .doc(String(userCid))
199
- .collection('lists')
200
- .doc(id);
201
-
202
- const watchlistDoc = await watchlistRef.get();
211
+ // Read from new path with migration fallback
212
+ const { readWithMigration } = require('../core/path_resolution_helpers');
213
+ const readResult = await readWithMigration(
214
+ db,
215
+ 'signedInUsers',
216
+ 'watchlists',
217
+ { cid: userCid },
218
+ {
219
+ isCollection: true,
220
+ dataType: 'watchlists',
221
+ config,
222
+ logger,
223
+ collectionRegistry: dependencies.collectionRegistry,
224
+ documentId: id
225
+ }
226
+ );
203
227
 
204
- if (!watchlistDoc.exists) {
228
+ if (!readResult || !readResult.exists) {
205
229
  return res.status(404).json({ error: "Watchlist not found" });
206
230
  }
207
231
 
208
- const existingData = watchlistDoc.data();
232
+ const existingData = readResult.data();
209
233
 
210
234
  // Verify ownership
211
235
  if (existingData.createdBy !== Number(userCid)) {
@@ -262,7 +286,7 @@ async function updateWatchlist(req, res, dependencies, config) {
262
286
 
263
287
  // Update global rootdata collection for computation system
264
288
  // Compare old and new items to find added/removed PIs
265
- const { updateWatchlistMembershipRootData } = require('../rootdata/rootdata_aggregation_helpers');
289
+ const { updateWatchlistMembershipRootData, updatePIWatchlistData } = require('../rootdata/rootdata_aggregation_helpers');
266
290
  const oldItems = existingData.items || [];
267
291
  const newItems = items || [];
268
292
  const oldPiCids = new Set(oldItems.map(item => String(item.cid)));
@@ -274,7 +298,10 @@ async function updateWatchlist(req, res, dependencies, config) {
274
298
  for (const newItem of newItems) {
275
299
  const piCid = String(newItem.cid);
276
300
  if (!oldPiCids.has(piCid)) {
301
+ // Update global WatchlistMembershipData/{date} document
277
302
  await updateWatchlistMembershipRootData(db, logger, piCid, userCid, isPublic, today, 'add');
303
+ // Update PI-centric PopularInvestors/{piCid}/watchlistData
304
+ await updatePIWatchlistData(db, logger, piCid, userCid, today, 'add');
278
305
  }
279
306
  }
280
307
 
@@ -282,7 +309,10 @@ async function updateWatchlist(req, res, dependencies, config) {
282
309
  for (const oldItem of oldItems) {
283
310
  const piCid = String(oldItem.cid);
284
311
  if (!newPiCids.has(piCid)) {
312
+ // Update global WatchlistMembershipData/{date} document
285
313
  await updateWatchlistMembershipRootData(db, logger, piCid, userCid, isPublic, today, 'remove');
314
+ // Update PI-centric PopularInvestors/{piCid}/watchlistData
315
+ await updatePIWatchlistData(db, logger, piCid, userCid, today, 'remove');
286
316
  }
287
317
  }
288
318
  }
@@ -291,16 +321,35 @@ async function updateWatchlist(req, res, dependencies, config) {
291
321
  updates.dynamicConfig = dynamicConfig;
292
322
  }
293
323
 
294
- await watchlistRef.update(updates);
324
+ // Merge updates with existing data
325
+ const updatedData = { ...existingData, ...updates };
326
+
327
+ // Write to new SignedInUsers path with dual-write to legacy path
328
+ const { writeWithMigration } = require('../core/path_resolution_helpers');
329
+ await writeWithMigration(
330
+ db,
331
+ 'signedInUsers',
332
+ 'watchlists',
333
+ { cid: userCid },
334
+ updatedData,
335
+ {
336
+ isCollection: true,
337
+ merge: true,
338
+ dataType: 'watchlists',
339
+ config,
340
+ collectionRegistry: dependencies.collectionRegistry,
341
+ documentId: id,
342
+ dualWrite: true
343
+ }
344
+ );
295
345
 
296
346
  logger.log('SUCCESS', `[updateWatchlist] Updated watchlist ${id} for user ${userCid}`);
297
347
 
298
- const updatedDoc = await watchlistRef.get();
299
348
  return res.status(200).json({
300
349
  success: true,
301
350
  watchlist: {
302
- id: updatedDoc.id,
303
- ...updatedDoc.data()
351
+ id: id,
352
+ ...updatedData
304
353
  }
305
354
  });
306
355
 
@@ -343,8 +392,41 @@ async function deleteWatchlist(req, res, dependencies, config) {
343
392
  return res.status(403).json({ error: "You can only delete your own watchlists" });
344
393
  }
345
394
 
346
- // Delete watchlist
347
- await watchlistRef.delete();
395
+ // Update global rootdata collections before deleting
396
+ // Remove all PIs from global tracking if this is a static watchlist with items
397
+ if (watchlistData.type === 'static' && watchlistData.items && watchlistData.items.length > 0) {
398
+ const { updateWatchlistMembershipRootData, updatePIWatchlistData } = require('../rootdata/rootdata_aggregation_helpers');
399
+ const today = new Date().toISOString().split('T')[0];
400
+ const isPublic = watchlistData.visibility === 'public';
401
+
402
+ for (const item of watchlistData.items) {
403
+ const piCid = String(item.cid);
404
+ // Remove from global WatchlistMembershipData/{date} document
405
+ await updateWatchlistMembershipRootData(db, logger, piCid, userCid, isPublic, today, 'remove');
406
+ // Remove from PI-centric PopularInvestors/{piCid}/watchlistData
407
+ await updatePIWatchlistData(db, logger, piCid, userCid, today, 'remove');
408
+ }
409
+ }
410
+
411
+ // Delete watchlist from both new and legacy paths
412
+ const { writeWithMigration } = require('../core/path_resolution_helpers');
413
+ // Delete from new path
414
+ const newPathRef = db.collection('SignedInUsers')
415
+ .doc(String(userCid))
416
+ .collection('watchlists')
417
+ .doc(id);
418
+ await newPathRef.delete().catch(err => {
419
+ logger.log('WARN', `[deleteWatchlist] Failed to delete from new path: ${err.message}`);
420
+ });
421
+
422
+ // Delete from legacy path if it exists
423
+ const legacyPathRef = db.collection(config.watchlistsCollection || 'watchlists')
424
+ .doc(String(userCid))
425
+ .collection('lists')
426
+ .doc(id);
427
+ await legacyPathRef.delete().catch(err => {
428
+ logger.log('WARN', `[deleteWatchlist] Failed to delete from legacy path: ${err.message}`);
429
+ });
348
430
 
349
431
  // Remove from public watchlists if it was public
350
432
  if (watchlistData.visibility === 'public') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.548",
3
+ "version": "1.0.549",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [