bulltrackers-module 1.0.237 → 1.0.239
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.
- package/functions/computation-system/WorkflowOrchestrator.js +2 -4
- package/functions/computation-system/helpers/computation_dispatcher.js +34 -34
- package/functions/computation-system/helpers/computation_worker.js +30 -18
- package/functions/computation-system/tools/BuildReporter.js +161 -0
- package/index.js +6 -0
- package/package.json +1 -1
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Main Orchestrator. Coordinates the topological execution.
|
|
3
|
-
* UPDATED:
|
|
4
|
-
* FIX: Decouples migration detection from hash verification to handle simultaneous code & category changes.
|
|
3
|
+
* UPDATED: Exports analyzeDateExecution for Build Reporting tools.
|
|
5
4
|
*/
|
|
6
5
|
const { normalizeName } = require('./utils/utils');
|
|
7
6
|
const { checkRootDataAvailability } = require('./data/AvailabilityChecker');
|
|
@@ -124,7 +123,6 @@ function analyzeDateExecution(dateStr, calcsInPass, rootDataStatus, dailyStatus,
|
|
|
124
123
|
report.runnable.push(calc);
|
|
125
124
|
} else if (storedHash !== currentHash) {
|
|
126
125
|
// Hash Mismatch (Code Changed).
|
|
127
|
-
// Pass migration info here too, in case category ALSO changed.
|
|
128
126
|
report.reRuns.push({
|
|
129
127
|
name: cName,
|
|
130
128
|
oldHash: storedHash,
|
|
@@ -268,4 +266,4 @@ async function runDateComputation(dateStr, passToRun, calcsInThisPass, config, d
|
|
|
268
266
|
return { date: dateStr, updates: dateUpdates };
|
|
269
267
|
}
|
|
270
268
|
|
|
271
|
-
module.exports = { runDateComputation, groupByPass };
|
|
269
|
+
module.exports = { runDateComputation, groupByPass, analyzeDateExecution };
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* FILENAME: bulltrackers-module/functions/computation-system/helpers/computation_dispatcher.js
|
|
3
|
-
* PURPOSE: Dispatches computation tasks
|
|
4
|
-
*
|
|
3
|
+
* PURPOSE: Dispatches granular computation tasks (1 task = 1 calculation/date).
|
|
4
|
+
* UPDATED: Implements "Atomic Task Dispatch" for maximum reliability.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const { getExpectedDateStrings } = require('../utils/utils.js');
|
|
8
|
-
const { groupByPass }
|
|
9
|
-
const { PubSubUtils }
|
|
7
|
+
const { getExpectedDateStrings, normalizeName } = require('../utils/utils.js');
|
|
8
|
+
const { groupByPass } = require('../WorkflowOrchestrator.js');
|
|
9
|
+
const { PubSubUtils } = require('../../core/utils/pubsub_utils');
|
|
10
10
|
|
|
11
11
|
const TOPIC_NAME = 'computation-tasks';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Dispatches computation tasks for a specific pass.
|
|
15
|
-
*
|
|
15
|
+
* Generates one Pub/Sub message per calculation per date.
|
|
16
16
|
*/
|
|
17
17
|
async function dispatchComputationPass(config, dependencies, computationManifest) {
|
|
18
18
|
const { logger } = dependencies;
|
|
@@ -21,17 +21,18 @@ async function dispatchComputationPass(config, dependencies, computationManifest
|
|
|
21
21
|
|
|
22
22
|
if (!passToRun) { return logger.log('ERROR', '[Dispatcher] No pass defined (COMPUTATION_PASS_TO_RUN). Aborting.'); }
|
|
23
23
|
|
|
24
|
-
// 1.
|
|
24
|
+
// 1. Get Calculations for this Pass
|
|
25
25
|
const passes = groupByPass(computationManifest);
|
|
26
26
|
const calcsInThisPass = passes[passToRun] || [];
|
|
27
27
|
|
|
28
28
|
if (!calcsInThisPass.length) { return logger.log('WARN', `[Dispatcher] No calcs for Pass ${passToRun}. Exiting.`); }
|
|
29
29
|
|
|
30
|
-
const calcNames = calcsInThisPass.map(c => c.name)
|
|
31
|
-
logger.log('INFO', `🚀 [Dispatcher] Preparing PASS ${passToRun}.`);
|
|
32
|
-
logger.log('INFO', `[Dispatcher]
|
|
30
|
+
const calcNames = calcsInThisPass.map(c => c.name);
|
|
31
|
+
logger.log('INFO', `🚀 [Dispatcher] Preparing PASS ${passToRun} (Granular Mode).`);
|
|
32
|
+
logger.log('INFO', `[Dispatcher] Target Calculations: [${calcNames.join(', ')}]`);
|
|
33
33
|
|
|
34
34
|
// 2. Determine Date Range
|
|
35
|
+
// (You can make this dynamic or config-driven)
|
|
35
36
|
const earliestDates = {
|
|
36
37
|
portfolio: new Date('2025-09-25T00:00:00Z'),
|
|
37
38
|
history: new Date('2025-11-05T00:00:00Z'),
|
|
@@ -44,35 +45,34 @@ async function dispatchComputationPass(config, dependencies, computationManifest
|
|
|
44
45
|
const endDateUTC = new Date(Date.UTC(new Date().getUTCFullYear(), new Date().getUTCMonth(), new Date().getUTCDate() - 1));
|
|
45
46
|
const allExpectedDates = getExpectedDateStrings(passEarliestDate, endDateUTC);
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const chunks = [];
|
|
55
|
-
for (let i = 0; i < allExpectedDates.length; i += BATCH_SIZE) { chunks.push(allExpectedDates.slice(i, i + BATCH_SIZE)); }
|
|
56
|
-
|
|
57
|
-
for (const chunk of chunks) {
|
|
58
|
-
const messages = chunk.map(dateStr => ({
|
|
59
|
-
json: {
|
|
60
|
-
action: 'RUN_COMPUTATION_DATE',
|
|
48
|
+
// 3. Generate Granular Tasks (Cartesian Product: Dates x Calculations)
|
|
49
|
+
const allTasks = [];
|
|
50
|
+
|
|
51
|
+
for (const dateStr of allExpectedDates) {
|
|
52
|
+
for (const calc of calcsInThisPass) {
|
|
53
|
+
allTasks.push({
|
|
54
|
+
action: 'RUN_COMPUTATION_DATE', // Maintained for compatibility
|
|
61
55
|
date: dateStr,
|
|
62
56
|
pass: passToRun,
|
|
57
|
+
computation: normalizeName(calc.name), // CRITICAL: Target specific calc
|
|
63
58
|
timestamp: Date.now()
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
try {
|
|
68
|
-
await pubsubUtils.publishMessageBatch(TOPIC_NAME, messages);
|
|
69
|
-
dispatchedCount += messages.length;
|
|
70
|
-
logger.log('INFO', `[Dispatcher] Dispatched batch of ${messages.length} tasks.`);
|
|
71
|
-
} catch (err) { logger.log('ERROR', `[Dispatcher] Failed to dispatch batch: ${err.message}`); }
|
|
59
|
+
});
|
|
60
|
+
}
|
|
72
61
|
}
|
|
73
62
|
|
|
74
|
-
logger.log('INFO', `[Dispatcher]
|
|
75
|
-
|
|
63
|
+
logger.log('INFO', `[Dispatcher] Generated ${allTasks.length} atomic tasks (${allExpectedDates.length} days × ${calcsInThisPass.length} calcs).`);
|
|
64
|
+
|
|
65
|
+
// 4. Batch Dispatch
|
|
66
|
+
// We send tasks in batches to Pub/Sub to be efficient,
|
|
67
|
+
// but the WORKERS will process them individually.
|
|
68
|
+
await pubsubUtils.batchPublishTasks(dependencies, {
|
|
69
|
+
topicName: TOPIC_NAME,
|
|
70
|
+
tasks: allTasks,
|
|
71
|
+
taskType: `computation-pass-${passToRun}`,
|
|
72
|
+
maxPubsubBatchSize: 100 // Safe batch size
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return { dispatched: allTasks.length };
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
module.exports = { dispatchComputationPass };
|
|
@@ -1,34 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* FILENAME: computation-system/helpers/computation_worker.js
|
|
3
3
|
* PURPOSE: Consumes computation tasks from Pub/Sub and executes them.
|
|
4
|
-
* UPDATED:
|
|
4
|
+
* UPDATED: Supports Granular Execution (Single Calculation filtering).
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const { runDateComputation, groupByPass } = require('../WorkflowOrchestrator.js');
|
|
8
8
|
const { getManifest } = require('../topology/ManifestLoader');
|
|
9
9
|
const { StructuredLogger } = require('../logger/logger');
|
|
10
|
+
const { normalizeName } = require('../utils/utils');
|
|
10
11
|
|
|
11
12
|
// 1. IMPORT CALCULATIONS
|
|
12
|
-
// We import the specific package containing the strategies (gem, pyro, core, etc.)
|
|
13
13
|
let calculationPackage;
|
|
14
14
|
try {
|
|
15
|
-
// Primary: Try to load from the installed npm package
|
|
16
15
|
calculationPackage = require('aiden-shared-calculations-unified');
|
|
17
16
|
} catch (e) {
|
|
18
17
|
console.error("FATAL: Could not load 'aiden-shared-calculations-unified'. Ensure it is installed or linked.");
|
|
19
18
|
throw e;
|
|
20
19
|
}
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
// The package exports { calculations: { ... }, utils: { ... } }
|
|
24
21
|
const calculations = calculationPackage.calculations;
|
|
25
22
|
|
|
26
23
|
/**
|
|
27
24
|
* Handles a single Pub/Sub message for a computation task.
|
|
28
|
-
* Supports both Gen 1 (Message) and Gen 2 (CloudEvent) formats.
|
|
29
|
-
* @param {object} message - The Pub/Sub message payload.
|
|
30
|
-
* @param {object} config - System configuration.
|
|
31
|
-
* @param {object} dependencies - Injected dependencies (db, generic logger, etc.).
|
|
32
25
|
*/
|
|
33
26
|
async function handleComputationTask(message, config, dependencies) {
|
|
34
27
|
|
|
@@ -45,7 +38,7 @@ async function handleComputationTask(message, config, dependencies) {
|
|
|
45
38
|
logger: systemLogger
|
|
46
39
|
};
|
|
47
40
|
|
|
48
|
-
const { logger } = runDependencies;
|
|
41
|
+
const { logger } = runDependencies;
|
|
49
42
|
|
|
50
43
|
// 4. LAZY LOAD MANIFEST
|
|
51
44
|
let computationManifest;
|
|
@@ -86,19 +79,37 @@ async function handleComputationTask(message, config, dependencies) {
|
|
|
86
79
|
return;
|
|
87
80
|
}
|
|
88
81
|
|
|
89
|
-
const { date, pass } = data;
|
|
82
|
+
const { date, pass, computation } = data; // Extract 'computation'
|
|
83
|
+
|
|
90
84
|
if (!date || !pass) {
|
|
91
85
|
logger.log('ERROR', `[Worker] Missing date or pass in payload: ${JSON.stringify(data)}`);
|
|
92
86
|
return;
|
|
93
87
|
}
|
|
94
88
|
|
|
95
|
-
|
|
96
|
-
|
|
89
|
+
// Load Full Pass
|
|
97
90
|
const passes = groupByPass(computationManifest);
|
|
98
|
-
|
|
91
|
+
let calcsInThisPass = passes[pass] || [];
|
|
99
92
|
|
|
93
|
+
// --- GRANULAR FILTERING ---
|
|
94
|
+
if (computation) {
|
|
95
|
+
const targetName = normalizeName(computation);
|
|
96
|
+
const targetCalc = calcsInThisPass.find(c => normalizeName(c.name) === targetName);
|
|
97
|
+
|
|
98
|
+
if (!targetCalc) {
|
|
99
|
+
logger.log('WARN', `[Worker] Targeted computation '${computation}' not found in Pass ${pass}. Skipping.`);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// We run ONLY this calculation
|
|
104
|
+
calcsInThisPass = [targetCalc];
|
|
105
|
+
logger.log('INFO', `[Worker] Granular Mode: Running ONLY ${targetCalc.name} for ${date}`);
|
|
106
|
+
} else {
|
|
107
|
+
logger.log('INFO', `[Worker] Bulk Mode: Running ${calcsInThisPass.length} calculations for ${date}`);
|
|
108
|
+
}
|
|
109
|
+
// ---------------------------
|
|
110
|
+
|
|
100
111
|
if (!calcsInThisPass.length) {
|
|
101
|
-
logger.log('WARN', `[Worker] No calculations found
|
|
112
|
+
logger.log('WARN', `[Worker] No calculations found to run.`);
|
|
102
113
|
return;
|
|
103
114
|
}
|
|
104
115
|
|
|
@@ -112,14 +123,15 @@ async function handleComputationTask(message, config, dependencies) {
|
|
|
112
123
|
);
|
|
113
124
|
|
|
114
125
|
if (result && result.updates && Object.keys(result.updates).length > 0) {
|
|
115
|
-
logger.log('INFO', `[Worker]
|
|
126
|
+
logger.log('INFO', `[Worker] Success ${date}. Updated: ${Object.keys(result.updates).join(', ')}`);
|
|
116
127
|
} else {
|
|
117
|
-
|
|
128
|
+
// In Granular Mode, this is common (e.g. if hash matched)
|
|
129
|
+
logger.log('INFO', `[Worker] Completed ${date} - No DB Writes (Up to date or skipped).`);
|
|
118
130
|
}
|
|
119
131
|
|
|
120
132
|
} catch (err) {
|
|
121
133
|
logger.log('ERROR', `[Worker] Fatal error processing task: ${err.message}`, { stack: err.stack });
|
|
122
|
-
throw err;
|
|
134
|
+
throw err; // Throwing ensures Pub/Sub retries this specific computation
|
|
123
135
|
}
|
|
124
136
|
}
|
|
125
137
|
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Build Reporter & Auto-Runner.
|
|
3
|
+
* Generates a "Pre-Flight" report of what the computation system WILL do.
|
|
4
|
+
* Simulates execution logic (Hash Mismatches) assuming perfect data availability.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { analyzeDateExecution } = require('../WorkflowOrchestrator');
|
|
8
|
+
const { fetchComputationStatus } = require('../persistence/StatusRepository');
|
|
9
|
+
const { normalizeName, getExpectedDateStrings } = require('../utils/utils');
|
|
10
|
+
const { FieldValue } = require('@google-cloud/firestore');
|
|
11
|
+
|
|
12
|
+
// Attempt to load package.json to get version. Path depends on where this is invoked.
|
|
13
|
+
let packageVersion = '1.0.237';
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* AUTO-RUN ENTRY POINT
|
|
18
|
+
* Checks if a report for the current version exists. If not, runs it.
|
|
19
|
+
* Designed to be called "Fire-and-Forget" at system init.
|
|
20
|
+
*/
|
|
21
|
+
async function ensureBuildReport(config, dependencies, manifest) {
|
|
22
|
+
const { db, logger } = dependencies;
|
|
23
|
+
const buildId = `v${packageVersion}`;
|
|
24
|
+
const reportRef = db.collection('computation_build_records').doc(buildId);
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const doc = await reportRef.get();
|
|
28
|
+
if (doc.exists) {
|
|
29
|
+
logger.log('INFO', `[BuildReporter] ✅ Pre-flight check for ${buildId} already exists. Skipping.`);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
logger.log('INFO', `[BuildReporter] 🚀 New Version Detected (${buildId}). Auto-running Pre-flight Report...`);
|
|
34
|
+
// We do NOT await this in the main thread if called during init,
|
|
35
|
+
// but here we are in an async function so we proceed.
|
|
36
|
+
await generateBuildReport(config, dependencies, manifest, 7, buildId);
|
|
37
|
+
|
|
38
|
+
} catch (e) {
|
|
39
|
+
logger.log('ERROR', `[BuildReporter] Auto-run check failed: ${e.message}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Generates the report and saves to Firestore.
|
|
45
|
+
* @param {object} config
|
|
46
|
+
* @param {object} dependencies
|
|
47
|
+
* @param {Array} manifest
|
|
48
|
+
* @param {number} daysBack - Days to simulate (default 7)
|
|
49
|
+
* @param {string} customBuildId - Optional ID override
|
|
50
|
+
*/
|
|
51
|
+
async function generateBuildReport(config, dependencies, manifest, daysBack = 90, customBuildId = null) {
|
|
52
|
+
const { db, logger } = dependencies;
|
|
53
|
+
const buildId = customBuildId || `manual_${Date.now()}`;
|
|
54
|
+
|
|
55
|
+
logger.log('INFO', `[BuildReporter] Generating Build Report: ${buildId} (Scope: ${daysBack} days)...`);
|
|
56
|
+
|
|
57
|
+
// 1. Determine Date Range
|
|
58
|
+
const today = new Date();
|
|
59
|
+
const startDate = new Date();
|
|
60
|
+
startDate.setDate(today.getDate() - daysBack);
|
|
61
|
+
|
|
62
|
+
// We check UP TO yesterday usually, as today might be partial.
|
|
63
|
+
// But let's check today too to see immediate effects.
|
|
64
|
+
const datesToCheck = getExpectedDateStrings(startDate, today);
|
|
65
|
+
const manifestMap = new Map(manifest.map(c => [normalizeName(c.name), c]));
|
|
66
|
+
|
|
67
|
+
const reportData = {
|
|
68
|
+
buildId,
|
|
69
|
+
version: packageVersion,
|
|
70
|
+
generatedAt: new Date().toISOString(),
|
|
71
|
+
summary: {},
|
|
72
|
+
dates: {}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
let totalReRuns = 0;
|
|
76
|
+
let totalNew = 0;
|
|
77
|
+
|
|
78
|
+
// 2. Iterate Dates & Simulate
|
|
79
|
+
for (const dateStr of datesToCheck) {
|
|
80
|
+
// A. Fetch REAL status from DB (What ran previously?)
|
|
81
|
+
const dailyStatus = await fetchComputationStatus(dateStr, config, dependencies);
|
|
82
|
+
|
|
83
|
+
// B. MOCK Root Data Availability
|
|
84
|
+
// We force this to TRUE to prove logic changes.
|
|
85
|
+
// We want to know: "IF data exists, would this run?"
|
|
86
|
+
const mockRootDataStatus = {
|
|
87
|
+
hasPortfolio: true,
|
|
88
|
+
hasInsights: true,
|
|
89
|
+
hasSocial: true,
|
|
90
|
+
hasHistory: true,
|
|
91
|
+
hasPrices: true
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// C. Run Logic Analysis
|
|
95
|
+
// Pass ENTIRE manifest to see global state
|
|
96
|
+
const analysis = analyzeDateExecution(dateStr, manifest, mockRootDataStatus, dailyStatus, manifestMap);
|
|
97
|
+
|
|
98
|
+
// D. Format Findings
|
|
99
|
+
const dateSummary = {
|
|
100
|
+
willRun: [],
|
|
101
|
+
willReRun: [],
|
|
102
|
+
blocked: []
|
|
103
|
+
// We omit 'skipped' to save space unless explicitly IMPOSSIBLE
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// -- Runnable (New) --
|
|
107
|
+
analysis.runnable.forEach(item => {
|
|
108
|
+
dateSummary.willRun.push({ name: item.name, reason: "New / No Previous Record" });
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// -- Re-Runs (Hash Mismatch / Migration) --
|
|
112
|
+
analysis.reRuns.forEach(item => {
|
|
113
|
+
let reason = "Hash Mismatch";
|
|
114
|
+
let details = `Old: ${item.oldHash?.substring(0,6)}... New: ${item.newHash?.substring(0,6)}...`;
|
|
115
|
+
|
|
116
|
+
if (item.previousCategory) {
|
|
117
|
+
reason = "Migration";
|
|
118
|
+
details = `Moving ${item.previousCategory} -> ${item.newCategory}`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
dateSummary.willReRun.push({ name: item.name, reason, details });
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// -- Blocked / Impossible --
|
|
125
|
+
analysis.impossible.forEach(item => {
|
|
126
|
+
dateSummary.blocked.push({ name: item.name, reason: item.reason }); // e.g. "Permanently Impossible"
|
|
127
|
+
});
|
|
128
|
+
analysis.failedDependency.forEach(item => {
|
|
129
|
+
dateSummary.blocked.push({ name: item.name, reason: `Dependency Missing: ${item.missing.join(', ')}` });
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Only add date to report if something interesting is happening
|
|
133
|
+
if (dateSummary.willRun.length || dateSummary.willReRun.length || dateSummary.blocked.length) {
|
|
134
|
+
reportData.dates[dateStr] = dateSummary;
|
|
135
|
+
totalNew += dateSummary.willRun.length;
|
|
136
|
+
totalReRuns += dateSummary.willReRun.length;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
reportData.summary = { totalReRuns, totalNew, scanRange: `${datesToCheck[0]} to ${datesToCheck[datesToCheck.length-1]}` };
|
|
141
|
+
|
|
142
|
+
// 3. Store Report
|
|
143
|
+
const reportRef = db.collection('computation_build_records').doc(buildId);
|
|
144
|
+
await reportRef.set(reportData);
|
|
145
|
+
|
|
146
|
+
// 4. Update 'latest' pointer
|
|
147
|
+
await db.collection('computation_build_records').doc('latest').set({
|
|
148
|
+
...reportData,
|
|
149
|
+
note: "Latest build report pointer."
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
logger.log('SUCCESS', `[BuildReporter] Report ${buildId} saved. Re-runs: ${totalReRuns}, New: ${totalNew}.`);
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
success: true,
|
|
156
|
+
reportId: buildId,
|
|
157
|
+
summary: reportData.summary
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
module.exports = { ensureBuildReport, generateBuildReport };
|
package/index.js
CHANGED
|
@@ -30,6 +30,9 @@ const { build: buildManifest } = require('./functions
|
|
|
30
30
|
const { runDateComputation: runComputationPass } = require('./functions/computation-system/WorkflowOrchestrator');
|
|
31
31
|
const { dispatchComputationPass } = require('./functions/computation-system/helpers/computation_dispatcher');
|
|
32
32
|
const { handleComputationTask } = require('./functions/computation-system/helpers/computation_worker');
|
|
33
|
+
// [NEW] Import Report Tools
|
|
34
|
+
const { ensureBuildReport, generateBuildReport } = require('./functions/computation-system/tools/BuildReporter');
|
|
35
|
+
|
|
33
36
|
const dataLoader = require('./functions/computation-system/utils/data_loader');
|
|
34
37
|
const computationUtils = require('./functions/computation-system/utils/utils');
|
|
35
38
|
|
|
@@ -87,6 +90,9 @@ const computationSystem = {
|
|
|
87
90
|
dataLoader,
|
|
88
91
|
computationUtils,
|
|
89
92
|
buildManifest,
|
|
93
|
+
// [NEW] Export Tools
|
|
94
|
+
ensureBuildReport,
|
|
95
|
+
generateBuildReport,
|
|
90
96
|
};
|
|
91
97
|
|
|
92
98
|
const api = {
|