bulltrackers-module 1.0.579 → 1.0.581

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.
@@ -7,7 +7,7 @@
7
7
  /**
8
8
  * @fileoverview Dynamic Manifest Builder - Handles Topological Sort and Auto-Discovery.
9
9
  * UPDATED: Removed Automatic Infra Hashing. Now relies strictly on SYSTEM_EPOCH.
10
- * UPDATED: Whitelisted 'rootDataSeries' and 'dependencySeries' metadata fields.
10
+ * UPDATED: Whitelisted 'rootDataSeries', 'dependencySeries', and 'mandatoryRoots' metadata fields.
11
11
  */
12
12
  const { generateCodeHash, LEGACY_MAPPING } = require('../topology/HashManager.js');
13
13
  const { normalizeName } = require('../utils/utils');
@@ -31,31 +31,22 @@ const LAYER_GROUPS = {
31
31
  * Heuristic to estimate the "weight" of a calculation based on its output structure.
32
32
  */
33
33
  function estimateComplexity(Class, metadata) {
34
- let weight = 1.0; // Base weight (for single aggregate values)
34
+ let weight = 1.0;
35
35
 
36
36
  try {
37
37
  const schema = typeof Class.getSchema === 'function' ? Class.getSchema() : {};
38
-
39
- // 1. Detect Map-like outputs (per-ticker, per-sector, per-user)
40
- // If the schema uses patternProperties, it's likely a dynamic map.
41
38
  if (schema.patternProperties || (schema.type === 'object' && !schema.properties)) {
42
- weight *= 3.0; // Higher weight for dynamic maps (e.g., Per Sector/Ticker)
39
+ weight *= 3.0;
43
40
  }
44
-
45
- // 2. Metadata hints
46
41
  const name = Class.name.toLowerCase();
47
42
  if (name.includes('perstock') || name.includes('perticker')) weight *= 2.0;
48
- if (name.includes('peruser')) weight *= 10.0; // Very high cost
43
+ if (name.includes('peruser')) weight *= 10.0;
49
44
 
50
- // 3. Dependency hints
51
45
  if (metadata.rootDataDependencies && metadata.rootDataDependencies.includes('portfolio')) {
52
- // Portfolio-based calcs usually iterate over all users in the StandardExecutor
53
46
  weight *= 1.5;
54
47
  }
55
48
 
56
- } catch (e) {
57
- // Fallback to base weight if schema is missing/broken
58
- }
49
+ } catch (e) { }
59
50
 
60
51
  return weight;
61
52
  }
@@ -122,9 +113,6 @@ function getDependencySet(endpoints, adjacencyList) {
122
113
  return required;
123
114
  }
124
115
 
125
- /**
126
- * Helper: Detects cycles using Tarjan's SCC Algorithm.
127
- */
128
116
  function detectCircularDependencies(manifestMap) {
129
117
  let index = 0;
130
118
  const stack = [];
@@ -209,7 +197,6 @@ function buildManifest(productLinesToRun = [], calculations) {
209
197
  const codeStr = Class.toString();
210
198
  const selfCodeHash = generateCodeHash(codeStr);
211
199
 
212
- // [UPDATED] Composite Hash now depends ONLY on Code + Epoch + Layers
213
200
  let compositeHashString = selfCodeHash + `|EPOCH:${SYSTEM_EPOCH}`;
214
201
 
215
202
  const usedDeps = [];
@@ -254,9 +241,10 @@ function buildManifest(productLinesToRun = [], calculations) {
254
241
  isPage: metadata.isPage === true,
255
242
  isHistorical: metadata.isHistorical !== undefined ? metadata.isHistorical : false,
256
243
  rootDataDependencies: metadata.rootDataDependencies || [],
257
- // [NEW] Pass Series Configuration
244
+ // [NEW] Pass Series & Mandatory Config
258
245
  rootDataSeries: metadata.rootDataSeries || null,
259
246
  dependencySeries: metadata.dependencySeries || null,
247
+ mandatoryRoots: metadata.mandatoryRoots || [], // [NEW]
260
248
 
261
249
  canHaveMissingRoots: metadata.canHaveMissingRoots || false,
262
250
  userType: metadata.userType,
@@ -300,9 +288,7 @@ function buildManifest(productLinesToRun = [], calculations) {
300
288
 
301
289
  const productLineEndpoints = [];
302
290
  const runAll = !productLinesToRun || productLinesToRun.length === 0 || productLinesToRun.includes('*');
303
- // Respect the product lines to run, but don't force core calculations to be run if not explicitly requested
304
291
  for (const [name, entry] of manifestMap.entries()) {
305
- // Removed "|| entry.sourcePackage === 'core'"
306
292
  if (runAll || productLinesToRun.includes(entry.category)) {
307
293
  productLineEndpoints.push(name);
308
294
  }
@@ -1,9 +1,7 @@
1
1
  /**
2
2
  * @fileoverview Checks availability of root data via the Root Data Index.
3
3
  * REFACTORED: Fully supports granular flags for PI, Signed-In Users, Rankings, and Verification.
4
- * FIXED: Removed permissive fallbacks to enforce strict userType availability rules.
5
- * UPDATED: Added strict social data checking for Popular Investors and Signed-In Users.
6
- * UPDATED: Added Optimistic Series Permission. Allows execution if "Today's" data is missing but a Lookback Series is requested.
4
+ * UPDATED: Enforces 'mandatoryRoots' metadata to override permissive flags.
7
5
  */
8
6
  const { normalizeName } = require('../utils/utils');
9
7
 
@@ -25,49 +23,46 @@ function checkRootDependencies(calcManifest, rootDataStatus) {
25
23
  let isAvailable = false;
26
24
 
27
25
  if (dep === 'portfolio') {
28
- if (userType === 'speculator' && rootDataStatus.speculatorPortfolio) isAvailable = true;
29
- else if (userType === 'normal' && rootDataStatus.normalPortfolio) isAvailable = true;
30
- else if (userType === 'popular_investor' && rootDataStatus.piPortfolios) isAvailable = true;
31
- else if (userType === 'signed_in_user' && rootDataStatus.signedInUserPortfolio) isAvailable = true;
32
- else if (userType === 'all' && rootDataStatus.hasPortfolio) isAvailable = true;
26
+ if (userType === 'speculator' && rootDataStatus.speculatorPortfolio) isAvailable = true;
27
+ else if (userType === 'normal' && rootDataStatus.normalPortfolio) isAvailable = true;
28
+ else if (userType === 'popular_investor' && rootDataStatus.piPortfolios) isAvailable = true;
29
+ else if (userType === 'signed_in_user' && rootDataStatus.signedInUserPortfolio) isAvailable = true;
30
+ else if (userType === 'all' && rootDataStatus.hasPortfolio) isAvailable = true;
33
31
 
34
32
  if (!isAvailable) {
35
33
  // [OPTIMIZATION] Optimistic Series Check
36
- // If Today's Portfolio is missing, but the computation explicitly asks for a Series (History),
37
- // we allow it to proceed optimistically, assuming historical data might exist.
38
34
  if (calcManifest.rootDataSeries && calcManifest.rootDataSeries[dep]) {
39
35
  available.push('portfolio');
40
36
  continue;
41
37
  }
42
38
 
43
- if (userType === 'speculator') missing.push('speculatorPortfolio');
44
- else if (userType === 'normal') missing.push('normalPortfolio');
39
+ if (userType === 'speculator') missing.push('speculatorPortfolio');
40
+ else if (userType === 'normal') missing.push('normalPortfolio');
45
41
  else if (userType === 'popular_investor') missing.push('piPortfolios');
46
- else if (userType === 'signed_in_user') missing.push('signedInUserPortfolio');
47
- else missing.push('portfolio');
42
+ else if (userType === 'signed_in_user') missing.push('signedInUserPortfolio');
43
+ else missing.push('portfolio');
48
44
  } else {
49
45
  available.push('portfolio');
50
46
  }
51
47
  }
52
- else if (dep === 'history') {
53
- if (userType === 'speculator' && rootDataStatus.speculatorHistory) isAvailable = true;
54
- else if (userType === 'normal' && rootDataStatus.normalHistory) isAvailable = true;
55
- else if (userType === 'popular_investor' && rootDataStatus.piHistory) isAvailable = true;
56
- else if (userType === 'signed_in_user' && rootDataStatus.signedInUserHistory) isAvailable = true;
57
- else if (userType === 'all' && rootDataStatus.hasHistory) isAvailable = true;
48
+ else if (dep === 'history') {
49
+ if (userType === 'speculator' && rootDataStatus.speculatorHistory) isAvailable = true;
50
+ else if (userType === 'normal' && rootDataStatus.normalHistory) isAvailable = true;
51
+ else if (userType === 'popular_investor' && rootDataStatus.piHistory) isAvailable = true;
52
+ else if (userType === 'signed_in_user' && rootDataStatus.signedInUserHistory) isAvailable = true;
53
+ else if (userType === 'all' && rootDataStatus.hasHistory) isAvailable = true;
58
54
 
59
55
  if (!isAvailable) {
60
- // [OPTIMIZATION] Optimistic Series Check
61
56
  if (calcManifest.rootDataSeries && calcManifest.rootDataSeries[dep]) {
62
57
  available.push('history');
63
58
  continue;
64
59
  }
65
60
 
66
- if (userType === 'speculator') missing.push('speculatorHistory');
67
- else if (userType === 'normal') missing.push('normalHistory');
61
+ if (userType === 'speculator') missing.push('speculatorHistory');
62
+ else if (userType === 'normal') missing.push('normalHistory');
68
63
  else if (userType === 'popular_investor') missing.push('piHistory');
69
- else if (userType === 'signed_in_user') missing.push('signedInUserHistory');
70
- else missing.push('history');
64
+ else if (userType === 'signed_in_user') missing.push('signedInUserHistory');
65
+ else missing.push('history');
71
66
  } else {
72
67
  available.push('history');
73
68
  }
@@ -77,7 +72,6 @@ function checkRootDependencies(calcManifest, rootDataStatus) {
77
72
  isAvailable = true;
78
73
  available.push('rankings');
79
74
  } else {
80
- // [OPTIMIZATION] Optimistic Series Check
81
75
  if (calcManifest.rootDataSeries && calcManifest.rootDataSeries[dep]) {
82
76
  available.push('rankings');
83
77
  } else {
@@ -98,7 +92,6 @@ function checkRootDependencies(calcManifest, rootDataStatus) {
98
92
  isAvailable = true;
99
93
  available.push('insights');
100
94
  } else {
101
- // [OPTIMIZATION] Optimistic Series Check
102
95
  if (calcManifest.rootDataSeries && calcManifest.rootDataSeries[dep]) {
103
96
  available.push('insights');
104
97
  } else {
@@ -108,25 +101,24 @@ function checkRootDependencies(calcManifest, rootDataStatus) {
108
101
  }
109
102
  else if (dep === 'social') {
110
103
  if (userType === 'popular_investor') {
111
- if (rootDataStatus.hasPISocial) isAvailable = true;
104
+ if (rootDataStatus.hasPISocial) isAvailable = true;
112
105
  } else if (userType === 'signed_in_user') {
113
- if (rootDataStatus.hasSignedInSocial) isAvailable = true;
106
+ if (rootDataStatus.hasSignedInSocial) isAvailable = true;
114
107
  } else {
115
- if (rootDataStatus.hasSocial) isAvailable = true;
108
+ if (rootDataStatus.hasSocial) isAvailable = true;
116
109
  }
117
110
 
118
111
  if (isAvailable) {
119
112
  available.push('social');
120
113
  } else {
121
- // [OPTIMIZATION] Optimistic Series Check
122
114
  if (calcManifest.rootDataSeries && calcManifest.rootDataSeries[dep]) {
123
115
  available.push('social');
124
116
  continue;
125
117
  }
126
118
 
127
- if (userType === 'popular_investor') missing.push('piSocial');
119
+ if (userType === 'popular_investor') missing.push('piSocial');
128
120
  else if (userType === 'signed_in_user') missing.push('signedInSocial');
129
- else missing.push('social');
121
+ else missing.push('social');
130
122
  }
131
123
  }
132
124
  else if (dep === 'price') {
@@ -134,7 +126,6 @@ function checkRootDependencies(calcManifest, rootDataStatus) {
134
126
  isAvailable = true;
135
127
  available.push('price');
136
128
  } else {
137
- // [OPTIMIZATION] Optimistic Series Check
138
129
  if (calcManifest.rootDataSeries && calcManifest.rootDataSeries[dep]) {
139
130
  available.push('price');
140
131
  } else {
@@ -142,13 +133,11 @@ function checkRootDependencies(calcManifest, rootDataStatus) {
142
133
  }
143
134
  }
144
135
  }
145
- // [NEW] New Root Data Types for Profile Metrics
146
136
  else if (dep === 'ratings') {
147
137
  if (rootDataStatus.piRatings) {
148
138
  isAvailable = true;
149
139
  available.push('ratings');
150
140
  } else {
151
- // [OPTIMIZATION] Optimistic Series Check (Vital for sparse data like ratings)
152
141
  if (calcManifest.rootDataSeries && calcManifest.rootDataSeries[dep]) {
153
142
  available.push('ratings');
154
143
  } else {
@@ -161,7 +150,6 @@ function checkRootDependencies(calcManifest, rootDataStatus) {
161
150
  isAvailable = true;
162
151
  available.push('pageViews');
163
152
  } else {
164
- // [OPTIMIZATION] Optimistic Series Check
165
153
  if (calcManifest.rootDataSeries && calcManifest.rootDataSeries[dep]) {
166
154
  available.push('pageViews');
167
155
  } else {
@@ -174,7 +162,6 @@ function checkRootDependencies(calcManifest, rootDataStatus) {
174
162
  isAvailable = true;
175
163
  available.push('watchlist');
176
164
  } else {
177
- // [OPTIMIZATION] Optimistic Series Check
178
165
  if (calcManifest.rootDataSeries && calcManifest.rootDataSeries[dep]) {
179
166
  available.push('watchlist');
180
167
  } else {
@@ -187,9 +174,6 @@ function checkRootDependencies(calcManifest, rootDataStatus) {
187
174
  isAvailable = true;
188
175
  available.push('alerts');
189
176
  } else {
190
- // [OPTIMIZATION] Optimistic Series Check
191
- // This explicitly solves the Alert History edge case where today's snapshot
192
- // might be missing but the lookback period contains data.
193
177
  if (calcManifest.rootDataSeries && calcManifest.rootDataSeries[dep]) {
194
178
  available.push('alerts');
195
179
  } else {
@@ -198,8 +182,16 @@ function checkRootDependencies(calcManifest, rootDataStatus) {
198
182
  }
199
183
  }
200
184
  }
185
+
186
+ // [NEW] Enforce Mandatory Roots (defined by computation)
187
+ // This allows granular control: "I need portfolio, but ratings are optional"
188
+ if (calcManifest.mandatoryRoots && Array.isArray(calcManifest.mandatoryRoots) && calcManifest.mandatoryRoots.length > 0) {
189
+ const missingMandatory = calcManifest.mandatoryRoots.filter(r => !available.includes(r));
190
+ if (missingMandatory.length > 0) {
191
+ return { canRun: false, missing, available };
192
+ }
193
+ }
201
194
 
202
- // [FIX] Enforce canHaveMissingRoots logic:
203
195
  if (canHaveMissingRoots) {
204
196
  const canRun = available.length > 0;
205
197
  return { canRun, missing, available };
@@ -236,10 +228,6 @@ function getViableCalculations(candidates, fullManifest, rootDataStatus, dailySt
236
228
  return viable;
237
229
  }
238
230
 
239
- /**
240
- * Checks data availability by reading the centralized index.
241
- * Extracts all granular details to support extended user types.
242
- */
243
231
  async function checkRootDataAvailability(dateStr, config, dependencies, earliestDates) {
244
232
  const { logger, db } = dependencies;
245
233
 
@@ -252,20 +240,15 @@ async function checkRootDataAvailability(dateStr, config, dependencies, earliest
252
240
 
253
241
  return {
254
242
  status: {
255
- // Global flags
256
243
  hasPortfolio: !!data.hasPortfolio,
257
244
  hasHistory: !!data.hasHistory,
258
- hasSocial: !!data.hasSocial, // Represents generic/asset posts
245
+ hasSocial: !!data.hasSocial,
259
246
  hasInsights: !!data.hasInsights,
260
247
  hasPrices: !!data.hasPrices,
261
-
262
- // Granular flags - Existing
263
248
  speculatorPortfolio: !!details.speculatorPortfolio,
264
249
  normalPortfolio: !!details.normalPortfolio,
265
250
  speculatorHistory: !!details.speculatorHistory,
266
251
  normalHistory: !!details.normalHistory,
267
-
268
- // Granular flags - NEW
269
252
  piRankings: !!details.piRankings,
270
253
  piPortfolios: !!details.piPortfolios,
271
254
  piDeepPortfolios: !!details.piDeepPortfolios,
@@ -273,18 +256,12 @@ async function checkRootDataAvailability(dateStr, config, dependencies, earliest
273
256
  signedInUserPortfolio: !!details.signedInUserPortfolio,
274
257
  signedInUserHistory: !!details.signedInUserHistory,
275
258
  signedInUserVerification: !!details.signedInUserVerification,
276
-
277
- // [UPDATED] Granular Social Flags
278
259
  hasPISocial: !!details.hasPISocial || !!data.hasPISocial,
279
260
  hasSignedInSocial: !!details.hasSignedInSocial || !!data.hasSignedInSocial,
280
-
281
- // [NEW] New Root Data Types for Profile Metrics
282
261
  piRatings: !!details.piRatings,
283
262
  piPageViews: !!details.piPageViews,
284
263
  watchlistMembership: !!details.watchlistMembership,
285
264
  piAlertHistory: !!details.piAlertHistory,
286
-
287
- // [NEW] Global Helper Data
288
265
  piMasterList: !!details.piMasterList
289
266
  },
290
267
  portfolioRefs: null,
@@ -297,28 +274,12 @@ async function checkRootDataAvailability(dateStr, config, dependencies, earliest
297
274
  logger.log('WARN', `[Availability] Index not found for ${dateStr}. Assuming NO data.`);
298
275
  return {
299
276
  status: {
300
- hasPortfolio: false,
301
- hasHistory: false,
302
- hasSocial: false,
303
- hasInsights: false,
304
- hasPrices: false,
305
- speculatorPortfolio: false,
306
- normalPortfolio: false,
307
- speculatorHistory: false,
308
- normalHistory: false,
309
- piRankings: false,
310
- piPortfolios: false,
311
- piDeepPortfolios: false,
312
- piHistory: false,
313
- signedInUserPortfolio: false,
314
- signedInUserHistory: false,
315
- signedInUserVerification: false,
316
- hasPISocial: false,
317
- hasSignedInSocial: false,
318
- piRatings: false,
319
- piPageViews: false,
320
- watchlistMembership: false,
321
- piAlertHistory: false
277
+ hasPortfolio: false, hasHistory: false, hasSocial: false, hasInsights: false, hasPrices: false,
278
+ speculatorPortfolio: false, normalPortfolio: false, speculatorHistory: false, normalHistory: false,
279
+ piRankings: false, piPortfolios: false, piDeepPortfolios: false, piHistory: false,
280
+ signedInUserPortfolio: false, signedInUserHistory: false, signedInUserVerification: false,
281
+ hasPISocial: false, hasSignedInSocial: false,
282
+ piRatings: false, piPageViews: false, watchlistMembership: false, piAlertHistory: false
322
283
  }
323
284
  };
324
285
  }
@@ -209,56 +209,99 @@ async function getUserComputations(req, res, dependencies, config) {
209
209
 
210
210
  const isDevOverrideActive = devOverride && devOverride.enabled && devOverride.fakeCopiedPIs.length > 0;
211
211
 
212
+ // Check if any of the requested computations are meta computations
213
+ const firstCompName = computationNames[0];
214
+ const firstCompMetadata = computationMetadata[firstCompName];
215
+ const isMetaComputation = firstCompMetadata && firstCompMetadata.type === 'meta';
216
+
212
217
  let datesToCheck = [today];
213
218
 
214
219
  if (mode === 'latest') {
215
- // Use same logic as data-status: search backwards and verify user exists
216
- // This ensures we find the same date that data-status found
217
- const firstCompName = computationNames[0];
218
- const maxDaysBack = 30; // Match data-status search window
219
220
  let foundDate = null;
220
221
 
221
- for (let daysBack = 0; daysBack < maxDaysBack; daysBack++) {
222
- const checkDate = new Date();
223
- checkDate.setDate(checkDate.getDate() - daysBack);
224
- const dateStr = checkDate.toISOString().split('T')[0];
222
+ if (isMetaComputation) {
223
+ // For meta computations: check today first, then look back 7 days
224
+ const maxDaysBack = 7;
225
225
 
226
- try {
227
- // Check if page/computation document exists for this user and date
228
- // For computations using pages subcollection, checkPiInComputationDate handles it
229
- // For other computations, check the main document first
230
- const { found } = await checkPiInComputationDate(
231
- db,
232
- insightsCollection,
233
- resultsSub,
234
- compsSub,
235
- category,
236
- firstCompName,
237
- dateStr,
238
- String(effectiveCid),
239
- logger
240
- );
226
+ for (let daysBack = 0; daysBack < maxDaysBack; daysBack++) {
227
+ const checkDate = new Date();
228
+ checkDate.setDate(checkDate.getDate() - daysBack);
229
+ const dateStr = checkDate.toISOString().split('T')[0];
230
+
231
+ try {
232
+ // For meta computations, just check if the document exists
233
+ const computationRef = db.collection(insightsCollection)
234
+ .doc(dateStr)
235
+ .collection(resultsSub)
236
+ .doc(category)
237
+ .collection(compsSub)
238
+ .doc(firstCompName);
239
+
240
+ const computationDoc = await computationRef.get();
241
+
242
+ if (computationDoc.exists) {
243
+ foundDate = dateStr;
244
+ if (dateStr !== today) {
245
+ logger.log('INFO', `[getUserComputations] Meta computation ${firstCompName} found on fallback date ${foundDate} (today: ${today})`);
246
+ } else {
247
+ logger.log('INFO', `[getUserComputations] Meta computation ${firstCompName} found on today's date`);
248
+ }
249
+ break; // Found document, stop searching
250
+ }
251
+ } catch (error) {
252
+ // Continue to next date if error
253
+ logger.log('DEBUG', `[getUserComputations] Error checking date ${dateStr} for meta computation:`, error.message);
254
+ continue;
255
+ }
256
+ }
257
+ } else {
258
+ // For user-specific computations: use same logic as data-status
259
+ // Search backwards and verify user exists
260
+ const maxDaysBack = 30; // Match data-status search window
261
+
262
+ for (let daysBack = 0; daysBack < maxDaysBack; daysBack++) {
263
+ const checkDate = new Date();
264
+ checkDate.setDate(checkDate.getDate() - daysBack);
265
+ const dateStr = checkDate.toISOString().split('T')[0];
241
266
 
242
- if (found) {
243
- foundDate = dateStr;
244
- if (dateStr !== today) {
245
- logger.log('INFO', `[getUserComputations] Using fallback date ${foundDate} for effective CID ${effectiveCid} (today: ${today})`);
246
- } else {
247
- logger.log('INFO', `[getUserComputations] Found computation for effective CID ${effectiveCid} on today's date`);
267
+ try {
268
+ // Check if page/computation document exists for this user and date
269
+ // For computations using pages subcollection, checkPiInComputationDate handles it
270
+ // For other computations, check the main document first
271
+ const { found } = await checkPiInComputationDate(
272
+ db,
273
+ insightsCollection,
274
+ resultsSub,
275
+ compsSub,
276
+ category,
277
+ firstCompName,
278
+ dateStr,
279
+ String(effectiveCid),
280
+ logger
281
+ );
282
+
283
+ if (found) {
284
+ foundDate = dateStr;
285
+ if (dateStr !== today) {
286
+ logger.log('INFO', `[getUserComputations] Using fallback date ${foundDate} for effective CID ${effectiveCid} (today: ${today})`);
287
+ } else {
288
+ logger.log('INFO', `[getUserComputations] Found computation for effective CID ${effectiveCid} on today's date`);
289
+ }
290
+ break; // Found user, stop searching
248
291
  }
249
- break; // Found user, stop searching
292
+ } catch (error) {
293
+ // Continue to next date if error
294
+ logger.log('DEBUG', `[getUserComputations] Error checking date ${dateStr}:`, error.message);
295
+ continue;
250
296
  }
251
- } catch (error) {
252
- // Continue to next date if error
253
- logger.log('DEBUG', `[getUserComputations] Error checking date ${dateStr}:`, error.message);
254
- continue;
255
297
  }
256
298
  }
257
299
 
258
300
  if (foundDate) {
259
301
  datesToCheck = [foundDate];
260
302
  } else {
261
- logger.log('WARN', `[getUserComputations] No computation data found for CID ${effectiveCid} in last ${maxDaysBack} days. Frontend will use fallback.`);
303
+ const maxDaysBack = isMetaComputation ? 7 : 30;
304
+ logger.log('WARN', `[getUserComputations] No computation data found for ${isMetaComputation ? 'meta computation' : `CID ${effectiveCid}`} in last ${maxDaysBack} days. Frontend will use fallback.`);
262
305
  return res.status(200).json({
263
306
  status: 'success',
264
307
  userCid: String(effectiveCid),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.579",
3
+ "version": "1.0.581",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [