bulltrackers-module 1.0.647 → 1.0.649

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.
@@ -274,15 +274,9 @@ async function fetchSingleResult(db, config, dateStr, name, category) {
274
274
 
275
275
  let data = snap.data();
276
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
- }
277
+ // CRITICAL: Don't check if empty yet - we need to load shards/compressed data first
278
+ // A sharded document will only have metadata in the pointer doc, but the actual data is in shards
279
+ // A compressed document will only have metadata + payload, but the actual data is in the payload
286
280
 
287
281
  // 1. Handle Compression
288
282
  if (data._compressed && data.payload) {
@@ -305,7 +299,7 @@ async function fetchSingleResult(db, config, dateStr, name, category) {
305
299
  }
306
300
  }
307
301
 
308
- // 2. Handle Sharding
302
+ // 2. Handle Sharding (MUST happen before empty check)
309
303
  if (data._sharded) {
310
304
  const shardPath = `${path}/_shards`;
311
305
  if (config.logger) {
@@ -330,11 +324,36 @@ async function fetchSingleResult(db, config, dateStr, name, category) {
330
324
  // Merge shard contents
331
325
  let hasData = false;
332
326
  shardSnaps.forEach(shard => {
333
- const shardData = shard.data();
327
+ let shardData = shard.data();
334
328
  const shardId = shard.id;
335
329
  if (config.logger) {
336
330
  config.logger.log('TRACE', `[DependencyFetcher] 📂 Loading Shard '${shardId}' for '${name}' from: ${shardPath}/${shardId}`);
337
331
  }
332
+
333
+ // CRITICAL: Shards themselves can be compressed (common in big data)
334
+ // Decompress the shard if needed before merging
335
+ if (shardData._compressed && shardData.payload) {
336
+ try {
337
+ const buffer = (shardData.payload instanceof Buffer) ? shardData.payload :
338
+ (shardData.payload._byteString ? Buffer.from(shardData.payload._byteString, 'base64') :
339
+ Buffer.from(shardData.payload));
340
+ const decompressed = zlib.gunzipSync(buffer);
341
+ const jsonStr = decompressed.toString('utf8');
342
+ const realData = JSON.parse(jsonStr);
343
+ // If it's double-encoded, parse again
344
+ const parsedData = (typeof realData === 'string') ? JSON.parse(realData) : realData;
345
+ shardData = { ...shardData, ...parsedData };
346
+ delete shardData.payload;
347
+ } catch (e) {
348
+ if (config.logger) {
349
+ config.logger.log('ERROR', `[DependencyFetcher] ❌ Failed to decompress shard '${shardId}' for '${name}': ${e.message}`);
350
+ } else {
351
+ console.error(`[DependencyFetcher] ❌ Failed to decompress shard '${shardId}' for '${name}': ${e.message}`);
352
+ }
353
+ // Continue with uncompressed data if decompression fails
354
+ }
355
+ }
356
+
338
357
  // Merge shard contents, ignoring internal metadata if it clashes
339
358
  Object.entries(shardData).forEach(([k, v]) => {
340
359
  if (!k.startsWith('_')) {
@@ -353,9 +372,27 @@ async function fetchSingleResult(db, config, dateStr, name, category) {
353
372
  }
354
373
  return null;
355
374
  }
375
+
376
+ // After loading shards, remove shard metadata from data object for cleaner output
377
+ // Keep only the actual data fields
378
+ const cleanedData = {};
379
+ const dataKeys = [];
380
+ Object.entries(data).forEach(([k, v]) => {
381
+ if (!k.startsWith('_')) {
382
+ cleanedData[k] = v;
383
+ dataKeys.push(k);
384
+ }
385
+ });
386
+ data = cleanedData;
387
+
388
+ // Log what we loaded for debugging
389
+ if (config.logger) {
390
+ config.logger.log('INFO', `[DependencyFetcher] ✅ Loaded ${shardSnaps.size} shard(s) for '${name}'. Data fields: ${dataKeys.length > 0 ? dataKeys.slice(0, 10).join(', ') + (dataKeys.length > 10 ? `... (+${dataKeys.length - 10} more)` : '') : 'none'}`);
391
+ }
356
392
  }
357
393
 
358
- // Final validation: ensure we have usable data after all processing
394
+ // Final validation: ensure we have usable data after all processing (decompression + sharding)
395
+ // Only check if we haven't already determined it's empty
359
396
  if (isDataEmpty(data)) {
360
397
  if (config.logger) {
361
398
  config.logger.log('ERROR', `[DependencyFetcher] ❌ Dependency '${name}' loaded but is empty (no usable data) at: ${path}`);
@@ -196,9 +196,50 @@ class MetaExecutor {
196
196
  seriesData
197
197
  });
198
198
 
199
+ // DEBUG: Log dependency availability
200
+ const depNames = c.getDependencies ? c.getDependencies() : (c.dependencies || []);
201
+ depNames.forEach(depName => {
202
+ const depData = context.computed[depName];
203
+ if (depData) {
204
+ const keys = Object.keys(depData);
205
+ logger.log('INFO', `[MetaExecutor] ✅ Dependency '${depName}' available for ${c.name}. Keys: ${keys.length} (sample: ${keys.slice(0, 5).join(', ')})`);
206
+ } else {
207
+ logger.log('ERROR', `[MetaExecutor] ❌ Dependency '${depName}' MISSING for ${c.name} in context.computed`);
208
+ }
209
+ });
210
+
199
211
  try {
200
212
  const result = await inst.process(context);
201
- inst.results = { [dStr]: { global: result } };
213
+
214
+ // DEBUG: Log result before saving
215
+ if (result && typeof result === 'object') {
216
+ const resultKeys = Object.keys(result);
217
+ logger.log('INFO', `[MetaExecutor] ✅ ${c.name} computed result. Keys: ${resultKeys.length} (sample: ${resultKeys.slice(0, 10).join(', ')})`);
218
+ if (resultKeys.length === 0) {
219
+ logger.log('WARN', `[MetaExecutor] ⚠️ ${c.name} returned EMPTY result object!`);
220
+ }
221
+ } else {
222
+ logger.log('WARN', `[MetaExecutor] ⚠️ ${c.name} returned non-object result: ${typeof result} - ${result}`);
223
+ }
224
+
225
+ // CRITICAL FIX: Do NOT overwrite this.results - the computation already sets it correctly
226
+ // The computation's process() method sets this.results, and getResult() returns it
227
+ // We only use the return value for logging/debugging
228
+ // inst.results should already be set by the computation's process() method
229
+ if (!inst.results) {
230
+ logger.log('WARN', `[MetaExecutor] ⚠️ ${c.name} did not set this.results - using return value as fallback`);
231
+ inst.results = result;
232
+ }
233
+
234
+ // DEBUG: Verify what getResult() will return
235
+ const finalResult = await inst.getResult();
236
+ if (finalResult && typeof finalResult === 'object') {
237
+ const finalKeys = Object.keys(finalResult);
238
+ logger.log('INFO', `[MetaExecutor] ✅ ${c.name} getResult() will return ${finalKeys.length} keys`);
239
+ } else {
240
+ logger.log('WARN', `[MetaExecutor] ⚠️ ${c.name} getResult() returns: ${typeof finalResult}`);
241
+ }
242
+
202
243
  state[c.name] = inst;
203
244
  } catch (e) {
204
245
  logger.log('ERROR', `Meta calc ${c.name} failed: ${e.message}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bulltrackers-module",
3
- "version": "1.0.647",
3
+ "version": "1.0.649",
4
4
  "description": "Helper Functions for Bulltrackers.",
5
5
  "main": "index.js",
6
6
  "files": [