bulltrackers-module 1.0.226 → 1.0.227

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.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @fileoverview Main Orchestrator. Coordinates the topological execution.
3
- * UPDATED: Strict Dependency & Hash Cascade Logic with Explicit Failure Marking.
3
+ * UPDATED: Includes defensive checks for Logger compatibility.
4
4
  */
5
5
  const { normalizeName } = require('./utils/utils');
6
6
  const { checkRootDataAvailability } = require('./data/AvailabilityChecker');
@@ -31,26 +31,17 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
31
31
  runnable: [],
32
32
  blocked: [], // Missing Root Data
33
33
  failedDependency: [], // Missing Dependency OR Stale Dependency
34
- reRuns: [], // Hash Mismatch (Triggered by self code change OR upstream cascade)
34
+ reRuns: [], // Hash Mismatch
35
35
  skipped: [] // Already done & valid
36
36
  };
37
37
 
38
- // Helper: Is a dependency satisfied AND valid (matching hash)?
39
38
  const isDepSatisfied = (depName, dailyStatus, manifestMap) => {
40
39
  const norm = normalizeName(depName);
41
40
  const storedDepHash = dailyStatus[norm];
42
41
  const depManifest = manifestMap.get(norm);
43
42
 
44
- // 1. Must exist in DB. If missing, we cannot run.
45
43
  if (!storedDepHash) return false;
46
-
47
- // 2. Must exist in Manifest (Sanity check)
48
44
  if (!depManifest) return false;
49
-
50
- // 3. STRICT: The dependency's stored hash must match its current manifest hash.
51
- // If 'A' changed code, 'A' has a new hash. If we are running 'B' (Pass 2),
52
- // we expect 'A' (Pass 1) to have already run and updated the DB with the NEW hash.
53
- // If DB still has OLD hash, 'A' failed or didn't run. 'B' is unsafe to run.
54
45
  if (storedDepHash !== depManifest.hash) return false;
55
46
 
56
47
  return true;
@@ -61,7 +52,7 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
61
52
  const storedHash = dailyStatus[cName];
62
53
  const currentHash = calc.hash;
63
54
 
64
- // 1. Root Data Check (FATAL)
55
+ // 1. Root Data Check
65
56
  const missingRoots = [];
66
57
  if (calc.rootDataDependencies) {
67
58
  for (const dep of calc.rootDataDependencies) {
@@ -75,12 +66,10 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
75
66
 
76
67
  if (missingRoots.length > 0) {
77
68
  report.blocked.push({ name: cName, reason: `Missing Root Data: ${missingRoots.join(', ')}` });
78
- continue; // Cannot proceed
69
+ continue;
79
70
  }
80
71
 
81
- // 2. Dependency Check (FATAL)
82
- // Since we are in a topological pass system, all dependencies SHOULD be satisfied
83
- // by previous passes. If not, it is a fatal error for this calculation.
72
+ // 2. Dependency Check
84
73
  const missingDeps = [];
85
74
  if (calc.dependencies) {
86
75
  for (const dep of calc.dependencies) {
@@ -92,23 +81,17 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
92
81
 
93
82
  if (missingDeps.length > 0) {
94
83
  report.failedDependency.push({ name: cName, missing: missingDeps });
95
- continue; // Cannot proceed
84
+ continue;
96
85
  }
97
86
 
98
87
  // 3. Hash / State Check
99
88
  if (!storedHash) {
100
- // Case A: No result exists (or status is 'false' from previous failure) -> RUN
101
- // Note: If storedHash is boolean false, !storedHash is true, so we retry.
102
89
  report.runnable.push(calc);
103
90
  } else if (storedHash !== currentHash) {
104
- // Case B: Result exists, but hash mismatch -> RE-RUN
105
- // This covers code changes in THIS calc, AND cascading changes from dependencies
106
91
  report.reRuns.push({ name: cName, oldHash: storedHash, newHash: currentHash });
107
92
  } else if (storedHash === true) {
108
- // Case C: Legacy boolean status -> RE-RUN (Upgrade to hash)
109
93
  report.reRuns.push({ name: cName, reason: 'Legacy Upgrade' });
110
94
  } else {
111
- // Case D: Result exists, Hash Matches -> SKIP
112
95
  report.skipped.push({ name: cName });
113
96
  }
114
97
  }
@@ -119,13 +102,12 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
119
102
  async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, dependencies, computationManifest) {
120
103
  const { logger } = dependencies;
121
104
  const orchestratorPid = generateProcessId(PROCESS_TYPES.ORCHESTRATOR, passToRun, dateStr);
122
-
123
105
  const dateToProcess = new Date(dateStr + 'T00:00:00Z');
124
106
 
125
107
  // 1. Fetch State
126
108
  const dailyStatus = await fetchComputationStatus(dateStr, config, dependencies);
127
109
 
128
- // 2. Check Data Availability (One shot)
110
+ // 2. Check Data Availability
129
111
  const earliestDates = {
130
112
  portfolio: new Date('2025-09-25T00:00:00Z'),
131
113
  history: new Date('2025-11-05T00:00:00Z'),
@@ -141,11 +123,17 @@ async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, d
141
123
  const manifestMap = new Map(computationManifest.map(c => [normalizeName(c.name), c]));
142
124
  const analysisReport = analyzeDateExecution(dateStr, calcsInThisPass, rootStatus, dailyStatus, manifestMap);
143
125
 
144
- // 4. LOG ANALYSIS
145
- logger.logDateAnalysis(dateStr, analysisReport);
126
+ // 4. LOG ANALYSIS (SAFEGUARDED)
127
+ if (logger && typeof logger.logDateAnalysis === 'function') {
128
+ logger.logDateAnalysis(dateStr, analysisReport);
129
+ } else {
130
+ // Fallback if injected logger is outdated
131
+ const logMsg = `[Analysis] Date: ${dateStr} | Runnable: ${analysisReport.runnable.length} | Blocked: ${analysisReport.blocked.length} | ReRuns: ${analysisReport.reRuns.length}`;
132
+ if (logger && logger.info) logger.info(logMsg);
133
+ else console.log(logMsg);
134
+ }
146
135
 
147
- // 5. MARK FAILURES (Explicitly write 'false' to DB for blocked items)
148
- // This prevents UI/downstream consumers from waiting indefinitely.
136
+ // 5. MARK FAILURES (Write 'false' to DB for blocked items)
149
137
  const failureUpdates = {};
150
138
  analysisReport.blocked.forEach(item => failureUpdates[item.name] = false);
151
139
  analysisReport.failedDependency.forEach(item => failureUpdates[item.name] = false);
@@ -163,11 +151,12 @@ async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, d
163
151
  const finalRunList = calcsInThisPass.filter(c => calcsToRunNames.has(normalizeName(c.name)));
164
152
 
165
153
  if (!finalRunList.length) {
166
- // Nothing to run (everything either skipped, blocked, or failed)
167
154
  return { date: dateStr, updates: {}, skipped: analysisReport.skipped.length };
168
155
  }
169
156
 
170
- logger.log('INFO', `[Orchestrator] Executing ${finalRunList.length} calculations for ${dateStr}`, { processId: orchestratorPid });
157
+ if (logger && logger.log) {
158
+ logger.log('INFO', `[Orchestrator] Executing ${finalRunList.length} calculations for ${dateStr}`, { processId: orchestratorPid });
159
+ }
171
160
 
172
161
  const standardToRun = finalRunList.filter(c => c.type === 'standard');
173
162
  const metaToRun = finalRunList.filter(c => c.type === 'meta');
@@ -177,11 +166,8 @@ async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, d
177
166
  try {
178
167
  const calcsRunning = [...standardToRun, ...metaToRun];
179
168
 
180
- // Fetch dependencies (Previous pass results)
181
- // includeSelf=false: We are re-running, so we ignore our own old results.
169
+ // Fetch dependencies
182
170
  const existingResults = await fetchExistingResults(dateStr, calcsRunning, computationManifest, config, dependencies, false);
183
-
184
- // Fetch Yesterday's results (if Historical)
185
171
  const prevDate = new Date(dateToProcess); prevDate.setUTCDate(prevDate.getUTCDate() - 1);
186
172
  const prevDateStr = prevDate.toISOString().slice(0, 10);
187
173
  const previousResults = await fetchExistingResults(prevDateStr, calcsRunning, computationManifest, config, dependencies, true);
@@ -196,9 +182,11 @@ async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, d
196
182
  }
197
183
 
198
184
  } catch (err) {
199
- // If execution CRASHES (code bug, timeout), we log and throw.
200
- // Pub/Sub will retry this message. This is correct for transient errors.
201
- logger.log('ERROR', `[Orchestrator] Failed execution for ${dateStr}`, { processId: orchestratorPid, error: err.message });
185
+ if (logger && logger.log) {
186
+ logger.log('ERROR', `[Orchestrator] Failed execution for ${dateStr}`, { processId: orchestratorPid, error: err.message });
187
+ } else {
188
+ console.error(`[Orchestrator] Failed execution for ${dateStr}: ${err.message}`);
189
+ }
202
190
  throw err;
203
191
  }
204
192
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.226",
3
+ "version": "1.0.227",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [