bulltrackers-module 1.0.267 → 1.0.268

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,14 +1,14 @@
1
1
  /**
2
2
  * @fileoverview Build Reporter & Auto-Runner.
3
3
  * Generates a "Pre-Flight" report of what the computation system WILL do.
4
- * UPDATED: Fixed 'latest' document overwrite bug.
5
- * UPDATED: Now reports specific reasons for Re-Runs.
4
+ * UPDATED: Shards report details to subcollections to bypass 1MB limit on 'Invalidate All' scenarios.
6
5
  */
7
6
 
8
7
  const { analyzeDateExecution } = require('../WorkflowOrchestrator');
9
8
  const { fetchComputationStatus } = require('../persistence/StatusRepository');
10
9
  const { normalizeName, getExpectedDateStrings, DEFINITIVE_EARLIEST_DATES } = require('../utils/utils');
11
10
  const { checkRootDataAvailability } = require('../data/AvailabilityChecker');
11
+ const { commitBatchInChunks } = require('../persistence/FirestoreUtils'); // Reuse chunker
12
12
  const pLimit = require('p-limit');
13
13
  const path = require('path');
14
14
  const packageJson = require(path.join(__dirname, '..', '..', '..', 'package.json'));
@@ -21,13 +21,11 @@ const packageVersion = pac
21
21
  async function ensureBuildReport(config, dependencies, manifest) {
22
22
  const { db, logger } = dependencies;
23
23
  const now = new Date();
24
- // Create a standardized build ID
25
24
  const buildId = `v${packageVersion}_${now.getFullYear()}-${String(now.getMonth()+1).padStart(2,'0')}-${String(now.getDate()).padStart(2,'0')}_${String(now.getHours()).padStart(2,'0')}-${String(now.getMinutes()).padStart(2,'0')}-${String(now.getSeconds()).padStart(2,'0')}`;
26
25
  const latestRef = db.collection('computation_build_records').doc('latest');
27
26
 
28
27
  try {
29
28
  const latestDoc = await latestRef.get();
30
- // Check using 'packageVersion' key to match what we store
31
29
  const priorVersion = latestDoc.exists ? latestDoc.data().packageVersion : null;
32
30
 
33
31
  if (priorVersion === packageVersion) {
@@ -37,7 +35,7 @@ async function ensureBuildReport(config, dependencies, manifest) {
37
35
 
38
36
  logger.log('INFO', `[BuildReporter] 🚀 New Version Detected (${packageVersion}). Auto-running Pre-flight Report...`);
39
37
 
40
- // Run generation. This function handles writing the 'latest' document with FULL data.
38
+ // Scope: 90 days is fine now that we shard the output
41
39
  await generateBuildReport(config, dependencies, manifest, 90, buildId);
42
40
 
43
41
  } catch (e) {
@@ -46,7 +44,7 @@ async function ensureBuildReport(config, dependencies, manifest) {
46
44
  }
47
45
 
48
46
  /**
49
- * Generates the report and saves to Firestore.
47
+ * Generates the report and saves to Firestore (Sharded).
50
48
  */
51
49
  async function generateBuildReport(config, dependencies, manifest, daysBack = 90, customBuildId = null) {
52
50
  const { db, logger } = dependencies;
@@ -54,7 +52,6 @@ async function generateBuildReport(config, dependencies, manifest, daysBack = 90
54
52
 
55
53
  logger.log('INFO', `[BuildReporter] Generating Build Report: ${buildId} (Scope: ${daysBack} days)...`);
56
54
 
57
- // 1. Determine Date Range
58
55
  const today = new Date();
59
56
  const startDate = new Date();
60
57
  startDate.setDate(today.getDate() - daysBack);
@@ -62,37 +59,33 @@ async function generateBuildReport(config, dependencies, manifest, daysBack = 90
62
59
  const datesToCheck = getExpectedDateStrings(startDate, today);
63
60
  const manifestMap = new Map(manifest.map(c => [normalizeName(c.name), c]));
64
61
 
65
- const reportData = {
62
+ // Main Report Header (Summary Only)
63
+ const reportHeader = {
66
64
  buildId,
67
65
  packageVersion: packageVersion,
68
66
  generatedAt: new Date().toISOString(),
69
67
  summary: {},
70
- dates: {}
68
+ _sharded: true // Flag to tell UI/Tools to look in subcollection
71
69
  };
72
70
 
73
71
  let totalReRuns = 0;
74
72
  let totalNew = 0;
73
+ const detailWrites = []; // Accumulate writes for batching
75
74
 
76
- // 2. PARALLEL PROCESSING
77
75
  const limit = pLimit(20);
78
76
 
79
77
  const processingPromises = datesToCheck.map(dateStr => limit(async () => {
80
78
  try {
81
- // [IMPROVED] Fetch all statuses in parallel
82
79
  const fetchPromises = [
83
- // A. Real status
84
80
  fetchComputationStatus(dateStr, config, dependencies),
85
- // C. Real Root Data
86
81
  checkRootDataAvailability(dateStr, config, dependencies, DEFINITIVE_EARLIEST_DATES)
87
82
  ];
88
83
 
89
- // B. Yesterday's Status (only if needed)
90
84
  let prevDateStr = null;
91
85
  if (manifest.some(c => c.isHistorical)) {
92
86
  const prevDate = new Date(dateStr + 'T00:00:00Z');
93
87
  prevDate.setUTCDate(prevDate.getUTCDate() - 1);
94
88
  prevDateStr = prevDate.toISOString().slice(0, 10);
95
-
96
89
  if (prevDate >= DEFINITIVE_EARLIEST_DATES.absoluteEarliest) {
97
90
  fetchPromises.push(fetchComputationStatus(prevDateStr, config, dependencies));
98
91
  }
@@ -101,18 +94,13 @@ async function generateBuildReport(config, dependencies, manifest, daysBack = 90
101
94
  const results = await Promise.all(fetchPromises);
102
95
  const dailyStatus = results[0];
103
96
  const availability = results[1];
104
- // If we fetched prevStatus, it's at index 2
105
97
  const prevDailyStatus = (prevDateStr && results[2]) ? results[2] : (prevDateStr ? {} : null);
106
-
107
98
  const rootDataStatus = availability ? availability.status : { hasPortfolio: false, hasHistory: false, hasSocial: false, hasInsights: false, hasPrices: false };
108
99
 
109
- // D. Run Logic Analysis
110
100
  const analysis = analyzeDateExecution(dateStr, manifest, rootDataStatus, dailyStatus, manifestMap, prevDailyStatus);
111
101
 
112
- // E. Format Findings
113
102
  const dateSummary = { willRun: [], willReRun: [], blocked: [], impossible: [] };
114
103
 
115
- // Pass the generated "Reason" string through to the report
116
104
  analysis.runnable.forEach (item => dateSummary.willRun.push ({ name: item.name, reason: "New / No Previous Record" }));
117
105
  analysis.reRuns.forEach (item => dateSummary.willReRun.push ({ name: item.name, reason: item.reason || "Hash Mismatch" }));
118
106
  analysis.impossible.forEach (item => dateSummary.impossible.push ({ name: item.name, reason: item.reason }));
@@ -120,12 +108,19 @@ async function generateBuildReport(config, dependencies, manifest, daysBack = 90
120
108
 
121
109
  const hasUpdates = dateSummary.willRun.length || dateSummary.willReRun.length || dateSummary.blocked.length || dateSummary.impossible.length;
122
110
 
123
- return {
124
- dateStr,
125
- dateSummary,
126
- hasUpdates,
127
- stats: { new: dateSummary.willRun.length, rerun: dateSummary.willReRun.length }
128
- };
111
+ if (hasUpdates) {
112
+ // Prepare Write for Subcollection
113
+ const detailRef = db.collection('computation_build_records').doc(buildId).collection('details').doc(dateStr);
114
+ detailWrites.push({
115
+ ref: detailRef,
116
+ data: dateSummary
117
+ });
118
+
119
+ return {
120
+ stats: { new: dateSummary.willRun.length, rerun: dateSummary.willReRun.length }
121
+ };
122
+ }
123
+ return null;
129
124
 
130
125
  } catch (err) {
131
126
  logger.log('ERROR', `[BuildReporter] Error analyzing date ${dateStr}: ${err.message}`);
@@ -135,30 +130,34 @@ async function generateBuildReport(config, dependencies, manifest, daysBack = 90
135
130
 
136
131
  const results = await Promise.all(processingPromises);
137
132
 
138
- // 3. Aggregate Results
139
133
  results.forEach(res => {
140
- if (res && res.hasUpdates) {
141
- reportData.dates[res.dateStr] = res.dateSummary;
134
+ if (res) {
142
135
  totalNew += res.stats.new;
143
136
  totalReRuns += res.stats.rerun;
144
137
  }
145
138
  });
146
139
 
147
- reportData.summary = { totalReRuns, totalNew, scanRange: `${datesToCheck[0]} to ${datesToCheck[datesToCheck.length-1]}` };
140
+ reportHeader.summary = { totalReRuns, totalNew, scanRange: `${datesToCheck[0]} to ${datesToCheck[datesToCheck.length-1]}` };
148
141
 
149
- // 4. Store Report
142
+ // 1. Write Header
150
143
  const reportRef = db.collection('computation_build_records').doc(buildId);
151
- await reportRef.set(reportData);
144
+ await reportRef.set(reportHeader);
145
+
146
+ // 2. Batch Write Details (Using FirestoreUtils to handle batching constraints)
147
+ if (detailWrites.length > 0) {
148
+ logger.log('INFO', `[BuildReporter] Writing ${detailWrites.length} detail records...`);
149
+ await commitBatchInChunks(config, dependencies, detailWrites, 'BuildReportDetails');
150
+ }
152
151
 
153
- // 5. Update 'latest' pointer
154
- await db.collection('computation_build_records').doc('latest').set({ ...reportData, note: "Latest build report pointer." });
152
+ // 3. Update 'latest' pointer (Summary only)
153
+ await db.collection('computation_build_records').doc('latest').set({ ...reportHeader, note: "Latest build report pointer (See subcollection for details)." });
155
154
 
156
155
  logger.log('SUCCESS', `[BuildReporter] Report ${buildId} saved. Re-runs: ${totalReRuns}, New: ${totalNew}.`);
157
156
 
158
157
  return {
159
158
  success: true,
160
159
  reportId: buildId,
161
- summary: reportData.summary
160
+ summary: reportHeader.summary
162
161
  };
163
162
  }
164
163
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.267",
3
+ "version": "1.0.268",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [