bulltrackers-module 1.0.108 → 1.0.110
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.
|
@@ -41,13 +41,37 @@ function groupByPass(manifest) {
|
|
|
41
41
|
}, {});
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* --- NEW HELPER ---
|
|
46
|
+
* Checks if a calculation's root data dependencies are met.
|
|
47
|
+
* @param {object} calcManifest - The manifest entry for the calculation.
|
|
48
|
+
* @param {object} rootDataStatus - The status object from checkRootDataAvailability.
|
|
49
|
+
* @returns {boolean} True if dependencies are met, false otherwise.
|
|
50
|
+
*/
|
|
51
|
+
function checkRootDependencies(calcManifest, rootDataStatus) {
|
|
52
|
+
// Default to true if property is missing (legacy support)
|
|
53
|
+
if (!calcManifest.rootDataDependencies || calcManifest.rootDataDependencies.length === 0) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Check each required dependency
|
|
58
|
+
for (const dep of calcManifest.rootDataDependencies) {
|
|
59
|
+
if (dep === 'portfolio' && !rootDataStatus.hasPortfolio) return false;
|
|
60
|
+
if (dep === 'insights' && !rootDataStatus.hasInsights) return false;
|
|
61
|
+
if (dep === 'social' && !rootDataStatus.hasSocial) return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return true; // All dependencies were met
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
44
68
|
/**
|
|
45
69
|
* --- NEW HELPER ---
|
|
46
70
|
* Checks if the root data (portfolios, insights, social) exists for a given day.
|
|
47
71
|
* @param {string} dateStr - The date string to check (YYYY-MM-DD).
|
|
48
72
|
* @param {object} config - The computation system configuration object.
|
|
49
73
|
* @param {object} dependencies - Contains db, logger, calculationUtils.
|
|
50
|
-
* @returns {Promise<object>} { portfolioRefs, insightsData, socialData, isAvailable }
|
|
74
|
+
* @returns {Promise<object>} { portfolioRefs, insightsData, socialData, isAvailable, hasPortfolio, hasInsights, hasSocial }
|
|
51
75
|
*/
|
|
52
76
|
async function checkRootDataAvailability(dateStr, config, dependencies) {
|
|
53
77
|
const { logger } = dependencies;
|
|
@@ -60,22 +84,33 @@ async function checkRootDataAvailability(dateStr, config, dependencies) {
|
|
|
60
84
|
loadDailySocialPostInsights(config, dependencies, dateStr)
|
|
61
85
|
]);
|
|
62
86
|
|
|
63
|
-
const
|
|
87
|
+
const hasPortfolio = (portfolioRefs && portfolioRefs.length > 0);
|
|
88
|
+
const hasInsights = !!insightsData;
|
|
89
|
+
const hasSocial = !!socialData;
|
|
90
|
+
const isAvailable = hasPortfolio || hasInsights || hasSocial;
|
|
64
91
|
|
|
65
92
|
if (isAvailable) {
|
|
66
|
-
logger.log('INFO', `[Orchestrator] Root data found for ${dateStr}. (Portfolio
|
|
93
|
+
logger.log('INFO', `[Orchestrator] Root data found for ${dateStr}. (Portfolio: ${hasPortfolio}, Insights: ${hasInsights}, Social: ${hasSocial})`);
|
|
67
94
|
}
|
|
68
95
|
|
|
69
96
|
return {
|
|
70
97
|
portfolioRefs: portfolioRefs || [],
|
|
71
98
|
insightsData: insightsData || null,
|
|
72
99
|
socialData: socialData || null,
|
|
73
|
-
isAvailable: isAvailable
|
|
100
|
+
isAvailable: isAvailable,
|
|
101
|
+
// --- MODIFIED: Return granular status ---
|
|
102
|
+
hasPortfolio: hasPortfolio,
|
|
103
|
+
hasInsights: hasInsights,
|
|
104
|
+
hasSocial: hasSocial
|
|
105
|
+
// --- END MODIFICATION ---
|
|
74
106
|
};
|
|
75
107
|
|
|
76
108
|
} catch (err) {
|
|
77
109
|
logger.log('ERROR', `[Orchestrator] Error checking data availability for ${dateStr}`, { errorMessage: err.message });
|
|
78
|
-
return {
|
|
110
|
+
return {
|
|
111
|
+
portfolioRefs: [], insightsData: null, socialData: null, isAvailable: false,
|
|
112
|
+
hasPortfolio: false, hasInsights: false, hasSocial: false
|
|
113
|
+
};
|
|
79
114
|
}
|
|
80
115
|
}
|
|
81
116
|
|
|
@@ -111,24 +146,26 @@ async function runComputationOrchestrator(config, dependencies, computationManif
|
|
|
111
146
|
for (const dateStr of allExpectedDates) {
|
|
112
147
|
const dateToProcess = new Date(dateStr + 'T00:00:00Z');
|
|
113
148
|
|
|
114
|
-
// ---
|
|
149
|
+
// --- MODIFIED: Check for root data *before* processing the day ---
|
|
115
150
|
const rootData = await checkRootDataAvailability(dateStr, config, dependencies);
|
|
151
|
+
// The 'isAvailable' flag now means "is there *any* data"
|
|
116
152
|
if (!rootData.isAvailable) {
|
|
117
153
|
logger.log('WARN', `[Orchestrator] Skipping all computations for ${dateStr} due to missing root data (no portfolios, insights, or social data found).`);
|
|
118
154
|
continue; // Skip to the next day
|
|
119
155
|
}
|
|
120
|
-
// --- END
|
|
156
|
+
// --- END MODIFIED CHECK ---
|
|
121
157
|
|
|
122
158
|
logger.log('INFO', `[Orchestrator] Processing all passes for ${dateStr}...`);
|
|
123
159
|
|
|
124
|
-
// This cache will hold results *for this day only*
|
|
125
160
|
const dailyResultsCache = new Map();
|
|
161
|
+
// --- NEW: Keep track of skipped calcs ---
|
|
162
|
+
const skippedCalculations = new Set();
|
|
126
163
|
let passSuccess = true;
|
|
127
164
|
|
|
128
165
|
for (const passNum of passNumbers) {
|
|
129
166
|
if (!passSuccess) {
|
|
130
167
|
logger.log('WARN', `[Orchestrator] Skipping Pass ${passNum} for ${dateStr} due to previous pass failure.`);
|
|
131
|
-
break;
|
|
168
|
+
break;
|
|
132
169
|
}
|
|
133
170
|
|
|
134
171
|
const calcsInPass = passes[passNum] || [];
|
|
@@ -139,38 +176,87 @@ async function runComputationOrchestrator(config, dependencies, computationManif
|
|
|
139
176
|
logger.log('INFO', `[Orchestrator] Starting Pass ${passNum} for ${dateStr} (${standardCalcs.length} standard, ${metaCalcs.length} meta).`);
|
|
140
177
|
|
|
141
178
|
try {
|
|
142
|
-
// 1. Run standard calcs for this pass
|
|
179
|
+
// --- 1. Run standard calcs for this pass ---
|
|
143
180
|
if (standardCalcs.length > 0) {
|
|
144
|
-
const standardResults = await runUnifiedComputation(
|
|
145
|
-
dateToProcess,
|
|
146
|
-
standardCalcs, // Pass the manifest objects
|
|
147
|
-
`Pass ${passNum} (Standard)`,
|
|
148
|
-
config,
|
|
149
|
-
dependencies,
|
|
150
|
-
rootData // <-- Pass pre-fetched root data
|
|
151
|
-
);
|
|
152
181
|
|
|
153
|
-
//
|
|
154
|
-
|
|
155
|
-
|
|
182
|
+
// --- NEW: Filter calcs based on root data availability ---
|
|
183
|
+
const standardCalcsToRun = [];
|
|
184
|
+
for (const calcManifest of standardCalcs) {
|
|
185
|
+
if (checkRootDependencies(calcManifest, rootData)) {
|
|
186
|
+
standardCalcsToRun.push(calcManifest);
|
|
187
|
+
} else {
|
|
188
|
+
logger.log('INFO', `[Pass ${passNum}] Skipping standard calc "${calcManifest.name}" for ${dateStr} due to missing root data.`);
|
|
189
|
+
skippedCalculations.add(calcManifest.name);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// --- END NEW FILTER ---
|
|
193
|
+
|
|
194
|
+
if (standardCalcsToRun.length > 0) {
|
|
195
|
+
const standardResults = await runUnifiedComputation(
|
|
196
|
+
dateToProcess,
|
|
197
|
+
standardCalcsToRun, // Pass the filtered list
|
|
198
|
+
`Pass ${passNum} (Standard)`,
|
|
199
|
+
config,
|
|
200
|
+
dependencies,
|
|
201
|
+
rootData
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
for (const [calcName, result] of Object.entries(standardResults)) {
|
|
205
|
+
dailyResultsCache.set(calcName, result);
|
|
206
|
+
}
|
|
156
207
|
}
|
|
157
208
|
}
|
|
158
209
|
|
|
159
|
-
// 2. Run meta calcs for this pass
|
|
210
|
+
// --- 2. Run meta calcs for this pass ---
|
|
160
211
|
if (metaCalcs.length > 0) {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
212
|
+
|
|
213
|
+
// --- NEW: Filter calcs based on root data AND computation dependencies ---
|
|
214
|
+
const metaCalcsToRun = [];
|
|
215
|
+
for (const calcManifest of metaCalcs) {
|
|
216
|
+
const calcName = calcManifest.name;
|
|
217
|
+
|
|
218
|
+
// Check 1: Are root data dependencies met?
|
|
219
|
+
const rootCheck = checkRootDependencies(calcManifest, rootData);
|
|
220
|
+
if (!rootCheck) {
|
|
221
|
+
logger.log('INFO', `[Pass ${passNum} (Meta)] Skipping meta calc "${calcName}" for ${dateStr} due to missing root data.`);
|
|
222
|
+
skippedCalculations.add(calcName);
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Check 2: Are computation dependencies met (i.e., not skipped)?
|
|
227
|
+
let depCheck = true;
|
|
228
|
+
let missingDepName = '';
|
|
229
|
+
for (const depName of (calcManifest.dependencies || [])) {
|
|
230
|
+
if (skippedCalculations.has(normalizeName(depName))) {
|
|
231
|
+
depCheck = false;
|
|
232
|
+
missingDepName = normalizeName(depName);
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (depCheck) {
|
|
238
|
+
metaCalcsToRun.push(calcManifest);
|
|
239
|
+
} else {
|
|
240
|
+
logger.log('INFO', `[Pass ${passNum} (Meta)] Skipping meta calc "${calcName}" for ${dateStr} due to missing computation dependency "${missingDepName}".`);
|
|
241
|
+
skippedCalculations.add(calcName);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
// --- END NEW FILTER ---
|
|
245
|
+
|
|
246
|
+
if (metaCalcsToRun.length > 0) {
|
|
247
|
+
const metaResults = await runMetaComputation(
|
|
248
|
+
dateToProcess,
|
|
249
|
+
metaCalcsToRun, // Pass the filtered list
|
|
250
|
+
`Pass ${passNum} (Meta)`,
|
|
251
|
+
config,
|
|
252
|
+
dependencies,
|
|
253
|
+
dailyResultsCache,
|
|
254
|
+
rootData
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
for (const [calcName, result] of Object.entries(metaResults)) {
|
|
258
|
+
dailyResultsCache.set(calcName, result);
|
|
259
|
+
}
|
|
174
260
|
}
|
|
175
261
|
}
|
|
176
262
|
logger.log('SUCCESS', `[Orchestrator] Completed Pass ${passNum} for ${dateStr}.`);
|
|
@@ -179,7 +265,7 @@ async function runComputationOrchestrator(config, dependencies, computationManif
|
|
|
179
265
|
passSuccess = false;
|
|
180
266
|
}
|
|
181
267
|
} // End passes loop
|
|
182
|
-
logger.log('INFO', `[Orchestrator] Finished processing for ${dateStr}
|
|
268
|
+
logger.log('INFO', `[Orchestrator] Finished processing for ${dateStr}. Total skipped calculations: ${skippedCalculations.size}`);
|
|
183
269
|
} // End dates loop
|
|
184
270
|
|
|
185
271
|
logger.log('INFO', '[Orchestrator] Computation orchestration finished.');
|
|
@@ -255,6 +341,8 @@ async function streamAndProcess(
|
|
|
255
341
|
if (!p) continue;
|
|
256
342
|
|
|
257
343
|
const userType = p.PublicPositions ? 'speculator' : 'normal';
|
|
344
|
+
// Add userType to context
|
|
345
|
+
context.userType = userType;
|
|
258
346
|
|
|
259
347
|
for (const calcName in state) { // calcName is already normalized
|
|
260
348
|
const calc = state[calcName];
|
|
@@ -364,14 +452,10 @@ async function runUnifiedComputation(dateToProcess, calculationsToRun, passName,
|
|
|
364
452
|
} = rootData;
|
|
365
453
|
// --- END NEW ---
|
|
366
454
|
|
|
367
|
-
// (The check for data availability is now done in the orchestrator)
|
|
368
|
-
|
|
369
455
|
let yesterdayPortfolios = {};
|
|
370
456
|
let yesterdayInsightsData = null;
|
|
371
457
|
let yesterdaySocialPostInsightsData = null;
|
|
372
458
|
|
|
373
|
-
// Check if any calc needs yesterday's data
|
|
374
|
-
// --- MODIFIED: Use the manifest entry ---
|
|
375
459
|
const requiresYesterdayPortfolio = calculationsToRun.some(c => c.isHistorical === true);
|
|
376
460
|
const requiresYesterdayInsights = calculationsToRun.some(c => c.class.prototype.process.toString().includes('yesterdayInsights'));
|
|
377
461
|
const requiresYesterdaySocialPosts = calculationsToRun.some(c => c.class.prototype.process.toString().includes('yesterdaySocialPostInsights'));
|
|
@@ -450,13 +534,11 @@ async function runUnifiedComputation(dateToProcess, calculationsToRun, passName,
|
|
|
450
534
|
const calc = state[calcName];
|
|
451
535
|
if (!calc || typeof calc.getResult !== 'function') continue;
|
|
452
536
|
|
|
453
|
-
// --- MODIFIED: Get category from the attached manifest ---
|
|
454
537
|
const category = calc.manifest.category || 'unknown';
|
|
455
538
|
|
|
456
539
|
try {
|
|
457
540
|
const result = await Promise.resolve(calc.getResult());
|
|
458
541
|
|
|
459
|
-
// Add to results map for in-memory cache
|
|
460
542
|
if (result) {
|
|
461
543
|
passResults[calcName] = result;
|
|
462
544
|
}
|
|
@@ -465,7 +547,6 @@ async function runUnifiedComputation(dateToProcess, calculationsToRun, passName,
|
|
|
465
547
|
const summaryData = {};
|
|
466
548
|
|
|
467
549
|
if (result && Object.keys(result).length > 0) {
|
|
468
|
-
// (Special handling for sharded calcs remains the same)
|
|
469
550
|
let isSharded = false;
|
|
470
551
|
const shardedCollections = {
|
|
471
552
|
'sharded_user_profile': config.shardedUserProfileCollection,
|
|
@@ -485,7 +566,6 @@ async function runUnifiedComputation(dateToProcess, calculationsToRun, passName,
|
|
|
485
566
|
pendingWrites.push({ ref: shardRef, data: shardedData[shardId] });
|
|
486
567
|
}
|
|
487
568
|
}
|
|
488
|
-
// Get results *not* in the shard key
|
|
489
569
|
const { [resultKey]: _, ...otherResults } = result;
|
|
490
570
|
if (Object.keys(otherResults).length > 0) {
|
|
491
571
|
const computationDocRef = resultsCollectionRef.doc(category)
|
|
@@ -502,7 +582,6 @@ async function runUnifiedComputation(dateToProcess, calculationsToRun, passName,
|
|
|
502
582
|
.doc(calcName);
|
|
503
583
|
pendingWrites.push({ ref: computationDocRef, data: result });
|
|
504
584
|
}
|
|
505
|
-
// --- END SHARDED HANDLING ---
|
|
506
585
|
|
|
507
586
|
if (!summaryData[category]) summaryData[category] = {};
|
|
508
587
|
summaryData[category][calcName] = true;
|
|
@@ -522,7 +601,15 @@ async function runUnifiedComputation(dateToProcess, calculationsToRun, passName,
|
|
|
522
601
|
successCount++;
|
|
523
602
|
}
|
|
524
603
|
} else {
|
|
525
|
-
|
|
604
|
+
// --- MODIFICATION: Do not log a warning if the result is null. ---
|
|
605
|
+
// This is now expected behavior if a calc (like user-profitability-tracker)
|
|
606
|
+
// has no data to process.
|
|
607
|
+
// We *do* still need to log if a result is empty.
|
|
608
|
+
if (result === null) {
|
|
609
|
+
logger.log('INFO', `[${passName}] Calculation ${calcName} returned null for ${dateStr}. This is expected if no data was processed.`);
|
|
610
|
+
} else {
|
|
611
|
+
logger.log('WARN', `[${passName}] Calculation ${calcName} produced empty results {} for ${dateStr}. Skipping write.`);
|
|
612
|
+
}
|
|
526
613
|
}
|
|
527
614
|
} catch (e) {
|
|
528
615
|
logger.log('ERROR', `[${passName}] getResult/Commit failed for ${calcName} on ${dateStr}`, { err: e.message });
|
|
@@ -532,7 +619,7 @@ async function runUnifiedComputation(dateToProcess, calculationsToRun, passName,
|
|
|
532
619
|
const completionStatus = successCount === calculationsToRun.length ? 'SUCCESS' : 'WARN';
|
|
533
620
|
logger.log(completionStatus, `[${passName}] Completed ${dateStr}. Success: ${successCount}/${calculationsToRun.length}.`);
|
|
534
621
|
|
|
535
|
-
return passResults;
|
|
622
|
+
return passResults;
|
|
536
623
|
|
|
537
624
|
} catch (err) {
|
|
538
625
|
logger.log('ERROR', `[${passName}] Fatal error for ${dateStr}`, { errorMessage: err.message, stack: err.stack });
|
|
@@ -543,39 +630,33 @@ async function runUnifiedComputation(dateToProcess, calculationsToRun, passName,
|
|
|
543
630
|
|
|
544
631
|
/**
|
|
545
632
|
* Internal sub-pipe: Runs "meta" or "backtest" computations for a single date.
|
|
546
|
-
* MODIFIED: Accepts in-memory cache and passes dependencies to calcs.
|
|
547
|
-
* MODIFIED: Accepts rootData and adds it to the dependencies object.
|
|
548
|
-
* Returns a map of results.
|
|
549
633
|
*/
|
|
550
634
|
async function runMetaComputation(
|
|
551
635
|
dateToProcess,
|
|
552
|
-
calculationsToRun,
|
|
636
|
+
calculationsToRun, // This is the *filtered* list
|
|
553
637
|
passName,
|
|
554
638
|
config,
|
|
555
639
|
dependencies,
|
|
556
640
|
dailyResultsCache,
|
|
557
|
-
rootData
|
|
641
|
+
rootData
|
|
558
642
|
) {
|
|
559
643
|
const { db, logger } = dependencies;
|
|
560
644
|
const dateStr = dateToProcess.toISOString().slice(0, 10);
|
|
561
645
|
logger.log('INFO', `[${passName}] Starting run for ${dateStr} with ${calculationsToRun.length} calcs.`);
|
|
562
646
|
|
|
563
|
-
const passResults = {};
|
|
647
|
+
const passResults = {};
|
|
564
648
|
|
|
565
649
|
try {
|
|
566
650
|
const resultsCollectionRef = db.collection(config.resultsCollection).doc(dateStr).collection(config.resultsSubcollection);
|
|
567
651
|
let successCount = 0;
|
|
568
652
|
|
|
569
|
-
// --- !! MODIFICATION: Add rootData to the dependencies object !! ---
|
|
570
653
|
const dependenciesForMetaCalc = {
|
|
571
654
|
...dependencies,
|
|
572
655
|
rootData: rootData
|
|
573
656
|
};
|
|
574
|
-
// --- END MODIFICATION ---
|
|
575
657
|
|
|
576
658
|
for (const manifestCalc of calculationsToRun) {
|
|
577
659
|
const calcName = normalizeName(manifestCalc.name);
|
|
578
|
-
// --- MODIFIED: Get category from manifest ---
|
|
579
660
|
const category = manifestCalc.category || 'unknown';
|
|
580
661
|
const CalcClass = manifestCalc.class;
|
|
581
662
|
|
|
@@ -588,12 +669,17 @@ async function runMetaComputation(
|
|
|
588
669
|
|
|
589
670
|
try {
|
|
590
671
|
// --- Gather dependencies from the cache ---
|
|
672
|
+
// This check is now more robust, as the orchestrator has already
|
|
673
|
+
// logged the *reason* for a skip. We just need to check the cache.
|
|
591
674
|
const computedDependencies = {};
|
|
592
675
|
let missingDep = false;
|
|
593
676
|
if (manifestCalc.dependencies) {
|
|
594
677
|
for (const depName of manifestCalc.dependencies) {
|
|
595
678
|
const normalizedDepName = normalizeName(depName);
|
|
596
679
|
if (!dailyResultsCache.has(normalizedDepName)) {
|
|
680
|
+
// This log is still important, as it indicates a logic error
|
|
681
|
+
// if a calc *wasn't* skipped by the orchestrator but its
|
|
682
|
+
// dependency is *still* missing (e.g., the dep returned null).
|
|
597
683
|
logger.log('ERROR', `[${passName}] Missing required dependency "${normalizedDepName}" for calculation "${calcName}". This should not happen. Skipping calc.`);
|
|
598
684
|
missingDep = true;
|
|
599
685
|
break;
|
|
@@ -606,15 +692,12 @@ async function runMetaComputation(
|
|
|
606
692
|
// --- Call process with the dependencies ---
|
|
607
693
|
const result = await Promise.resolve(instance.process(
|
|
608
694
|
dateStr,
|
|
609
|
-
dependenciesForMetaCalc,
|
|
695
|
+
dependenciesForMetaCalc,
|
|
610
696
|
config,
|
|
611
|
-
computedDependencies
|
|
697
|
+
computedDependencies
|
|
612
698
|
));
|
|
613
699
|
|
|
614
|
-
|
|
615
|
-
if (result) {
|
|
616
|
-
passResults[calcName] = result;
|
|
617
|
-
}
|
|
700
|
+
passResults[calcName] = result;
|
|
618
701
|
|
|
619
702
|
const pendingWrites = [];
|
|
620
703
|
const summaryData = {};
|
|
@@ -647,18 +730,17 @@ async function runMetaComputation(
|
|
|
647
730
|
}
|
|
648
731
|
} catch (e) {
|
|
649
732
|
logger.log('ERROR', `[${passName}] Meta-calc process/commit failed for ${calcName} on ${dateStr}`, { err: e.message, stack: e.stack });
|
|
650
|
-
// Don't re-throw, allow other meta-calcs in the pass to run
|
|
651
733
|
}
|
|
652
734
|
}
|
|
653
735
|
|
|
654
736
|
const completionStatus = successCount === calculationsToRun.length ? 'SUCCESS' : 'WARN';
|
|
655
737
|
logger.log(completionStatus, `[${passName}] Completed ${dateStr}. Success: ${successCount}/${calculationsToRun.length}.`);
|
|
656
738
|
|
|
657
|
-
return passResults;
|
|
739
|
+
return passResults;
|
|
658
740
|
|
|
659
741
|
} catch (err) {
|
|
660
742
|
logger.log('ERROR', `[${passName}] Fatal error for ${dateStr}`, { errorMessage: err.message, stack: err.stack });
|
|
661
|
-
throw err;
|
|
743
|
+
throw err;
|
|
662
744
|
}
|
|
663
745
|
}
|
|
664
746
|
|