bulltrackers-module 1.0.259 → 1.0.261

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,13 @@
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: Now fetches 'prevDailyStatus' to enforce strict historical continuity checks.
4
+ * UPDATED: Optimized with Parallel Status Fetches inside the date loop.
5
5
  */
6
6
 
7
7
  const { analyzeDateExecution } = require('../WorkflowOrchestrator');
8
8
  const { fetchComputationStatus } = require('../persistence/StatusRepository');
9
9
  const { normalizeName, getExpectedDateStrings, DEFINITIVE_EARLIEST_DATES } = require('../utils/utils');
10
10
  const { checkRootDataAvailability } = require('../data/AvailabilityChecker');
11
- const { FieldValue } = require('@google-cloud/firestore');
12
11
  const pLimit = require('p-limit');
13
12
  const path = require('path');
14
13
  const packageJson = require(path.join(__dirname, '..', '..', '..', 'package.json'));
@@ -21,11 +20,7 @@ const packageVersion = packageJson.version;
21
20
  async function ensureBuildReport(config, dependencies, manifest) {
22
21
  const { db, logger } = dependencies;
23
22
  const now = new Date();
24
-
25
- // BuildId still includes timestamp for uniqueness
26
23
  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')}`;
27
-
28
- // Reference to "latest" doc
29
24
  const latestRef = db.collection('computation_build_records').doc('latest');
30
25
 
31
26
  try {
@@ -38,10 +33,8 @@ async function ensureBuildReport(config, dependencies, manifest) {
38
33
  }
39
34
 
40
35
  logger.log('INFO', `[BuildReporter] 🚀 New Version Detected (${packageVersion}). Auto-running Pre-flight Report...`);
41
-
42
36
  await generateBuildReport(config, dependencies, manifest, 90, buildId);
43
-
44
- // Update "latest" pointer
37
+
45
38
  await latestRef.set({
46
39
  packageVersion,
47
40
  buildId,
@@ -55,11 +48,6 @@ async function ensureBuildReport(config, dependencies, manifest) {
55
48
 
56
49
  /**
57
50
  * Generates the report and saves to Firestore.
58
- * @param {object} config
59
- * @param {object} dependencies
60
- * @param {Array} manifest
61
- * @param {number} daysBack - Days to simulate (Default 90)
62
- * @param {string} customBuildId - Optional ID override
63
51
  */
64
52
  async function generateBuildReport(config, dependencies, manifest, daysBack = 90, customBuildId = null) {
65
53
  const { db, logger } = dependencies;
@@ -86,99 +74,61 @@ async function generateBuildReport(config, dependencies, manifest, daysBack = 90
86
74
  let totalReRuns = 0;
87
75
  let totalNew = 0;
88
76
 
89
- // 2. PARALLEL PROCESSING (Fix for DEADLINE_EXCEEDED)
90
- // Run 20 reads in parallel.
77
+ // 2. PARALLEL PROCESSING
91
78
  const limit = pLimit(20);
92
79
 
93
80
  const processingPromises = datesToCheck.map(dateStr => limit(async () => {
94
81
  try {
95
- // A. Fetch REAL status from DB (What ran previously?)
96
- const dailyStatus = await fetchComputationStatus(dateStr, config, dependencies);
97
-
98
- // [NEW] B. Fetch YESTERDAY'S Status (For strict continuity checks)
99
- let prevDailyStatus = null;
100
-
101
- // Only fetch if ANY calculation in the manifest is historical
102
- // (Optimization: In BuildReporter we pass the full manifest, so we check if any exist generally)
82
+ // [IMPROVED] Fetch all statuses in parallel
83
+ const fetchPromises = [
84
+ // A. Real status
85
+ fetchComputationStatus(dateStr, config, dependencies),
86
+ // C. Real Root Data
87
+ checkRootDataAvailability(dateStr, config, dependencies, DEFINITIVE_EARLIEST_DATES)
88
+ ];
89
+
90
+ // B. Yesterday's Status (only if needed)
91
+ let prevDateStr = null;
103
92
  if (manifest.some(c => c.isHistorical)) {
104
93
  const prevDate = new Date(dateStr + 'T00:00:00Z');
105
94
  prevDate.setUTCDate(prevDate.getUTCDate() - 1);
106
- const prevDateStr = prevDate.toISOString().slice(0, 10);
95
+ prevDateStr = prevDate.toISOString().slice(0, 10);
107
96
 
108
- // Ensure we don't look before the dawn of time
109
97
  if (prevDate >= DEFINITIVE_EARLIEST_DATES.absoluteEarliest) {
110
- prevDailyStatus = await fetchComputationStatus(prevDateStr, config, dependencies);
111
- } else {
112
- prevDailyStatus = {}; // Pre-epoch is valid empty context
98
+ fetchPromises.push(fetchComputationStatus(prevDateStr, config, dependencies));
113
99
  }
114
100
  }
115
101
 
116
- // C. REAL Root Data Check
117
- // Uses the new Indexer logic (returns null refs, but valid status flags)
118
- const availability = await checkRootDataAvailability(dateStr, config, dependencies, DEFINITIVE_EARLIEST_DATES);
119
-
102
+ const results = await Promise.all(fetchPromises);
103
+ const dailyStatus = results[0];
104
+ const availability = results[1];
105
+ // If we fetched prevStatus, it's at index 2
106
+ const prevDailyStatus = (prevDateStr && results[2]) ? results[2] : (prevDateStr ? {} : null);
107
+
120
108
  const rootDataStatus = availability ? availability.status : {
121
- hasPortfolio: false,
122
- hasHistory: false,
123
- hasSocial: false,
124
- hasInsights: false,
125
- hasPrices: false
109
+ hasPortfolio: false, hasHistory: false, hasSocial: false, hasInsights: false, hasPrices: false
126
110
  };
127
111
 
128
112
  // D. Run Logic Analysis
129
- // Now passes prevDailyStatus to enable the "Blocked if yesterday missing" logic
130
113
  const analysis = analyzeDateExecution(dateStr, manifest, rootDataStatus, dailyStatus, manifestMap, prevDailyStatus);
131
114
 
132
115
  // E. Format Findings
133
116
  const dateSummary = {
134
- willRun: [],
135
- willReRun: [],
136
- blocked: [],
137
- impossible: []
117
+ willRun: [], willReRun: [], blocked: [], impossible: []
138
118
  };
139
119
 
140
- // -- Runnable (New) --
141
- analysis.runnable.forEach(item => {
142
- dateSummary.willRun.push({ name: item.name, reason: "New / No Previous Record" });
143
- });
120
+ analysis.runnable.forEach(item => dateSummary.willRun.push({ name: item.name, reason: "New / No Previous Record" }));
121
+ analysis.reRuns.forEach(item => dateSummary.willReRun.push({ name: item.name, reason: item.previousCategory ? "Migration" : "Hash Mismatch" }));
122
+ analysis.impossible.forEach(item => dateSummary.impossible.push({ name: item.name, reason: item.reason }));
123
+ [...analysis.blocked, ...analysis.failedDependency].forEach(item => dateSummary.blocked.push({ name: item.name, reason: item.reason || 'Dependency' }));
144
124
 
145
- // -- Re-Runs (Hash Mismatch / Migration) --
146
- analysis.reRuns.forEach(item => {
147
- let reason = "Hash Mismatch";
148
- let details = `Old: ${item.oldHash?.substring(0,6)}... New: ${item.newHash?.substring(0,6)}...`;
149
-
150
- if (item.previousCategory) {
151
- reason = "Migration";
152
- details = `Moving ${item.previousCategory} -> ${item.newCategory}`;
153
- }
154
-
155
- dateSummary.willReRun.push({ name: item.name, reason, details });
156
- });
157
-
158
- // -- Impossible (Permanent) --
159
- analysis.impossible.forEach(item => {
160
- dateSummary.impossible.push({ name: item.name, reason: item.reason });
161
- });
162
-
163
- // -- Blocked (Retriable) --
164
- analysis.blocked.forEach(item => {
165
- dateSummary.blocked.push({ name: item.name, reason: item.reason });
166
- });
167
- analysis.failedDependency.forEach(item => {
168
- dateSummary.blocked.push({ name: item.name, reason: `Dependency Missing: ${item.missing.join(', ')}` });
169
- });
170
-
171
- // Return result for aggregation
172
125
  const hasUpdates = dateSummary.willRun.length || dateSummary.willReRun.length || dateSummary.blocked.length || dateSummary.impossible.length;
173
126
 
174
127
  return {
175
128
  dateStr,
176
129
  dateSummary,
177
130
  hasUpdates,
178
- stats: {
179
- new: dateSummary.willRun.length,
180
- rerun: dateSummary.willReRun.length
181
- }
131
+ stats: { new: dateSummary.willRun.length, rerun: dateSummary.willReRun.length }
182
132
  };
183
133
 
184
134
  } catch (err) {
@@ -187,7 +137,6 @@ async function generateBuildReport(config, dependencies, manifest, daysBack = 90
187
137
  }
188
138
  }));
189
139
 
190
- // Wait for all dates to process
191
140
  const results = await Promise.all(processingPromises);
192
141
 
193
142
  // 3. Aggregate Results
@@ -1,12 +1,34 @@
1
1
  /**
2
2
  * @fileoverview Schema capture utility for computation outputs
3
3
  * This module batches and stores pre-defined static schemas in Firestore.
4
+ * UPDATED: Added schema validation to prevent silent batch failures.
4
5
  */
5
6
 
7
+ /**
8
+ * Validates a schema object before storage.
9
+ * Checks for circular references and size limits.
10
+ * @param {object} schema
11
+ * @returns {object} { valid: boolean, reason: string }
12
+ */
13
+ function validateSchema(schema) {
14
+ try {
15
+ // 1. Detect circular references
16
+ const jsonStr = JSON.stringify(schema);
17
+
18
+ // 2. Ensure it's not too large (Firestore limit: 1MB, reserve 100KB for metadata)
19
+ const size = Buffer.byteLength(jsonStr);
20
+ if (size > 900 * 1024) {
21
+ return { valid: false, reason: `Schema exceeds 900KB limit (${(size/1024).toFixed(2)} KB)` };
22
+ }
23
+
24
+ return { valid: true };
25
+ } catch (e) {
26
+ return { valid: false, reason: `Serialization failed: ${e.message}` };
27
+ }
28
+ }
29
+
6
30
  /**
7
31
  * Batch store schemas for multiple computations.
8
- * This function now expects a fully-formed schema, not sample output.
9
- * It strictly stamps a 'lastUpdated' field to support stale-schema filtering in the API.
10
32
  *
11
33
  * @param {object} dependencies - Contains db, logger
12
34
  * @param {object} config - Configuration object
@@ -31,6 +53,13 @@ async function batchStoreSchemas(dependencies, config, schemas) {
31
53
  continue;
32
54
  }
33
55
 
56
+ // [IMPROVED] Validate before adding to batch
57
+ const validation = validateSchema(item.schema);
58
+ if (!validation.valid) {
59
+ logger.log('WARN', `[SchemaCapture] Invalid schema for ${item.name}: ${validation.reason}`);
60
+ continue;
61
+ }
62
+
34
63
  const docRef = db.collection(schemaCollection).doc(item.name);
35
64
 
36
65
  // Critical: Always overwrite 'lastUpdated' to now
package/index.js CHANGED
@@ -27,7 +27,6 @@ const { handleUpdate } = require('./functions
27
27
 
28
28
  // Computation System
29
29
  const { build: buildManifest } = require('./functions/computation-system/context/ManifestBuilder');
30
- // const { runDateComputation: runComputationPass } = require('./functions/computation-system/WorkflowOrchestrator'); Depreciated
31
30
  const { dispatchComputationPass } = require('./functions/computation-system/helpers/computation_dispatcher');
32
31
  const { handleComputationTask } = require('./functions/computation-system/helpers/computation_worker');
33
32
  // [NEW] Import Report Tools
@@ -54,7 +53,7 @@ const { handlePost } = require('./functions
54
53
 
55
54
  // NEW
56
55
 
57
- const { runRootDataIndexer } = require('./functions/root-data-indexer/index'); // <--- IMPORT
56
+ const { runRootDataIndexer } = require('./functions/root-data-indexer/index');
58
57
 
59
58
  const core = {
60
59
  IntelligentHeaderManager,
@@ -88,7 +87,6 @@ const taskEngine = {
88
87
  };
89
88
 
90
89
  const computationSystem = {
91
- // runComputationPass, Depreciated
92
90
  dispatchComputationPass,
93
91
  handleComputationTask,
94
92
  dataLoader,
@@ -112,7 +110,7 @@ const maintenance = {
112
110
  runSocialOrchestrator,
113
111
  handleSocialTask,
114
112
  runBackfillAssetPrices,
115
- runRootDataIndexer, // <--- EXPORT
113
+ runRootDataIndexer,
116
114
  };
117
115
 
118
116
  const proxy = { handlePost };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.259",
3
+ "version": "1.0.261",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [