bulltrackers-module 1.0.229 → 1.0.231

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: Includes defensive checks for Logger compatibility.
3
+ * UPDATED: Removed legacy boolean 'true' logic.
4
4
  */
5
5
  const { normalizeName } = require('./utils/utils');
6
6
  const { checkRootDataAvailability } = require('./data/AvailabilityChecker');
@@ -10,6 +10,8 @@ const { StandardExecutor } = require('./executor
10
10
  const { MetaExecutor } = require('./executors/MetaExecutor');
11
11
  const { generateProcessId, PROCESS_TYPES } = require('./logger/logger');
12
12
 
13
+ const STATUS_IMPOSSIBLE = 'IMPOSSIBLE';
14
+
13
15
  function groupByPass(manifest) {
14
16
  return manifest.reduce((acc, calc) => {
15
17
  (acc[calc.pass] = acc[calc.pass] || []).push(calc);
@@ -17,29 +19,24 @@ function groupByPass(manifest) {
17
19
  }, {});
18
20
  }
19
21
 
20
- /**
21
- * Performs strict analysis of what can run based on availability and hash states.
22
- * AIRTIGHT LOGIC:
23
- * 1. Missing Root Data -> Blocked (Writes 'false' to DB)
24
- * 2. Missing/Stale Dependency -> FailedDependency (Writes 'false' to DB)
25
- * 3. Hash Mismatch -> ReRun (Cascade or Code Change)
26
- * 4. No Result -> Run
27
- * 5. Result Exists & Hash Match -> Skip
28
- */
29
22
  function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus, manifestMap) {
30
23
  const report = {
31
24
  runnable: [],
32
- blocked: [], // Missing Root Data
33
- failedDependency: [], // Missing Dependency OR Stale Dependency
34
- reRuns: [], // Hash Mismatch
35
- skipped: [] // Already done & valid
25
+ blocked: [],
26
+ impossible: [],
27
+ failedDependency: [],
28
+ reRuns: [],
29
+ skipped: []
36
30
  };
37
31
 
32
+ const isTargetToday = (dateStr === new Date().toISOString().slice(0, 10));
33
+
38
34
  const isDepSatisfied = (depName, dailyStatus, manifestMap) => {
39
35
  const norm = normalizeName(depName);
40
36
  const storedDepHash = dailyStatus[norm];
41
37
  const depManifest = manifestMap.get(norm);
42
38
 
39
+ if (storedDepHash === STATUS_IMPOSSIBLE) return false;
43
40
  if (!storedDepHash) return false;
44
41
  if (!depManifest) return false;
45
42
  if (storedDepHash !== depManifest.hash) return false;
@@ -52,7 +49,13 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
52
49
  const storedHash = dailyStatus[cName];
53
50
  const currentHash = calc.hash;
54
51
 
55
- // 1. Root Data Check
52
+ // 1. Check Impossible
53
+ if (storedHash === STATUS_IMPOSSIBLE) {
54
+ report.skipped.push({ name: cName, reason: 'Permanently Impossible' });
55
+ continue;
56
+ }
57
+
58
+ // 2. Root Data Check
56
59
  const missingRoots = [];
57
60
  if (calc.rootDataDependencies) {
58
61
  for (const dep of calc.rootDataDependencies) {
@@ -65,33 +68,50 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
65
68
  }
66
69
 
67
70
  if (missingRoots.length > 0) {
68
- report.blocked.push({ name: cName, reason: `Missing Root Data: ${missingRoots.join(', ')}` });
71
+ if (!isTargetToday) {
72
+ report.impossible.push({ name: cName, reason: `Missing Root Data: ${missingRoots.join(', ')} (Historical)` });
73
+ } else {
74
+ report.blocked.push({ name: cName, reason: `Missing Root Data: ${missingRoots.join(', ')} (Waiting)` });
75
+ }
69
76
  continue;
70
77
  }
71
78
 
72
- // 2. Dependency Check
79
+ // 3. Dependency Check
80
+ let dependencyIsImpossible = false;
73
81
  const missingDeps = [];
82
+
74
83
  if (calc.dependencies) {
75
84
  for (const dep of calc.dependencies) {
85
+ const normDep = normalizeName(dep);
86
+
87
+ if (dailyStatus[normDep] === STATUS_IMPOSSIBLE) {
88
+ dependencyIsImpossible = true;
89
+ break;
90
+ }
91
+
76
92
  if (!isDepSatisfied(dep, dailyStatus, manifestMap)) {
77
93
  missingDeps.push(dep);
78
94
  }
79
95
  }
80
96
  }
81
97
 
98
+ if (dependencyIsImpossible) {
99
+ report.impossible.push({ name: cName, reason: 'Dependency is Impossible' });
100
+ continue;
101
+ }
102
+
82
103
  if (missingDeps.length > 0) {
83
104
  report.failedDependency.push({ name: cName, missing: missingDeps });
84
105
  continue;
85
106
  }
86
107
 
87
- // 3. Hash / State Check
88
- if (!storedHash) {
108
+ // 4. Hash / State Check (Legacy 'true' logic removed)
109
+ if (!storedHash || storedHash === false) {
89
110
  report.runnable.push(calc);
90
111
  } else if (storedHash !== currentHash) {
91
112
  report.reRuns.push({ name: cName, oldHash: storedHash, newHash: currentHash });
92
- } else if (storedHash === true) {
93
- report.reRuns.push({ name: cName, reason: 'Legacy Upgrade' });
94
113
  } else {
114
+ // Stored Hash === Current Hash
95
115
  report.skipped.push({ name: cName });
96
116
  }
97
117
  }
@@ -123,23 +143,23 @@ async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, d
123
143
  const manifestMap = new Map(computationManifest.map(c => [normalizeName(c.name), c]));
124
144
  const analysisReport = analyzeDateExecution(dateStr, calcsInThisPass, rootStatus, dailyStatus, manifestMap);
125
145
 
126
- // 4. LOG ANALYSIS (SAFEGUARDED)
146
+ // 4. LOG ANALYSIS
127
147
  if (logger && typeof logger.logDateAnalysis === 'function') {
128
148
  logger.logDateAnalysis(dateStr, analysisReport);
129
149
  } 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}`;
150
+ const logMsg = `[Analysis] Date: ${dateStr} | Runnable: ${analysisReport.runnable.length} | Blocked: ${analysisReport.blocked.length} | Impossible: ${analysisReport.impossible.length}`;
132
151
  if (logger && logger.info) logger.info(logMsg);
133
152
  else console.log(logMsg);
134
153
  }
135
154
 
136
- // 5. MARK FAILURES (Write 'false' to DB for blocked items)
137
- const failureUpdates = {};
138
- analysisReport.blocked.forEach(item => failureUpdates[item.name] = false);
139
- analysisReport.failedDependency.forEach(item => failureUpdates[item.name] = false);
155
+ // 5. UPDATE STATUS FOR NON-RUNNABLE ITEMS
156
+ const statusUpdates = {};
157
+ analysisReport.blocked.forEach(item => statusUpdates[item.name] = false);
158
+ analysisReport.failedDependency.forEach(item => statusUpdates[item.name] = false);
159
+ analysisReport.impossible.forEach(item => statusUpdates[item.name] = STATUS_IMPOSSIBLE);
140
160
 
141
- if (Object.keys(failureUpdates).length > 0) {
142
- await updateComputationStatus(dateStr, failureUpdates, config, dependencies);
161
+ if (Object.keys(statusUpdates).length > 0) {
162
+ await updateComputationStatus(dateStr, statusUpdates, config, dependencies);
143
163
  }
144
164
 
145
165
  // 6. EXECUTE RUNNABLES
@@ -151,7 +171,12 @@ async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, d
151
171
  const finalRunList = calcsInThisPass.filter(c => calcsToRunNames.has(normalizeName(c.name)));
152
172
 
153
173
  if (!finalRunList.length) {
154
- return { date: dateStr, updates: {}, skipped: analysisReport.skipped.length };
174
+ return {
175
+ date: dateStr,
176
+ updates: {},
177
+ skipped: analysisReport.skipped.length,
178
+ impossible: analysisReport.impossible.length
179
+ };
155
180
  }
156
181
 
157
182
  if (logger && logger.log) {
@@ -1,26 +1,14 @@
1
1
  /**
2
2
  * @fileoverview Structured Logging System for Computation Engine
3
- * Provides comprehensive tracking with process IDs, context, and filtering capabilities.
4
- * UPDATED: Fixed Date Analysis keys to match WorkflowOrchestrator (failedDependency vs resolving).
3
+ * UPDATED: Added 'impossible' category handling to Date Analysis.
5
4
  */
6
5
 
7
6
  const crypto = require('crypto');
8
7
 
9
- /**
10
- * Log Levels (Ordered by Severity)
11
- */
12
8
  const LOG_LEVELS = {
13
- TRACE: 0,
14
- DEBUG: 1,
15
- INFO: 2,
16
- WARN: 3,
17
- ERROR: 4,
18
- FATAL: 5
9
+ TRACE: 0, DEBUG: 1, INFO: 2, WARN: 3, ERROR: 4, FATAL: 5
19
10
  };
20
11
 
21
- /**
22
- * Process Types for Tracking
23
- */
24
12
  const PROCESS_TYPES = {
25
13
  MANIFEST: 'manifest',
26
14
  ORCHESTRATOR: 'orchestrator',
@@ -31,17 +19,11 @@ const PROCESS_TYPES = {
31
19
  DISPATCH: 'dispatch'
32
20
  };
33
21
 
34
- /**
35
- * Generates a deterministic process ID.
36
- */
37
22
  function generateProcessId(type, identifier, date = '') {
38
23
  const input = `${type}|${identifier}|${date}`;
39
24
  return crypto.createHash('sha256').update(input).digest('hex').substring(0, 16);
40
25
  }
41
26
 
42
- /**
43
- * Formats a log entry into structured JSON
44
- */
45
27
  function formatLogEntry(entry) {
46
28
  return JSON.stringify({
47
29
  timestamp: entry.timestamp,
@@ -68,7 +50,6 @@ class StructuredLogger {
68
50
  includeStackTrace: config.includeStackTrace !== false,
69
51
  ...config
70
52
  };
71
-
72
53
  this.activeProcesses = new Map();
73
54
  }
74
55
 
@@ -78,7 +59,6 @@ class StructuredLogger {
78
59
  : crypto.randomBytes(8).toString('hex');
79
60
 
80
61
  const processLogger = new ProcessLogger(this, processType, processId, computationName, date);
81
-
82
62
  this.activeProcesses.set(processId, {
83
63
  type: processType,
84
64
  computationName,
@@ -86,7 +66,6 @@ class StructuredLogger {
86
66
  startTime: Date.now(),
87
67
  logger: processLogger
88
68
  });
89
-
90
69
  return processLogger;
91
70
  }
92
71
 
@@ -101,17 +80,17 @@ class StructuredLogger {
101
80
  }
102
81
 
103
82
  /**
104
- * The Main Date Analysis Logger (FIXED)
105
- * Handles 'failedDependency', 'skipped', and 'blocked' correctly.
83
+ * The Main Date Analysis Logger (UPDATED)
84
+ * Handles 'impossible' category for permanent failures.
106
85
  */
107
86
  logDateAnalysis(dateStr, analysisReport) {
108
- // Destructure with SAFETY DEFAULTS to prevent crashes
109
87
  const {
110
88
  runnable = [],
111
89
  blocked = [],
90
+ impossible = [], // New Category
112
91
  reRuns = [],
113
- failedDependency = [], // Matches WorkflowOrchestrator
114
- skipped = [] // Matches WorkflowOrchestrator
92
+ failedDependency = [],
93
+ skipped = []
115
94
  } = analysisReport;
116
95
 
117
96
  // 1. Structured Output
@@ -125,6 +104,7 @@ class StructuredLogger {
125
104
  stats: {
126
105
  runnable: runnable.length,
127
106
  blocked: blocked.length,
107
+ impossible: impossible.length,
128
108
  reRuns: reRuns.length,
129
109
  failedDependency: failedDependency.length,
130
110
  skipped: skipped.length
@@ -135,7 +115,10 @@ class StructuredLogger {
135
115
 
136
116
  // 2. Human Readable Output
137
117
  if (this.config.enableConsole) {
138
- const symbols = { info: 'ℹ️', warn: '⚠️', check: '✅', block: '⛔', link: '🔗', cycle: '🔄', skip: '⏭️' };
118
+ const symbols = {
119
+ info: 'ℹ️', warn: '⚠️', check: '✅', block: '⛔',
120
+ cycle: '🔄', skip: '⏭️', dead: '💀'
121
+ };
139
122
 
140
123
  console.log(`\n🔍 === DATE ANALYSIS REPORT: ${dateStr} ===`);
141
124
 
@@ -146,29 +129,34 @@ class StructuredLogger {
146
129
  });
147
130
  }
148
131
 
149
- // Updated to handle failedDependency instead of resolving
150
132
  if (failedDependency.length) {
151
- console.log(`\n${symbols.block} [FAILED DEPENDENCIES] (Missing upstream data)`);
133
+ console.log(`\n${symbols.block} [FAILED DEPENDENCIES] (Upstream failure)`);
152
134
  failedDependency.forEach(item => {
153
- // item.missing comes from Orchestrator (not item.missingDeps)
154
135
  console.log(` • ${item.name}: Missing ${item.missing ? item.missing.join(', ') : 'dependencies'}`);
155
136
  });
156
137
  }
157
138
 
139
+ // NEW: Impossible items
140
+ if (impossible.length) {
141
+ console.log(`\n${symbols.dead} [IMPOSSIBLE] (Permanent Failure - Will not retry)`);
142
+ impossible.forEach(item => {
143
+ console.log(` • ${item.name}: ${item.reason}`);
144
+ });
145
+ }
146
+
158
147
  if (runnable.length) {
159
148
  console.log(`\n${symbols.check} [READY TO RUN]`);
160
149
  runnable.forEach(item => console.log(` • ${item.name}`));
161
150
  }
162
151
 
163
152
  if (blocked.length) {
164
- console.log(`\n${symbols.warn} [BLOCKED] (Missing Root Data)`);
153
+ console.log(`\n${symbols.warn} [BLOCKED] (Waiting for data - Retriable)`);
165
154
  blocked.forEach(item => {
166
155
  console.log(` • ${item.name}: ${item.reason}`);
167
156
  });
168
157
  }
169
158
 
170
159
  if (skipped.length) {
171
- // Optional: Only show count if list is huge
172
160
  if (skipped.length > 10) {
173
161
  console.log(`\n${symbols.skip} [SKIPPED] (${skipped.length} calculations up-to-date)`);
174
162
  } else {
@@ -264,7 +252,6 @@ class StructuredLogger {
264
252
  }
265
253
  }
266
254
 
267
- // ... ProcessLogger and Templates (unchanged) ...
268
255
  class ProcessLogger {
269
256
  constructor(parent, processType, processId, computationName, date) {
270
257
  this.parent = parent;
@@ -45,12 +45,19 @@ async function commitResults(stateObj, dStr, passName, config, deps, skipStatusW
45
45
  await commitBatchInChunks(config, deps, updates, `${name} Results`);
46
46
 
47
47
  // Structured Storage Log
48
- logger.logStorage(pid, name, dStr, mainDocRef.path, totalSize, isSharded);
48
+ if (logger && logger.logStorage) {
49
+ logger.logStorage(pid, name, dStr, mainDocRef.path, totalSize, isSharded);
50
+ }
49
51
 
50
- successUpdates[name] = calc.manifest.hash || true;
52
+ // Update success tracking
53
+ if (calc.manifest.hash) {
54
+ successUpdates[name] = calc.manifest.hash;
55
+ }
51
56
  }
52
57
  } catch (e) {
53
- logger.log('ERROR', `Commit failed for ${name}`, { processId: pid, error: e.message });
58
+ if (logger && logger.log) {
59
+ logger.log('ERROR', `Commit failed for ${name}`, { processId: pid, error: e.message });
60
+ }
54
61
  }
55
62
  }
56
63
 
@@ -62,10 +69,7 @@ async function commitResults(stateObj, dStr, passName, config, deps, skipStatusW
62
69
  return successUpdates;
63
70
  }
64
71
 
65
- // ... rest of file (calculateFirestoreBytes, prepareAutoShardedWrites) remains same ...
66
- // Just ensure prepareAutoShardedWrites uses the provided logger if it logs internal warnings.
67
72
  function calculateFirestoreBytes(value) {
68
- // ... same as before
69
73
  if (value === null) return 1;
70
74
  if (value === undefined) return 0;
71
75
  if (typeof value === 'boolean') return 1;
@@ -79,8 +83,6 @@ function calculateFirestoreBytes(value) {
79
83
  }
80
84
 
81
85
  async function prepareAutoShardedWrites(result, docRef, logger) {
82
- // ... same logic, just ensure existing logs inside here use the logger properly if needed
83
- // Copied from previous logic, essentially checks size > 900KB and splits
84
86
  const SAFETY_THRESHOLD_BYTES = 1000 * 1024;
85
87
  const OVERHEAD_ALLOWANCE = 20 * 1024;
86
88
  const CHUNK_LIMIT = SAFETY_THRESHOLD_BYTES - OVERHEAD_ALLOWANCE;
@@ -94,8 +96,6 @@ async function prepareAutoShardedWrites(result, docRef, logger) {
94
96
 
95
97
  if ((totalSize + docPathSize) < CHUNK_LIMIT) { const data = { ...result, _completed: true, _sharded: false }; return [{ ref: docRef, data, options: { merge: true } }]; }
96
98
 
97
- // Note: We don't log "Sharding..." here anymore because we log the structured event in commitResults
98
-
99
99
  for (const [key, value] of Object.entries(result)) {
100
100
  if (key.startsWith('_')) continue;
101
101
  const keySize = Buffer.byteLength(key, 'utf8') + 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.229",
3
+ "version": "1.0.231",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [