bulltrackers-module 1.0.222 → 1.0.223

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.
@@ -68,7 +68,7 @@ async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, d
68
68
  const { logger } = dependencies;
69
69
  const dateToProcess = new Date(dateStr + 'T00:00:00Z');
70
70
 
71
- // 1. Version Check: Determine which calculations are *stale*
71
+ // 1. Version Check
72
72
  const dailyStatus = await fetchComputationStatus(dateStr, config, dependencies);
73
73
  const calcsToAttempt = [];
74
74
 
@@ -78,16 +78,13 @@ async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, d
78
78
  const currentHash = calc.hash;
79
79
 
80
80
  if (!storedStatus) {
81
- // New calculation
82
81
  calcsToAttempt.push(calc); continue;
83
82
  }
84
83
  if (typeof storedStatus === 'string' && currentHash && storedStatus !== currentHash) {
85
- // Code changed, must re-run
86
84
  logger.log('INFO', `[Versioning] ${cName}: Code Changed.`);
87
85
  calcsToAttempt.push(calc); continue;
88
86
  }
89
87
  if (storedStatus === true && currentHash) {
90
- // Migrating legacy status
91
88
  logger.log('INFO', `[Versioning] ${cName}: Upgrading legacy status.`);
92
89
  calcsToAttempt.push(calc); continue;
93
90
  }
@@ -108,12 +105,12 @@ async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, d
108
105
  if (!rootData) { logger.log('INFO', `[DateRunner] Root data missing for ${dateStr}. Skipping.`); return null; }
109
106
 
110
107
  // 3. Viability Check (Smart Execution Map)
111
- // Filter candidates: Remove any calculation that misses Root Data OR Dependencies
112
- const runnableCalcs = getViableCalculations(calcsToAttempt, rootData.status, dailyStatus);
108
+ // Filter candidates: Remove any calculation that misses Root Data OR Matches Stale Dependencies
109
+ // PASSED: computationManifest (needed for hash lookup)
110
+ const runnableCalcs = getViableCalculations(calcsToAttempt, computationManifest, rootData.status, dailyStatus);
113
111
 
114
112
  if (!runnableCalcs.length) {
115
- // If we had candidates but they were pruned, it means they are blocked.
116
- // logger.log('INFO', `[DateRunner] ${dateStr}: Candidates pruned due to missing deps/data.`);
113
+ // logger.log('INFO', `[DateRunner] ${dateStr}: Candidates pruned due to missing deps/data/stale hashes.`);
117
114
  return null;
118
115
  }
119
116
 
@@ -125,7 +122,6 @@ async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, d
125
122
 
126
123
  try {
127
124
  const calcsRunning = [...standardToRun, ...metaToRun];
128
- // Fetch dependencies for the *runnable* calculations
129
125
  const existingResults = await fetchExistingResults(dateStr, calcsRunning, computationManifest, config, dependencies, false);
130
126
  const prevDate = new Date(dateToProcess); prevDate.setUTCDate(prevDate.getUTCDate() - 1);
131
127
  const prevDateStr = prevDate.toISOString().slice(0, 10);
@@ -26,31 +26,46 @@ function checkRootDependencies(calcManifest, rootDataStatus) {
26
26
  * Filters candidates to only those that are strictly "viable" to run.
27
27
  * A calculation is Viable if:
28
28
  * 1. All required Root Data is present.
29
- * 2. All required Dependencies (from previous passes) are present in dailyStatus.
29
+ * 2. All required Dependencies are present AND their stored hash matches their current code hash.
30
30
  * * @param {Array} candidates - Calculations attempting to run in this pass.
31
+ * @param {Array} fullManifest - The complete manifest (to lookup dependency current hashes).
31
32
  * @param {Object} rootDataStatus - { hasPortfolio: bool, hasPrices: bool... }
32
33
  * @param {Object} dailyStatus - Map of { "calc-name": "hash" } for completed items.
33
34
  */
34
- function getViableCalculations(candidates, rootDataStatus, dailyStatus) {
35
+ function getViableCalculations(candidates, fullManifest, rootDataStatus, dailyStatus) {
35
36
  const viable = [];
37
+ const manifestMap = new Map(fullManifest.map(c => [normalizeName(c.name), c]));
36
38
 
37
39
  for (const calc of candidates) {
38
40
  // 1. Check Root Data
39
41
  const rootCheck = checkRootDependencies(calc, rootDataStatus);
40
42
  if (!rootCheck.canRun) {
41
- // Root data missing -> Impossible to run.
42
- continue;
43
+ continue; // Root data missing -> Impossible.
43
44
  }
44
45
 
45
- // 2. Check Dependencies
46
+ // 2. Check Dependencies (Strict Hash Verification)
46
47
  let dependenciesMet = true;
47
48
  if (calc.dependencies && calc.dependencies.length > 0) {
48
49
  for (const depName of calc.dependencies) {
49
50
  const normDep = normalizeName(depName);
51
+ const storedHash = dailyStatus[normDep];
52
+ const depManifest = manifestMap.get(normDep);
50
53
 
51
- // If a dependency is missing from dailyStatus, it failed in a previous pass.
52
- // Therefore, the current calculation is impossible.
53
- if (!dailyStatus[normDep]) {
54
+ // If dependency is missing from manifest, we can't verify it (shouldn't happen)
55
+ if (!depManifest) {
56
+ dependenciesMet = false;
57
+ break;
58
+ }
59
+
60
+ // CHECK: Does the dependency exist in DB?
61
+ if (!storedHash) {
62
+ dependenciesMet = false;
63
+ break;
64
+ }
65
+
66
+ // CHECK: Does the stored hash match the current code hash?
67
+ // This prevents running on stale data if a dependency failed to update.
68
+ if (storedHash !== depManifest.hash) {
54
69
  dependenciesMet = false;
55
70
  break;
56
71
  }
@@ -75,7 +90,6 @@ async function checkRootDataAvailability(dateStr, config, dependencies, earliest
75
90
 
76
91
  try {
77
92
  const tasks = [];
78
- // Only check data sources if the date is after the earliest known data point
79
93
  if (dateToProcess >= earliestDates.portfolio) {
80
94
  tasks.push(getPortfolioPartRefs(config, dependencies, dateStr).then(r => { portfolioRefs = r; hasPortfolio = !!r.length; }));
81
95
  }
@@ -94,7 +108,6 @@ async function checkRootDataAvailability(dateStr, config, dependencies, earliest
94
108
 
95
109
  await Promise.all(tasks);
96
110
 
97
- // If ABSOLUTELY NO data exists, we can return null early
98
111
  if (!(hasPortfolio || hasInsights || hasSocial || hasHistory || hasPrices)) return null;
99
112
 
100
113
  return {
@@ -103,7 +116,7 @@ async function checkRootDataAvailability(dateStr, config, dependencies, earliest
103
116
  todayInsights: insightsData,
104
117
  todaySocialPostInsights: socialData,
105
118
  status: { hasPortfolio, hasInsights, hasSocial, hasHistory, hasPrices },
106
- yesterdayPortfolioRefs: null // Filled later by StandardExecutor if needed
119
+ yesterdayPortfolioRefs: null
107
120
  };
108
121
  } catch (err) {
109
122
  logger.log('ERROR', `Error checking data: ${err.message}`);
@@ -111,10 +124,6 @@ async function checkRootDataAvailability(dateStr, config, dependencies, earliest
111
124
  }
112
125
  }
113
126
 
114
- /**
115
- * Checks if any price data exists in the collection.
116
- * Note: Uses a lightweight limit(1) query.
117
- */
118
127
  async function checkPriceAvailability(config, db) {
119
128
  try {
120
129
  const collection = config.priceCollection || 'asset_prices';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.222",
3
+ "version": "1.0.223",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [