bulltrackers-module 1.0.105 → 1.0.106

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.
Files changed (33) hide show
  1. package/README.MD +222 -222
  2. package/functions/appscript-api/helpers/errors.js +19 -19
  3. package/functions/appscript-api/index.js +58 -58
  4. package/functions/computation-system/helpers/orchestration_helpers.js +647 -113
  5. package/functions/computation-system/utils/data_loader.js +191 -191
  6. package/functions/computation-system/utils/utils.js +149 -254
  7. package/functions/core/utils/firestore_utils.js +433 -433
  8. package/functions/core/utils/pubsub_utils.js +53 -53
  9. package/functions/dispatcher/helpers/dispatch_helpers.js +47 -47
  10. package/functions/dispatcher/index.js +52 -52
  11. package/functions/etoro-price-fetcher/helpers/handler_helpers.js +124 -124
  12. package/functions/fetch-insights/helpers/handler_helpers.js +91 -91
  13. package/functions/generic-api/helpers/api_helpers.js +379 -379
  14. package/functions/generic-api/index.js +150 -150
  15. package/functions/invalid-speculator-handler/helpers/handler_helpers.js +75 -75
  16. package/functions/orchestrator/helpers/discovery_helpers.js +226 -226
  17. package/functions/orchestrator/helpers/update_helpers.js +92 -92
  18. package/functions/orchestrator/index.js +147 -147
  19. package/functions/price-backfill/helpers/handler_helpers.js +116 -123
  20. package/functions/social-orchestrator/helpers/orchestrator_helpers.js +61 -61
  21. package/functions/social-task-handler/helpers/handler_helpers.js +288 -288
  22. package/functions/task-engine/handler_creator.js +78 -78
  23. package/functions/task-engine/helpers/discover_helpers.js +125 -125
  24. package/functions/task-engine/helpers/update_helpers.js +118 -118
  25. package/functions/task-engine/helpers/verify_helpers.js +162 -162
  26. package/functions/task-engine/utils/firestore_batch_manager.js +258 -258
  27. package/index.js +105 -113
  28. package/package.json +45 -45
  29. package/functions/computation-system/computation_dependencies.json +0 -120
  30. package/functions/computation-system/helpers/worker_helpers.js +0 -340
  31. package/functions/computation-system/utils/computation_state_manager.js +0 -178
  32. package/functions/computation-system/utils/dependency_graph.js +0 -191
  33. package/functions/speculator-cleanup-orchestrator/helpers/cleanup_helpers.js +0 -160
@@ -1,191 +0,0 @@
1
- /**
2
- * @fileoverview Builds a true Directed Acyclic Graph (DAG) for computations.
3
- *
4
- * This refactored utility ingests calculation classes and a dependency config.
5
- * It builds an adjacency list, performs a topological sort (Kahn's algorithm)
6
- * to detect cycles, and provides the graph structure to the orchestrator and worker.
7
- */
8
-
9
- /**
10
- * Represents a single calculation node in the graph.
11
- */
12
- class ComputationNode {
13
- constructor(category, name, calcClass) {
14
- this.category = category;
15
- this.name = name;
16
- this.calcClass = calcClass;
17
- this.children = new Set(); // Nodes that depend on this one
18
- this.parents = new Set(); // Nodes this one depends on
19
- this.requires = []; // <-- ADDED: Stores data requirements
20
- }
21
- }
22
-
23
- /**
24
- * Builds and validates the computation DAG.
25
- */
26
- class ComputationGraph {
27
- /**
28
- * @param {object} unifiedCalculations - The calculations object from 'aiden-shared-calculations-unified'.
29
- * @param {object} dependencyConfig - The JSON config object with dependency definitions.
30
- * @param {object} [logger=console] - A logger instance.
31
- */
32
- constructor(unifiedCalculations, dependencyConfig, logger = console) {
33
- this.logger = logger;
34
- this.nodes = new Map(); // Map<string, ComputationNode>
35
- this.sortedNodes = []; // Array of node names, in valid execution order
36
- this.isBuilt = false;
37
-
38
- // --- MODIFIED: Pass requirements config to _registerNodes ---
39
- this._registerNodes(unifiedCalculations, dependencyConfig.requirements || {});
40
- this._buildAdjacencyLists(dependencyConfig.dependencies || {});
41
- }
42
-
43
- /**
44
- * Pass 1: Register all calculations as nodes in the graph.
45
- * --- MODIFIED: Added requirementsConfig parameter ---
46
- */
47
- _registerNodes(unifiedCalculations, requirementsConfig) {
48
- this.logger.log('INFO', '[DepGraph] Pass 1: Registering all calculation nodes...');
49
- for (const category in unifiedCalculations) {
50
- if (category === 'utils') continue;
51
-
52
- for (const subKey in unifiedCalculations[category]) {
53
- const item = unifiedCalculations[category][subKey];
54
-
55
- // Handle nested directories (e.g., 'historical', 'meta', 'backtests')
56
- if (typeof item === 'object' && item !== null && !Array.isArray(item)) {
57
- for (const calcName in item) {
58
- if (typeof item[calcName] === 'function') {
59
- // --- MODIFIED: Create node, set requirements ---
60
- const node = new ComputationNode(category, calcName, item[calcName]);
61
- node.requires = requirementsConfig[calcName] || [];
62
- this.nodes.set(calcName, node);
63
- // --- END MODIFICATION ---
64
- }
65
- }
66
- }
67
- // Handle regular calcs at the root of the category
68
- else if (typeof item === 'function') {
69
- const calcName = subKey;
70
- // --- MODIFIED: Create node, set requirements ---
71
- const node = new ComputationNode(category, calcName, item[calcName]);
72
- node.requires = requirementsConfig[calcName] || [];
73
- this.nodes.set(calcName, node);
74
- // --- END MODIFICATION ---
75
- }
76
- }
77
- }
78
- this.logger.log('INFO', `[DepGraph] Pass 1: Registered ${this.nodes.size} total nodes.`);
79
- }
80
-
81
- /**
82
- * Pass 2: Build parent/child relationships from the dependency config.
83
- * (This function remains unchanged as it only reads the 'dependencies' key)
84
- */
85
- _buildAdjacencyLists(dependencies) {
86
- this.logger.log('INFO', '[DepGraph] Pass 2: Building adjacency lists...');
87
- for (const nodeName in dependencies) {
88
- const parentNames = dependencies[nodeName];
89
- const childNode = this.nodes.get(nodeName);
90
-
91
- if (!childNode) {
92
- this.logger.log('WARN', `[DepGraph] Dependency config lists node "${nodeName}", but it was not found in registered calculations. Skipping.`);
93
- continue;
94
- }
95
-
96
- for (const parentName of parentNames) {
97
- const parentNode = this.nodes.get(parentName);
98
- if (!parentNode) {
99
- this.logger.log('WARN', `[DepGraph] Node "${nodeName}" lists dependency "${parentName}", but it was not found. Skipping dependency.`);
100
- continue;
101
- }
102
-
103
- // Create the links
104
- childNode.parents.add(parentName);
105
- parentNode.children.add(nodeName);
106
- }
107
- }
108
- this.logger.log('INFO', '[DepGraph] Pass 2: Adjacency lists built.');
109
- }
110
-
111
- /**
112
- * Pass 3: Perform Topological Sort (Kahn's Algorithm) to find execution order
113
- * and detect cycles. This is the "build" step.
114
- * (This function remains unchanged)
115
- */
116
- build() {
117
- if (this.isBuilt) return;
118
- this.logger.log('INFO', '[DepGraph] Pass 3: Running topological sort (Kahn\'s Algorithm)...');
119
-
120
- const inDegree = new Map(); // Map<string, number>
121
- const queue = []; // Queue of node names
122
-
123
- // Initialize in-degree (parent count) for all nodes
124
- for (const [name, node] of this.nodes.entries()) {
125
- inDegree.set(name, node.parents.size);
126
- if (node.parents.size === 0) {
127
- queue.push(name);
128
- }
129
- }
130
-
131
- while (queue.length > 0) {
132
- const nodeName = queue.shift();
133
- this.sortedNodes.push(nodeName);
134
-
135
- const node = this.nodes.get(nodeName);
136
- if (!node) continue;
137
-
138
- for (const childName of node.children) {
139
- const newDegree = inDegree.get(childName) - 1;
140
- inDegree.set(childName, newDegree);
141
- if (newDegree === 0) {
142
- queue.push(childName);
143
- }
144
- }
145
- }
146
-
147
- // Check for cycles
148
- if (this.sortedNodes.length < this.nodes.size) {
149
- const missing = [...this.nodes.keys()].filter(n => !this.sortedNodes.includes(n));
150
- this.logger.log('ERROR', `[DepGraph] CRITICAL: Cycle detected! ${missing.length} nodes could not be sorted.`, {
151
- unprocessedNodes: missing
152
- });
153
- throw new Error(`Cycle detected in computation graph. Unprocessed nodes: ${missing.join(', ')}`);
154
- }
155
-
156
- this.logger.log('SUCCESS', `[DepGraph] Pass 3: Topological sort complete. Valid DAG confirmed.`);
157
- this.isBuilt = true;
158
- }
159
-
160
- /**
161
- * Returns all nodes that have no dependencies (graph roots).
162
- * @returns {ComputationNode[]}
163
- */
164
- getRootNodes() {
165
- if (!this.isBuilt) this.build();
166
- return this.sortedNodes
167
- .map(name => this.nodes.get(name))
168
- .filter(node => node.parents.size === 0);
169
- }
170
-
171
- /**
172
- * Returns a node by its name.
173
- * @param {string} nodeName
174
- * @returns {ComputationNode | undefined}
175
- */
176
- getNode(nodeName) {
177
- return this.nodes.get(nodeName);
178
- }
179
-
180
- /**
181
- * Returns all registered node objects.
182
- * @returns {ComputationNode[]}
183
- */
184
- getAllNodes() {
185
- return Array.from(this.nodes.values());
186
- }
187
- }
188
-
189
- module.exports = {
190
- ComputationGraph
191
- };
@@ -1,160 +0,0 @@
1
- /**
2
- * @fileoverview Main pipe: pipe.maintenance.runSpeculatorCleanup
3
- * REFACTORED: Now stateless and receives dependencies.
4
- */
5
-
6
- const { FieldValue } = require('@google-cloud/firestore');
7
-
8
- /**
9
- * Main pipe: pipe.maintenance.runSpeculatorCleanup
10
- * Orchestrates the cleanup process.
11
- * @param {object} config - Configuration object.
12
- * @param {object} dependencies - Contains db, logger.
13
- */
14
- exports.runCleanup = async (config, dependencies) => {
15
- const { logger } = dependencies;
16
- logger.log('INFO', '[CleanupHelpers] Running cleanup orchestrator...');
17
-
18
- try {
19
- // Start cleanup for pending users
20
- const { batch: batchAfterPending, count: pendingRemoved } = await cleanupPendingSpeculators(config, dependencies);
21
-
22
- // Continue with the same batch for stale speculators
23
- const { batch: finalBatch, count: staleRemoved } = await cleanupStaleSpeculators(config, dependencies, batchAfterPending);
24
-
25
- if (pendingRemoved > 0 || staleRemoved > 0) {
26
- await finalBatch.commit();
27
- logger.log('SUCCESS', `[CleanupHelpers] Cleanup commit successful. Removed ${pendingRemoved} pending, ${staleRemoved} stale speculators.`);
28
- return { pendingRemoved, staleRemoved };
29
- } else {
30
- logger.log('SUCCESS', '[CleanupHelpers] No stale users found in pending or blocks.');
31
- return { pendingRemoved: 0, staleRemoved: 0 };
32
- }
33
- } catch (error) {
34
- logger.log('ERROR', '[CleanupHelpers] FATAL error during cleanup orchestration', { errorMessage: error.message, errorStack: error.stack });
35
- throw error;
36
- }
37
- };
38
-
39
- /**
40
- * Internal sub-pipe for cleaning pending speculators.
41
- */
42
- async function cleanupPendingSpeculators(config, dependencies) {
43
- const { db, logger } = dependencies;
44
- logger.log('INFO', '[CleanupHelpers] Starting pending speculator cleanup...');
45
-
46
- const batch = db.batch(); // Use db from dependencies
47
- let stalePendingUsersRemoved = 0;
48
- const pendingCollectionRef = db.collection(config.pendingSpeculatorsCollectionName); // Use db
49
- const staleThreshold = new Date();
50
- staleThreshold.setHours(staleThreshold.getHours() - config.pendingGracePeriodHours);
51
-
52
- try {
53
- const pendingSnapshot = await pendingCollectionRef.get();
54
- if (pendingSnapshot.empty) {
55
- logger.log('INFO', '[CleanupHelpers] Pending speculators collection is empty.');
56
- return { batch, count: 0 };
57
- }
58
-
59
- for (const doc of pendingSnapshot.docs) {
60
- const pendingData = doc.data().users || {};
61
- const updates = {};
62
- let updatesInDoc = 0;
63
-
64
- for (const userId in pendingData) {
65
- const timestamp = pendingData[userId]?.toDate ? pendingData[userId].toDate() : null;
66
- if (timestamp && timestamp < staleThreshold) {
67
- updates[`users.${userId}`] = FieldValue.delete();
68
- stalePendingUsersRemoved++;
69
- updatesInDoc++;
70
- }
71
- }
72
-
73
- if (updatesInDoc > 0) {
74
- logger.log('TRACE', `[CleanupHelpers] Marking ${updatesInDoc} users for removal from pending doc ${doc.id}`);
75
- batch.update(doc.ref, updates);
76
- }
77
- }
78
- logger.log('INFO', `[CleanupHelpers] Marked ${stalePendingUsersRemoved} total stale pending users for removal.`);
79
- } catch (error) {
80
- logger.log('ERROR', '[CleanupHelpers] Error cleaning pending speculators', { errorMessage: error.message });
81
- throw error;
82
- }
83
- return { batch, count: stalePendingUsersRemoved };
84
- }
85
-
86
- /**
87
- * Internal sub-pipe for cleaning stale speculators.
88
- */
89
- async function cleanupStaleSpeculators(config, dependencies, batch) {
90
- const { db, logger } = dependencies;
91
- logger.log('INFO', '[CleanupHelpers] Starting stale speculator cleanup from blocks...');
92
- let totalUsersRemoved = 0;
93
- const blocksCollectionRef = db.collection(config.speculatorBlocksCollectionName); // Use db
94
- const gracePeriodDate = new Date();
95
- gracePeriodDate.setDate(gracePeriodDate.getDate() - config.activityGracePeriodDays);
96
- const blockCountsUpdate = {};
97
-
98
- try {
99
- const blocksSnapshot = await blocksCollectionRef.get();
100
- if (blocksSnapshot.empty) {
101
- logger.log('INFO', '[CleanupHelpers] Speculator blocks collection is empty.');
102
- return { batch, count: 0 };
103
- }
104
-
105
- for (const doc of blocksSnapshot.docs) {
106
- const blockId = doc.id;
107
- const blockData = doc.data();
108
- const users = blockData.users || {};
109
- let usersRemovedFromBlock = 0;
110
- const updates = {};
111
-
112
- for (const userKey in users) {
113
- const userId = userKey.split('.')[1];
114
- if (!userId) continue;
115
-
116
- const userData = users[userKey];
117
- const lastHeldTimestamp = userData.lastHeldSpeculatorAsset?.toDate ? userData.lastHeldSpeculatorAsset.toDate() : null;
118
-
119
- if (lastHeldTimestamp && lastHeldTimestamp < gracePeriodDate) {
120
- updates[userKey] = FieldValue.delete();
121
- usersRemovedFromBlock++;
122
-
123
- if (userData.instruments && Array.isArray(userData.instruments)) {
124
- userData.instruments.forEach(instrumentId => {
125
- const instrumentBlockKey = `${instrumentId}_${blockId}`;
126
- if (!blockCountsUpdate[instrumentBlockKey]) {
127
- blockCountsUpdate[instrumentBlockKey] = 0;
128
- }
129
- blockCountsUpdate[instrumentBlockKey]--;
130
- });
131
- }
132
- }
133
- }
134
-
135
- if (usersRemovedFromBlock > 0) {
136
- logger.log('TRACE', `[CleanupHelpers] Marking ${usersRemovedFromBlock} users for removal from block ${blockId}.`);
137
- batch.update(doc.ref, updates);
138
- totalUsersRemoved += usersRemovedFromBlock;
139
- }
140
- }
141
-
142
- if (totalUsersRemoved > 0 && Object.keys(blockCountsUpdate).length > 0) {
143
- const countsRef = db.doc(config.speculatorBlockCountsDocPath); // Use db
144
- const finalCountUpdates = {};
145
- for (const key in blockCountsUpdate) {
146
- finalCountUpdates[`counts.${key}`] = FieldValue.increment(blockCountsUpdate[key]);
147
- }
148
- logger.log('TRACE', '[CleanupHelpers] Staging block count decrements.', { updates: finalCountUpdates });
149
- batch.set(countsRef, finalCountUpdates, { merge: true });
150
- }
151
-
152
- logger.log('INFO', `[CleanupHelpers] Marked ${totalUsersRemoved} total stale speculators for removal from blocks.`);
153
-
154
- } catch (error) {
155
- logger.log('ERROR', '[CleanupHelpers] Error cleaning stale speculators', { errorMessage: error.message });
156
- throw error;
157
- }
158
-
159
- return { batch, count: totalUsersRemoved };
160
- }