bulltrackers-module 1.0.162 → 1.0.164

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.
@@ -11,11 +11,9 @@
11
11
  * It has removed all logic for deprecated folder structures (e.g., /historical/).
12
12
  */
13
13
 
14
- const { calculations } = require('aiden-shared-calculations-unified');
15
14
  const fs = require('fs');
16
15
  const path = require('path');
17
- const Viz = require('viz.js');
18
- const { Module, render } = require('viz.js/full.render.js');
16
+ const Viz = require('../node-graphviz'); // Uses your local module
19
17
 
20
18
  /* --------------------------------------------------
21
19
  * Pretty Console Helpers
@@ -88,9 +86,10 @@ function getDependencySet(endpoints, adjacencyList) {
88
86
  /**
89
87
  * Builds the full computation manifest.
90
88
  * @param {string[]} productLinesToRun - Array of product line categories (folder names) to build for.
89
+ * @param {object} calculations - The injected calculations object from the main application.
91
90
  * @returns {object[]} The final sorted manifest array.
92
91
  */
93
- function buildManifest(productLinesToRun = []) {
92
+ function buildManifest(productLinesToRun = [], calculations) {
94
93
  log.divider('Building Dynamic Manifest');
95
94
  log.info(`Target Product Lines: [${productLinesToRun.join(', ')}]`);
96
95
  const manifestMap = new Map();
@@ -134,6 +133,14 @@ function buildManifest(productLinesToRun = []) {
134
133
  adjacency.set(normalizedName, dependencies);
135
134
  inDegree.set(normalizedName, dependencies.length);
136
135
  dependencies.forEach(dep => { if (!reverseAdjacency.has(dep)) reverseAdjacency.set(dep, []); reverseAdjacency.get(dep).push(normalizedName); }); }
136
+
137
+ // --- UPDATED ---
138
+ // This 'calculations' object is now the one passed in as an argument.
139
+ if (!calculations || typeof calculations !== 'object') {
140
+ log.fatal('Calculations object was not provided or is invalid.');
141
+ throw new Error('Manifest build failed: Invalid calculations object.');
142
+ }
143
+
137
144
  for (const category in calculations) {
138
145
  if (category === 'legacy') { log.info('Skipping "legacy" calculations package.'); continue; }
139
146
  const group = calculations[category];
@@ -219,7 +226,7 @@ function buildManifest(productLinesToRun = []) {
219
226
  */
220
227
  async function generateSvgGraph(manifest, filename = 'dependency-tree.svg') {
221
228
  log.divider(`Generating SVG Graph: ${filename}`);
222
- const viz = new Viz({ Module, render });
229
+ const viz = new Viz({ worker: false });
223
230
  const categories = [...new Set(manifest.map(e => e.category))];
224
231
  const colors = ['#e6194b', '#3cb44b', '#ffe119', '#4363d8', '#f58231', '#911eb4', '#46f0f0', '#f032e6', '#bcf60c', '#fabebe', '#008080', '#e6beff', '#9a6324', '#fffac8', '#800000', '#aaffc3', '#808000', '#ffd8b1', '#000075', '#808080'];
225
232
  const colorMap = new Map(categories.map((cat, i) => [cat, colors[i % colors.length]]));
@@ -251,12 +258,13 @@ async function generateSvgGraph(manifest, filename = 'dependency-tree.svg') {
251
258
  /**
252
259
  * Main entry point for building and exporting the manifest.
253
260
  * @param {string[]} productLinesToRun - Array of product line categories (folder names) to build for.
261
+ * @param {object} calculations - The injected calculations object.
254
262
  */
255
- function build(productLinesToRun) {
263
+ function build(productLinesToRun, calculations) {
256
264
  try {
257
265
  // This function MUST NOT fail or generate SVGs.
258
266
  // It has one job: build and return the manifest array.
259
- const manifest = buildManifest(productLinesToRun);
267
+ const manifest = buildManifest(productLinesToRun, calculations);
260
268
  return manifest; // Returns the array
261
269
  } catch (error) {
262
270
  log.error(error.message);
@@ -266,29 +274,9 @@ function build(productLinesToRun) {
266
274
  }
267
275
  }
268
276
 
269
- module.exports = { build };
277
+ // --- REMOVED ---
278
+ // The entire 'if (require.main === module)' block has been removed
279
+ // to prevent any local 'require' conflicts. This file is now a pure
280
+ // module and must be called by another script.
270
281
 
271
- // --- MODIFIED ---
272
- // This block is ONLY executed when you run `node computation_manifest_builder.js`
273
- // This is now the ONLY place SVG generation happens.
274
- if (require.main === module) {
275
- (async () => { // This part remains async for the SVG generation
276
- log.info('Running manifest builder in local debug mode...');
277
- const allProductLines = Object.keys(calculations).filter(c => c !== 'legacy');
278
-
279
- try {
280
- // 1. Build the full manifest
281
- const fullManifest = buildManifest(allProductLines); // Sync call
282
-
283
- if (fullManifest) {
284
- // 2. Generate the full SVG graph
285
- await generateSvgGraph(fullManifest, 'dependency-tree-full.svg');
286
- log.info('Local debug build and graph generation complete.');
287
- } else {
288
- log.fatal('Local debug build FAILED.');
289
- }
290
- } catch (error) {
291
- log.error(error.message);
292
- }
293
- })();
294
- }
282
+ module.exports = { build, generateSvgGraph }; // <-- Also exporting generateSvgGraph
@@ -20,11 +20,25 @@ exports.fetchAndStoreInsights = async (config, dependencies) => {
20
20
  if (!selectedHeader || !selectedHeader.header) { throw new Error("Could not select a valid header for the request."); }
21
21
  logger.log('INFO', `[FetchInsightsHelpers] Using header ID: ${selectedHeader.id}`, { userAgent: selectedHeader.header['User-Agent'] });
22
22
  const fetchOptions = { headers: selectedHeader.header, timeout: 30000 };
23
- const response = await proxyManager.fetch(config.etoroInsightsUrl, fetchOptions);
23
+ let response;
24
+
25
+ try {
26
+ logger.log('INFO', '[FetchInsightsHelpers] Attempting fetch via proxy manager...');
27
+ response = await proxyManager.fetch(config.etoroInsightsUrl, fetchOptions);
28
+ } catch (proxyError) {
29
+ logger.log('WARNING', `[FetchInsightsHelpers] Proxy manager fetch failed. Attempting fallback with node-fetch. Error: ${proxyError.message}`);
30
+ try {
31
+ response = await fetch(config.etoroInsightsUrl, fetchOptions);
32
+ } catch (nodeFetchError) {
33
+ logger.log('ERROR', `[FetchInsightsHelpers] Fallback node-fetch also failed. Error: ${nodeFetchError.message}`);
34
+ throw nodeFetchError; // Throw the error from the last attempt
35
+ }
36
+ }
37
+
24
38
  if (!response || typeof response.text !== 'function') { const responseString = JSON.stringify(response, null, 2);
25
- logger.log('ERROR', `[FetchInsightsHelpers] Invalid or incomplete response received. Response object: ${responseString}`); throw new Error(`Invalid response structure received from proxy.`); }
39
+ logger.log('ERROR', `[FetchInsightsHelpers] Invalid or incomplete response received. Response object: ${responseString}`); throw new Error(`Invalid response structure received.`); }
26
40
  if (!response.ok) { const errorText = await response.text();
27
- throw new Error(`API request failed via proxy with status ${response.status}: ${errorText}`); }
41
+ throw new Error(`API request failed with status ${response.status}: ${errorText}`); }
28
42
  wasSuccessful = true;
29
43
  const insightsData = await response.json();
30
44
  if (!Array.isArray(insightsData) || insightsData.length === 0) { throw new Error('API returned empty or invalid data.'); }
@@ -66,9 +66,26 @@ exports.handleSocialTask = async (message, context, config, dependencies) => {
66
66
  const selectedHeader = await headerManager.selectHeader();
67
67
  let wasSuccess = false;
68
68
  let response;
69
- try { response = await proxyManager.fetch(url, { headers: selectedHeader.header }); if (!response.ok) { throw new Error(`API error ${response.status}`); } wasSuccess = true;
70
- } catch (fetchError) { logger.log('WARN', `[SocialTask/${taskId}] Fetch failed for offset ${offset}.`, { err: fetchError.message }); keepFetching = false; if (selectedHeader) headerManager.updatePerformance(selectedHeader.id, false); continue;
71
- } finally { if (selectedHeader) headerManager.updatePerformance(selectedHeader.id, wasSuccess); }
69
+ try {
70
+ response = await proxyManager.fetch(url, { headers: selectedHeader.header });
71
+ if (!response.ok) throw new Error(`API error ${response.status}`);
72
+ wasSuccess = true;
73
+ } catch (proxyError) {
74
+ logger.log('WARN', `[SocialTask/${taskId}] Proxy fetch failed for offset ${offset}: ${proxyError.message}`, { err: proxyError.message, url } );
75
+ logger.log('INFO', `[SocialTask/${taskId}] Retrying fetch without proxy for.`, url);
76
+ try {
77
+ response = await fetch(url, { headers: selectedHeader.header });
78
+ if (!response.ok) throw new Error(`Direct fetch API error ${response.status} for ${url}`);
79
+ } catch (directError) {
80
+ logger.log('ERROR', `[SocialTask/${taskId}] Direct fetch retry failed: ${directError.message}`, { err: directError.message , url});
81
+ if (selectedHeader) headerManager.updatePerformance(selectedHeader.id, false);
82
+ keepFetching = false;
83
+ continue;
84
+ }
85
+ } finally {
86
+ if (selectedHeader) headerManager.updatePerformance(selectedHeader.id, wasSuccess);
87
+ logger.log('DEBUG', 'Response succeeded for', url)
88
+ }
72
89
  const page = await response.json();
73
90
  const discussions = page?.discussions;
74
91
  if (!Array.isArray(discussions) || discussions.length === 0) { logger.log('INFO', `[SocialTask/${taskId}] No more posts found at offset ${offset}. Stopping.`); keepFetching = false; continue; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.162",
3
+ "version": "1.0.164",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -33,13 +33,15 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@google-cloud/firestore": "^7.11.3",
36
- "sharedsetup": "latest",
37
- "require-all": "^3.0.0",
38
36
  "@google-cloud/pubsub": "latest",
39
- "express": "^4.19.2",
37
+ "aiden-shared-calculations-unified": "^1.0.81",
40
38
  "cors": "^2.8.5",
39
+ "dotenv": "latest",
40
+ "express": "^4.19.2",
41
+ "node-graphviz": "^0.1.1",
41
42
  "p-limit": "^3.1.0",
42
- "dotenv": "latest"
43
+ "require-all": "^3.0.0",
44
+ "sharedsetup": "latest"
43
45
  },
44
46
  "devDependencies": {
45
47
  "bulltracker-deployer": "file:../bulltracker-deployer"