bulltrackers-module 1.0.644 → 1.0.647

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.
@@ -231,7 +231,8 @@ function buildManifest(productLinesToRun = [], calculations) {
231
231
  const manifestEntry = {
232
232
  name: normalizedName,
233
233
  class: Class,
234
- category: folderName === 'core' && metadata.category ? metadata.category : folderName,
234
+ // [CHANGED] Strictly use the folderName as the category.
235
+ category: folderName,
235
236
  sourcePackage: folderName,
236
237
  type: metadata.type,
237
238
  isPage: metadata.isPage === true,
@@ -84,6 +84,9 @@ class CachedDataLoader {
84
84
 
85
85
  async loadInsights(dateStr) {
86
86
  if (this.cache.insights.has(dateStr)) return this.cache.insights.get(dateStr);
87
+ const collectionName = this.config.insightsCollectionName || 'daily_instrument_insights';
88
+ const path = `${collectionName}/${dateStr}`;
89
+ this.deps.logger?.log('INFO', `[CachedDataLoader] 📂 Loading Root Data 'insights' from: ${path}`);
87
90
  const promise = loadDailyInsights(this.config, this.deps, dateStr);
88
91
  this.cache.insights.set(dateStr, promise);
89
92
  return promise;
@@ -91,6 +94,9 @@ class CachedDataLoader {
91
94
 
92
95
  async loadSocial(dateStr) {
93
96
  if (this.cache.social.has(dateStr)) return this.cache.social.get(dateStr);
97
+ const collectionName = this.config.socialInsightsCollection || 'daily_social_insights';
98
+ const path = `${collectionName}/${dateStr}`;
99
+ this.deps.logger?.log('INFO', `[CachedDataLoader] 📂 Loading Root Data 'social' from: ${path}`);
94
100
  const promise = loadDailySocialPostInsights(this.config, this.deps, dateStr);
95
101
  this.cache.social.set(dateStr, promise);
96
102
  return promise;
@@ -98,6 +104,9 @@ class CachedDataLoader {
98
104
 
99
105
  async loadVerifications() {
100
106
  if (this.cache.verifications) return this.cache.verifications;
107
+ const collectionName = this.config.verificationsCollection || 'verification_profiles';
108
+ const path = `${collectionName}`;
109
+ this.deps.logger?.log('INFO', `[CachedDataLoader] 📂 Loading Root Data 'verifications' from: ${path}`);
101
110
  const verifications = await loadVerificationProfiles(this.config, this.deps);
102
111
  this.cache.verifications = verifications;
103
112
  return verifications;
@@ -105,6 +114,9 @@ class CachedDataLoader {
105
114
 
106
115
  async loadRankings(dateStr) {
107
116
  if (this.cache.rankings.has(dateStr)) return this.cache.rankings.get(dateStr);
117
+ const collectionName = this.config.popularInvestorRankingsCollection || 'popular_investor_rankings';
118
+ const path = `${collectionName}/${dateStr}`;
119
+ this.deps.logger?.log('INFO', `[CachedDataLoader] 📂 Loading Root Data 'rankings' from: ${path}`);
108
120
  const promise = loadPopularInvestorRankings(this.config, this.deps, dateStr);
109
121
  this.cache.rankings.set(dateStr, promise);
110
122
  return promise;
@@ -112,6 +124,9 @@ class CachedDataLoader {
112
124
 
113
125
  async loadRatings(dateStr) {
114
126
  if (this.cache.ratings.has(dateStr)) return this.cache.ratings.get(dateStr);
127
+ const collectionName = this.config.piRatingsCollection || 'PIRatingsData';
128
+ const path = `${collectionName}/${dateStr}`;
129
+ this.deps.logger?.log('INFO', `[CachedDataLoader] 📂 Loading Root Data 'ratings' from: ${path}`);
115
130
  const promise = loadPIRatings(this.config, this.deps, dateStr);
116
131
  this.cache.ratings.set(dateStr, promise);
117
132
  return promise;
@@ -119,6 +134,9 @@ class CachedDataLoader {
119
134
 
120
135
  async loadPageViews(dateStr) {
121
136
  if (this.cache.pageViews.has(dateStr)) return this.cache.pageViews.get(dateStr);
137
+ const collectionName = this.config.piPageViewsCollection || 'PIPageViewsData';
138
+ const path = `${collectionName}/${dateStr}`;
139
+ this.deps.logger?.log('INFO', `[CachedDataLoader] 📂 Loading Root Data 'pageViews' from: ${path}`);
122
140
  const promise = loadPIPageViews(this.config, this.deps, dateStr);
123
141
  this.cache.pageViews.set(dateStr, promise);
124
142
  return promise;
@@ -126,6 +144,9 @@ class CachedDataLoader {
126
144
 
127
145
  async loadWatchlistMembership(dateStr) {
128
146
  if (this.cache.watchlistMembership.has(dateStr)) return this.cache.watchlistMembership.get(dateStr);
147
+ const collectionName = this.config.watchlistMembershipCollection || 'WatchlistMembershipData';
148
+ const path = `${collectionName}/${dateStr}`;
149
+ this.deps.logger?.log('INFO', `[CachedDataLoader] 📂 Loading Root Data 'watchlistMembership' from: ${path}`);
129
150
  const promise = loadWatchlistMembershipData(this.config, this.deps, dateStr);
130
151
  this.cache.watchlistMembership.set(dateStr, promise);
131
152
  return promise;
@@ -133,6 +154,9 @@ class CachedDataLoader {
133
154
 
134
155
  async loadAlertHistory(dateStr) {
135
156
  if (this.cache.alertHistory.has(dateStr)) return this.cache.alertHistory.get(dateStr);
157
+ const collectionName = this.config.piAlertHistoryCollection || 'PIAlertHistoryData';
158
+ const path = `${collectionName}/${dateStr}`;
159
+ this.deps.logger?.log('INFO', `[CachedDataLoader] 📂 Loading Root Data 'alertHistory' from: ${path}`);
136
160
  const promise = loadPIAlertHistory(this.config, this.deps, dateStr);
137
161
  this.cache.alertHistory.set(dateStr, promise);
138
162
  return promise;
@@ -243,6 +267,10 @@ class CachedDataLoader {
243
267
  let foundCount = 0;
244
268
 
245
269
  if (batchRefs.length > 0) {
270
+ // Log summary of all paths being loaded
271
+ const paths = batchRefs.map(ref => ref.path).join(', ');
272
+ this.deps.logger?.log('INFO', `[CachedDataLoader] 📂 Batch loading ${batchRefs.length} documents for '${loaderMethod}': ${paths}`);
273
+
246
274
  try {
247
275
  const snapshots = await this.deps.db.getAll(...batchRefs);
248
276
 
@@ -279,12 +307,16 @@ class CachedDataLoader {
279
307
  }
280
308
  }
281
309
 
282
- return {
310
+ const summary = {
283
311
  dates: Object.keys(results).sort(),
284
312
  data: results,
285
313
  found: foundCount,
286
314
  requested: lookbackDays
287
315
  };
316
+
317
+ this.deps.logger?.log('INFO', `[CachedDataLoader] ✅ Loaded ${foundCount}/${lookbackDays} dates for '${loaderMethod}' (found: ${summary.dates.join(', ') || 'none'})`);
318
+
319
+ return summary;
288
320
  }
289
321
 
290
322
  /**
@@ -300,6 +332,7 @@ class CachedDataLoader {
300
332
  d.setUTCDate(d.getUTCDate() - i);
301
333
  const dString = d.toISOString().slice(0, 10);
302
334
 
335
+ // Log path for legacy loader (will be logged by individual load methods)
303
336
  promises.push(
304
337
  this[loaderMethod](dString)
305
338
  .then(data => ({ date: dString, data }))
@@ -9,6 +9,34 @@
9
9
  const { normalizeName } = require('../utils/utils');
10
10
  const zlib = require('zlib');
11
11
 
12
+ /**
13
+ * Checks if data is effectively empty (no usable content).
14
+ * @param {any} data - The data to check
15
+ * @returns {boolean} True if data is empty/null/undefined or contains no meaningful content
16
+ */
17
+ function isDataEmpty(data) {
18
+ if (!data || data === null || data === undefined) return true;
19
+
20
+ // Check if it's an object with only metadata fields
21
+ if (typeof data === 'object' && !Array.isArray(data)) {
22
+ const keys = Object.keys(data);
23
+ // If only metadata/internal fields, consider it empty
24
+ const metadataFields = ['_completed', '_compressed', '_sharded', '_shardCount', '_isPageMode', '_pageCount', '_lastUpdated', '_expireAt'];
25
+ const hasOnlyMetadata = keys.length > 0 && keys.every(k => metadataFields.includes(k) || k.startsWith('_'));
26
+
27
+ if (hasOnlyMetadata) return true;
28
+
29
+ // If object has no keys (after filtering metadata), it's empty
30
+ const dataKeys = keys.filter(k => !k.startsWith('_'));
31
+ if (dataKeys.length === 0) return true;
32
+ }
33
+
34
+ // Check if it's an empty array
35
+ if (Array.isArray(data) && data.length === 0) return true;
36
+
37
+ return false;
38
+ }
39
+
12
40
  /**
13
41
  * BRIDGE FUNCTION: Matches WorkflowOrchestrator signature.
14
42
  * Adapts (dateStr, calcs, manifest, ...) -> (dateObj, calcs, ..., manifestLookup).
@@ -28,7 +56,9 @@ async function fetchExistingResults(dateStr, calcs, fullManifest, config, deps,
28
56
  const dateObj = new Date(dateStr + (dateStr.includes('T') ? '' : 'T00:00:00Z'));
29
57
 
30
58
  // 3. Delegate to fetchDependencies
31
- return fetchDependencies(dateObj, calcs, config, deps, manifestLookup);
59
+ // CRITICAL: For historical context (yesterday's data), allow missing dependencies
60
+ // Historical lookbacks are optional - gaps in historical data are permissible
61
+ return fetchDependencies(dateObj, calcs, config, deps, manifestLookup, isHistoricalContext);
32
62
  }
33
63
 
34
64
  /**
@@ -38,8 +68,9 @@ async function fetchExistingResults(dateStr, calcs, fullManifest, config, deps,
38
68
  * @param {Object} config - System config.
39
69
  * @param {Object} deps - System dependencies (db, logger).
40
70
  * @param {Object} manifestLookup - Map of { [calcName]: categoryString }.
71
+ * @param {boolean} allowMissing - If true, missing/empty dependencies are allowed (for historical/lookback scenarios).
41
72
  */
42
- async function fetchDependencies(date, calcs, config, deps, manifestLookup = {}) {
73
+ async function fetchDependencies(date, calcs, config, deps, manifestLookup = {}, allowMissing = false) {
43
74
  const { db, logger } = deps;
44
75
  const dStr = date.toISOString().slice(0, 10);
45
76
 
@@ -65,27 +96,87 @@ async function fetchDependencies(date, calcs, config, deps, manifestLookup = {})
65
96
 
66
97
  if (needed.size === 0) return {};
67
98
 
68
- logger.log('INFO', `[DependencyFetcher] Fetching ${needed.size} dependencies for ${dStr}`);
99
+ const calcNames = calcs.map(c => c.name || c.constructor?.name || 'unknown').join(', ');
100
+ logger.log('INFO', `[DependencyFetcher] Fetching ${needed.size} dependencies for computation(s): ${calcNames} (date: ${dStr})`);
69
101
 
70
102
  const results = {};
103
+ const missingDeps = [];
104
+ const emptyDeps = [];
105
+
106
+ // Helper to build path string
107
+ const buildPath = (category, normName) => {
108
+ return `${config.resultsCollection || 'computation_results'}/${dStr}/${config.resultsSubcollection || 'results'}/${category}/${config.computationsSubcollection || 'computations'}/${normName}`;
109
+ };
110
+
71
111
  // CHANGED: Iterate over the entries to access both normalized and original names
72
112
  const promises = Array.from(needed.entries()).map(async ([normName, originalName]) => {
113
+ // Resolve Category from Lookup, default to 'analytics' if unknown
114
+ // Note: manifestLookup keys are expected to be normalized
115
+ const category = manifestLookup[normName] || 'analytics';
116
+ const path = buildPath(category, normName);
117
+
73
118
  try {
74
- // Resolve Category from Lookup, default to 'analytics' if unknown
75
- // Note: manifestLookup keys are expected to be normalized
76
- const category = manifestLookup[normName] || 'analytics';
119
+ // Pass logger in config for fetchSingleResult
120
+ const fetchConfig = { ...config, logger };
77
121
 
78
122
  // Fetch using the normalized name (system standard)
79
- const data = await fetchSingleResult(db, config, dStr, normName, category);
123
+ const data = await fetchSingleResult(db, fetchConfig, dStr, normName, category);
80
124
 
81
- // CHANGED: Store result using the ORIGINAL name so context.computed['CaseSensitive'] works
82
- if (data) results[originalName] = data;
125
+ // CRITICAL: Validate that dependency exists and has data
126
+ if (!data) {
127
+ missingDeps.push({ name: originalName, normalizedName: normName, path });
128
+ // Log level depends on context - ERROR for current date, INFO for historical
129
+ if (allowMissing) {
130
+ logger.log('INFO', `[DependencyFetcher] ⚠️ Missing dependency '${originalName}' (${normName}) from: ${path} (Historical context - allowed)`);
131
+ } else {
132
+ logger.log('ERROR', `[DependencyFetcher] ❌ Missing required dependency '${originalName}' (${normName}) from: ${path}`);
133
+ }
134
+ } else if (isDataEmpty(data)) {
135
+ emptyDeps.push({ name: originalName, normalizedName: normName, path });
136
+ // Log level depends on context - ERROR for current date, INFO for historical
137
+ if (allowMissing) {
138
+ logger.log('INFO', `[DependencyFetcher] ⚠️ Empty dependency '${originalName}' (${normName}) from: ${path} (Historical context - allowed)`);
139
+ } else {
140
+ logger.log('ERROR', `[DependencyFetcher] ❌ Empty dependency '${originalName}' (${normName}) from: ${path} - Document exists but contains no usable data`);
141
+ }
142
+ } else {
143
+ // CHANGED: Store result using the ORIGINAL name so context.computed['CaseSensitive'] works
144
+ results[originalName] = data;
145
+ }
83
146
  } catch (e) {
84
- logger.log('WARN', `[DependencyFetcher] Failed to load dependency ${originalName}: ${e.message}`);
147
+ missingDeps.push({ name: originalName, normalizedName: normName, path, error: e.message });
148
+ // Log level depends on context - ERROR for current date, INFO for historical
149
+ if (allowMissing) {
150
+ logger.log('INFO', `[DependencyFetcher] ⚠️ Failed to load dependency '${originalName}' (${normName}) from: ${path} - Error: ${e.message} (Historical context - allowed)`);
151
+ } else {
152
+ logger.log('ERROR', `[DependencyFetcher] ❌ Failed to load dependency '${originalName}' (${normName}) from: ${path} - Error: ${e.message}`);
153
+ }
85
154
  }
86
155
  });
87
156
 
88
157
  await Promise.all(promises);
158
+
159
+ // CRITICAL: Fail if any required dependencies are missing or empty
160
+ // EXCEPTION: For historical/lookback scenarios, missing dependencies are permissible
161
+ if ((missingDeps.length > 0 || emptyDeps.length > 0) && !allowMissing) {
162
+ const missingList = missingDeps.map(d => `'${d.name}' (path: ${d.path}${d.error ? `, error: ${d.error}` : ''})`).join(', ');
163
+ const emptyList = emptyDeps.map(d => `'${d.name}' (path: ${d.path})`).join(', ');
164
+
165
+ const errorMsg = `[DependencyFetcher] ❌ CRITICAL: Cannot proceed - Required dependencies missing or empty for computation(s): ${calcNames}\n` +
166
+ `Missing dependencies (${missingDeps.length}): ${missingList}\n` +
167
+ (emptyDeps.length > 0 ? `Empty dependencies (${emptyDeps.length}): ${emptyList}\n` : '') +
168
+ `Date: ${dStr}\n` +
169
+ `This computation will FAIL and no results will be saved.`;
170
+
171
+ logger.log('ERROR', errorMsg);
172
+ throw new Error(errorMsg);
173
+ } else if (missingDeps.length > 0 || emptyDeps.length > 0) {
174
+ // Historical/lookback context - log but allow missing dependencies
175
+ const missingList = missingDeps.map(d => `'${d.name}' (path: ${d.path})`).join(', ');
176
+ const emptyList = emptyDeps.map(d => `'${d.name}' (path: ${d.path})`).join(', ');
177
+ logger.log('INFO', `[DependencyFetcher] ⚠️ Historical/Lookback context: Missing/empty dependencies allowed for ${calcNames} on ${dStr}. Missing: ${missingList}${emptyDeps.length > 0 ? `, Empty: ${emptyList}` : ''}`);
178
+ }
179
+
89
180
  return results;
90
181
  }
91
182
 
@@ -110,7 +201,7 @@ async function fetchResultSeries(endDateStr, calcNames, manifestLookup, config,
110
201
  // Initialize structure
111
202
  calcNames.forEach(name => { results[normalizeName(name)] = {}; });
112
203
 
113
- logger.log('INFO', `[DependencyFetcher] Loading series for ${calcNames.length} calcs over ${lookbackDays} days.`);
204
+ logger.log('INFO', `[DependencyFetcher] Loading series for ${calcNames.length} computation dependencies over ${lookbackDays} days: ${calcNames.join(', ')}`);
114
205
 
115
206
  const fetchOps = [];
116
207
 
@@ -120,11 +211,19 @@ async function fetchResultSeries(endDateStr, calcNames, manifestLookup, config,
120
211
  const category = manifestLookup[normName] || 'analytics';
121
212
 
122
213
  fetchOps.push(async () => {
123
- const val = await fetchSingleResult(db, config, dateStr, rawName, category);
124
- if (val) {
214
+ const fetchConfig = { ...config, logger };
215
+ const val = await fetchSingleResult(db, fetchConfig, dateStr, rawName, category);
216
+ // CRITICAL: For series/lookback, we allow missing dates (historical lookback may have gaps)
217
+ // This is expected behavior - not all historical dates will have data
218
+ // But we still validate that the data isn't empty if it exists
219
+ if (val && !isDataEmpty(val)) {
125
220
  if (!results[normName]) results[normName] = {};
126
221
  results[normName][dateStr] = val;
222
+ } else if (val && isDataEmpty(val)) {
223
+ // Log but don't fail - series can have gaps, empty data is treated as missing
224
+ logger.log('INFO', `[DependencyFetcher] ⚠️ Empty dependency '${rawName}' found at ${dateStr} in series (allowing gap - historical lookback)`);
127
225
  }
226
+ // If val is null, that's fine - missing dates in historical series are permissible
128
227
  });
129
228
  }
130
229
  }
@@ -142,18 +241,49 @@ async function fetchResultSeries(endDateStr, calcNames, manifestLookup, config,
142
241
  * Core Helper: Fetches a single result, handles Sharding & Compression.
143
242
  */
144
243
  async function fetchSingleResult(db, config, dateStr, name, category) {
145
- const docRef = db.collection(config.resultsCollection)
244
+ const resultsCollection = config.resultsCollection || 'computation_results';
245
+ const resultsSubcollection = config.resultsSubcollection || 'results';
246
+ const computationsSubcollection = config.computationsSubcollection || 'computations';
247
+
248
+ const path = `${resultsCollection}/${dateStr}/${resultsSubcollection}/${category}/${computationsSubcollection}/${name}`;
249
+
250
+ // Log path - use console.log if logger not available (for backward compatibility)
251
+ if (config.logger) {
252
+ config.logger.log('INFO', `[DependencyFetcher] 📂 Loading Dependency '${name}' from: ${path}`);
253
+ } else {
254
+ console.log(`[DependencyFetcher] 📂 Loading Dependency '${name}' from: ${path}`);
255
+ }
256
+
257
+ const docRef = db.collection(resultsCollection)
146
258
  .doc(dateStr)
147
- .collection(config.resultsSubcollection)
259
+ .collection(resultsSubcollection)
148
260
  .doc(category)
149
- .collection(config.computationsSubcollection)
261
+ .collection(computationsSubcollection)
150
262
  .doc(name);
151
263
 
152
264
  const snap = await docRef.get();
153
- if (!snap.exists) return null;
265
+ if (!snap.exists) {
266
+ // Log the missing document path clearly
267
+ if (config.logger) {
268
+ config.logger.log('ERROR', `[DependencyFetcher] ❌ Document does not exist at: ${path}`);
269
+ } else {
270
+ console.error(`[DependencyFetcher] ❌ Document does not exist at: ${path}`);
271
+ }
272
+ return null;
273
+ }
154
274
 
155
275
  let data = snap.data();
156
276
 
277
+ // Check if document exists but is effectively empty (only metadata)
278
+ if (isDataEmpty(data)) {
279
+ if (config.logger) {
280
+ config.logger.log('ERROR', `[DependencyFetcher] ❌ Document exists but is empty at: ${path} (only contains metadata fields)`);
281
+ } else {
282
+ console.error(`[DependencyFetcher] ❌ Document exists but is empty at: ${path} (only contains metadata fields)`);
283
+ }
284
+ return null; // Return null so it gets caught as missing
285
+ }
286
+
157
287
  // 1. Handle Compression
158
288
  if (data._compressed && data.payload) {
159
289
  try {
@@ -165,27 +295,74 @@ async function fetchSingleResult(db, config, dateStr, name, category) {
165
295
  data = { ...data, ...realData };
166
296
  delete data.payload;
167
297
  } catch (e) {
168
- console.warn(`[DependencyFetcher] Decompression failed for ${name}: ${e.message}`);
298
+ const errorMsg = `Decompression failed for ${name}: ${e.message}`;
299
+ if (config.logger) {
300
+ config.logger.log('ERROR', `[DependencyFetcher] ❌ ${errorMsg} at: ${path}`);
301
+ } else {
302
+ console.error(`[DependencyFetcher] ❌ ${errorMsg} at: ${path}`);
303
+ }
169
304
  return null;
170
305
  }
171
306
  }
172
307
 
173
308
  // 2. Handle Sharding
174
309
  if (data._sharded) {
310
+ const shardPath = `${path}/_shards`;
311
+ if (config.logger) {
312
+ config.logger.log('INFO', `[DependencyFetcher] 📂 Loading Shards for '${name}' from: ${shardPath}`);
313
+ } else {
314
+ console.log(`[DependencyFetcher] 📂 Loading Shards for '${name}' from: ${shardPath}`);
315
+ }
316
+
175
317
  const shardCol = docRef.collection('_shards');
176
318
  const shardSnaps = await shardCol.get();
177
319
 
178
- if (!shardSnaps.empty) {
179
- shardSnaps.forEach(shard => {
180
- const shardData = shard.data();
181
- // Merge shard contents, ignoring internal metadata if it clashes
182
- Object.entries(shardData).forEach(([k, v]) => {
183
- if (!k.startsWith('_')) {
184
- data[k] = v;
185
- }
186
- });
320
+ if (shardSnaps.empty) {
321
+ // No shards found - this is a problem
322
+ if (config.logger) {
323
+ config.logger.log('ERROR', `[DependencyFetcher] Document marked as sharded but no shards found at: ${shardPath}`);
324
+ } else {
325
+ console.error(`[DependencyFetcher] ❌ Document marked as sharded but no shards found at: ${shardPath}`);
326
+ }
327
+ return null; // Return null so it gets caught as missing
328
+ }
329
+
330
+ // Merge shard contents
331
+ let hasData = false;
332
+ shardSnaps.forEach(shard => {
333
+ const shardData = shard.data();
334
+ const shardId = shard.id;
335
+ if (config.logger) {
336
+ config.logger.log('TRACE', `[DependencyFetcher] 📂 Loading Shard '${shardId}' for '${name}' from: ${shardPath}/${shardId}`);
337
+ }
338
+ // Merge shard contents, ignoring internal metadata if it clashes
339
+ Object.entries(shardData).forEach(([k, v]) => {
340
+ if (!k.startsWith('_')) {
341
+ data[k] = v;
342
+ hasData = true;
343
+ }
187
344
  });
345
+ });
346
+
347
+ // If shards contained no actual data, treat as empty
348
+ if (!hasData) {
349
+ if (config.logger) {
350
+ config.logger.log('ERROR', `[DependencyFetcher] ❌ Shards found but contain no data at: ${shardPath}`);
351
+ } else {
352
+ console.error(`[DependencyFetcher] ❌ Shards found but contain no data at: ${shardPath}`);
353
+ }
354
+ return null;
355
+ }
356
+ }
357
+
358
+ // Final validation: ensure we have usable data after all processing
359
+ if (isDataEmpty(data)) {
360
+ if (config.logger) {
361
+ config.logger.log('ERROR', `[DependencyFetcher] ❌ Dependency '${name}' loaded but is empty (no usable data) at: ${path}`);
362
+ } else {
363
+ console.error(`[DependencyFetcher] ❌ Dependency '${name}' loaded but is empty (no usable data) at: ${path}`);
188
364
  }
365
+ return null;
189
366
  }
190
367
 
191
368
  return data;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.644",
3
+ "version": "1.0.647",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [