bulltrackers-module 1.0.733 → 1.0.734

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 (56) hide show
  1. package/functions/computation-system-v2/README.md +152 -0
  2. package/functions/computation-system-v2/computations/PopularInvestorProfileMetrics.js +720 -0
  3. package/functions/computation-system-v2/computations/PopularInvestorRiskAssessment.js +176 -0
  4. package/functions/computation-system-v2/computations/PopularInvestorRiskMetrics.js +294 -0
  5. package/functions/computation-system-v2/computations/TestComputation.js +46 -0
  6. package/functions/computation-system-v2/computations/UserPortfolioSummary.js +172 -0
  7. package/functions/computation-system-v2/config/bulltrackers.config.js +317 -0
  8. package/functions/computation-system-v2/framework/core/Computation.js +73 -0
  9. package/functions/computation-system-v2/framework/core/Manifest.js +223 -0
  10. package/functions/computation-system-v2/framework/core/RuleInjector.js +53 -0
  11. package/functions/computation-system-v2/framework/core/Rules.js +231 -0
  12. package/functions/computation-system-v2/framework/core/RunAnalyzer.js +163 -0
  13. package/functions/computation-system-v2/framework/cost/CostTracker.js +154 -0
  14. package/functions/computation-system-v2/framework/data/DataFetcher.js +399 -0
  15. package/functions/computation-system-v2/framework/data/QueryBuilder.js +232 -0
  16. package/functions/computation-system-v2/framework/data/SchemaRegistry.js +287 -0
  17. package/functions/computation-system-v2/framework/execution/Orchestrator.js +498 -0
  18. package/functions/computation-system-v2/framework/execution/TaskRunner.js +35 -0
  19. package/functions/computation-system-v2/framework/execution/middleware/CostTrackerMiddleware.js +32 -0
  20. package/functions/computation-system-v2/framework/execution/middleware/LineageMiddleware.js +32 -0
  21. package/functions/computation-system-v2/framework/execution/middleware/Middleware.js +14 -0
  22. package/functions/computation-system-v2/framework/execution/middleware/ProfilerMiddleware.js +47 -0
  23. package/functions/computation-system-v2/framework/index.js +45 -0
  24. package/functions/computation-system-v2/framework/lineage/LineageTracker.js +147 -0
  25. package/functions/computation-system-v2/framework/monitoring/Profiler.js +80 -0
  26. package/functions/computation-system-v2/framework/resilience/Checkpointer.js +66 -0
  27. package/functions/computation-system-v2/framework/scheduling/ScheduleValidator.js +327 -0
  28. package/functions/computation-system-v2/framework/storage/StateRepository.js +286 -0
  29. package/functions/computation-system-v2/framework/storage/StorageManager.js +469 -0
  30. package/functions/computation-system-v2/framework/storage/index.js +9 -0
  31. package/functions/computation-system-v2/framework/testing/ComputationTester.js +86 -0
  32. package/functions/computation-system-v2/framework/utils/Graph.js +205 -0
  33. package/functions/computation-system-v2/handlers/dispatcher.js +109 -0
  34. package/functions/computation-system-v2/handlers/index.js +23 -0
  35. package/functions/computation-system-v2/handlers/onDemand.js +289 -0
  36. package/functions/computation-system-v2/handlers/scheduler.js +327 -0
  37. package/functions/computation-system-v2/index.js +163 -0
  38. package/functions/computation-system-v2/rules/index.js +49 -0
  39. package/functions/computation-system-v2/rules/instruments.js +465 -0
  40. package/functions/computation-system-v2/rules/metrics.js +304 -0
  41. package/functions/computation-system-v2/rules/portfolio.js +534 -0
  42. package/functions/computation-system-v2/rules/rankings.js +655 -0
  43. package/functions/computation-system-v2/rules/social.js +562 -0
  44. package/functions/computation-system-v2/rules/trades.js +545 -0
  45. package/functions/computation-system-v2/scripts/migrate-sectors.js +73 -0
  46. package/functions/computation-system-v2/test/test-dispatcher.js +317 -0
  47. package/functions/computation-system-v2/test/test-framework.js +500 -0
  48. package/functions/computation-system-v2/test/test-real-execution.js +166 -0
  49. package/functions/computation-system-v2/test/test-real-integration.js +194 -0
  50. package/functions/computation-system-v2/test/test-refactor-e2e.js +131 -0
  51. package/functions/computation-system-v2/test/test-results.json +31 -0
  52. package/functions/computation-system-v2/test/test-risk-metrics-computation.js +329 -0
  53. package/functions/computation-system-v2/test/test-scheduler.js +204 -0
  54. package/functions/computation-system-v2/test/test-storage.js +449 -0
  55. package/functions/orchestrator/index.js +18 -26
  56. package/package.json +3 -2
@@ -0,0 +1,287 @@
1
+ /**
2
+ * @fileoverview Schema Registry - Dynamic schema discovery with caching
3
+ *
4
+ * Core innovation of v2: No hardcoded schemas. Instead, we:
5
+ * 1. Fetch schemas from BigQuery INFORMATION_SCHEMA on first access
6
+ * 2. Cache them with configurable TTL
7
+ * 3. Validate all queries against cached schemas BEFORE sending to BigQuery
8
+ *
9
+ * This prevents runtime query failures and eliminates schema maintenance burden.
10
+ */
11
+
12
+ const { BigQuery } = require('@google-cloud/bigquery');
13
+
14
+ /**
15
+ * @typedef {Object} ColumnInfo
16
+ * @property {string} name - Column name
17
+ * @property {string} type - BigQuery data type (STRING, INT64, DATE, JSON, etc.)
18
+ * @property {boolean} nullable - Whether column allows NULL
19
+ * @property {number} position - Ordinal position in table
20
+ */
21
+
22
+ /**
23
+ * @typedef {Object} TableSchema
24
+ * @property {string} tableName - Table name
25
+ * @property {ColumnInfo[]} columns - Array of column definitions
26
+ * @property {Set<string>} columnNames - Set of column names for fast lookup
27
+ * @property {number} fetchedAt - Timestamp when schema was fetched
28
+ */
29
+
30
+ class SchemaRegistry {
31
+ /**
32
+ * @param {Object} config - Configuration object
33
+ * @param {string} config.projectId - GCP project ID
34
+ * @param {string} config.dataset - BigQuery dataset name
35
+ * @param {string} [config.location='US'] - BigQuery location
36
+ * @param {number} [config.cacheTTLMs=3600000] - Schema cache TTL in milliseconds (default: 1 hour)
37
+ * @param {Object} [logger] - Logger instance
38
+ */
39
+ constructor(config, logger = null) {
40
+ this.projectId = config.projectId;
41
+ this.dataset = config.dataset;
42
+ this.location = config.location || 'US';
43
+ this.cacheTTLMs = config.cacheTTLMs || 3600000; // 1 hour default
44
+ this.logger = logger;
45
+
46
+ this.client = new BigQuery({ projectId: this.projectId });
47
+ this.cache = new Map();
48
+
49
+ // Track schema fetch stats for monitoring
50
+ this.stats = {
51
+ hits: 0,
52
+ misses: 0,
53
+ refreshes: 0,
54
+ errors: 0
55
+ };
56
+ }
57
+
58
+ /**
59
+ * Get schema for a table, fetching from BigQuery if not cached.
60
+ * @param {string} tableName - Table name (without dataset prefix)
61
+ * @returns {Promise<TableSchema>}
62
+ */
63
+ async getSchema(tableName) {
64
+ const cached = this.cache.get(tableName);
65
+
66
+ if (cached && !this._isExpired(cached)) {
67
+ this.stats.hits++;
68
+ return cached;
69
+ }
70
+
71
+ if (cached) {
72
+ this.stats.refreshes++;
73
+ this._log('DEBUG', `Schema cache expired for ${tableName}, refreshing...`);
74
+ } else {
75
+ this.stats.misses++;
76
+ this._log('DEBUG', `Schema cache miss for ${tableName}, fetching...`);
77
+ }
78
+
79
+ return await this._fetchAndCacheSchema(tableName);
80
+ }
81
+
82
+ /**
83
+ * Check if a table exists in the dataset.
84
+ * @param {string} tableName - Table name
85
+ * @returns {Promise<boolean>}
86
+ */
87
+ async tableExists(tableName) {
88
+ try {
89
+ await this.getSchema(tableName);
90
+ return true;
91
+ } catch (e) {
92
+ if (e.message.includes('not found') || e.message.includes('Not found')) {
93
+ return false;
94
+ }
95
+ throw e;
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Validate that columns exist in a table's schema.
101
+ * Throws if any column doesn't exist.
102
+ * @param {string} tableName - Table name
103
+ * @param {string[]} columns - Column names to validate
104
+ * @throws {Error} If any column doesn't exist
105
+ */
106
+ async validateColumns(tableName, columns) {
107
+ const schema = await this.getSchema(tableName);
108
+ const invalid = columns.filter(col => !schema.columnNames.has(col));
109
+
110
+ if (invalid.length > 0) {
111
+ throw new Error(
112
+ `[SchemaRegistry] Invalid columns for table '${tableName}': ${invalid.join(', ')}. ` +
113
+ `Valid columns: ${Array.from(schema.columnNames).join(', ')}`
114
+ );
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Validate a WHERE clause object against table schema.
120
+ * @param {string} tableName - Table name
121
+ * @param {Object} whereClause - Object of { columnName: value } conditions
122
+ * @throws {Error} If any column in the where clause doesn't exist
123
+ */
124
+ async validateWhere(tableName, whereClause) {
125
+ if (!whereClause || typeof whereClause !== 'object') return;
126
+ await this.validateColumns(tableName, Object.keys(whereClause));
127
+ }
128
+
129
+ /**
130
+ * Get column type for a specific column.
131
+ * @param {string} tableName - Table name
132
+ * @param {string} columnName - Column name
133
+ * @returns {Promise<string|null>} Column type or null if not found
134
+ */
135
+ async getColumnType(tableName, columnName) {
136
+ const schema = await this.getSchema(tableName);
137
+ const column = schema.columns.find(c => c.name === columnName);
138
+ return column ? column.type : null;
139
+ }
140
+
141
+ /**
142
+ * Check if a table has a specific column.
143
+ * @param {string} tableName - Table name
144
+ * @param {string} columnName - Column name
145
+ * @returns {Promise<boolean>}
146
+ */
147
+ async hasColumn(tableName, columnName) {
148
+ const schema = await this.getSchema(tableName);
149
+ return schema.columnNames.has(columnName);
150
+ }
151
+
152
+ /**
153
+ * Pre-warm the cache by fetching schemas for specified tables.
154
+ * Useful at startup to avoid cold-start latency.
155
+ * @param {string[]} tableNames - Tables to pre-warm
156
+ * @returns {Promise<Object>} Summary of pre-warm results
157
+ */
158
+ async warmCache(tableNames) {
159
+ const results = { success: [], failed: [] };
160
+
161
+ await Promise.all(tableNames.map(async (tableName) => {
162
+ try {
163
+ await this.getSchema(tableName);
164
+ results.success.push(tableName);
165
+ } catch (e) {
166
+ results.failed.push({ table: tableName, error: e.message });
167
+ }
168
+ }));
169
+
170
+ this._log('INFO', `Schema cache warmed: ${results.success.length} success, ${results.failed.length} failed`);
171
+ return results;
172
+ }
173
+
174
+ /**
175
+ * Clear the schema cache (useful for testing or after schema changes).
176
+ * @param {string} [tableName] - Specific table to clear, or all if not specified
177
+ */
178
+ clearCache(tableName = null) {
179
+ if (tableName) {
180
+ this.cache.delete(tableName);
181
+ this._log('DEBUG', `Cleared schema cache for ${tableName}`);
182
+ } else {
183
+ this.cache.clear();
184
+ this._log('DEBUG', 'Cleared entire schema cache');
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Get cache statistics.
190
+ * @returns {Object} Cache statistics
191
+ */
192
+ getStats() {
193
+ return {
194
+ ...this.stats,
195
+ cachedTables: this.cache.size,
196
+ cacheContents: Array.from(this.cache.keys())
197
+ };
198
+ }
199
+
200
+ /**
201
+ * List all tables in the dataset.
202
+ * @returns {Promise<string[]>} Array of table names
203
+ */
204
+ async listTables() {
205
+ const query = `
206
+ SELECT table_name
207
+ FROM \`${this.projectId}.${this.dataset}.INFORMATION_SCHEMA.TABLES\`
208
+ WHERE table_type = 'BASE TABLE'
209
+ ORDER BY table_name
210
+ `;
211
+
212
+ try {
213
+ const [rows] = await this.client.query({ query, location: this.location });
214
+ return rows.map(r => r.table_name);
215
+ } catch (e) {
216
+ this._log('ERROR', `Failed to list tables: ${e.message}`);
217
+ throw e;
218
+ }
219
+ }
220
+
221
+ // =========================================================================
222
+ // PRIVATE METHODS
223
+ // =========================================================================
224
+
225
+ async _fetchAndCacheSchema(tableName) {
226
+ const query = `
227
+ SELECT
228
+ column_name,
229
+ data_type,
230
+ is_nullable,
231
+ ordinal_position
232
+ FROM \`${this.projectId}.${this.dataset}.INFORMATION_SCHEMA.COLUMNS\`
233
+ WHERE table_name = @tableName
234
+ ORDER BY ordinal_position
235
+ `;
236
+
237
+ try {
238
+ const [rows] = await this.client.query({
239
+ query,
240
+ params: { tableName },
241
+ location: this.location
242
+ });
243
+
244
+ if (rows.length === 0) {
245
+ throw new Error(`Table '${tableName}' not found in dataset '${this.dataset}'`);
246
+ }
247
+
248
+ const columns = rows.map(r => ({
249
+ name: r.column_name,
250
+ type: r.data_type,
251
+ nullable: r.is_nullable === 'YES',
252
+ position: r.ordinal_position
253
+ }));
254
+
255
+ const schema = {
256
+ tableName,
257
+ columns,
258
+ columnNames: new Set(columns.map(c => c.name)),
259
+ fetchedAt: Date.now()
260
+ };
261
+
262
+ this.cache.set(tableName, schema);
263
+ this._log('INFO', `Cached schema for '${tableName}': ${columns.length} columns`);
264
+
265
+ return schema;
266
+
267
+ } catch (e) {
268
+ this.stats.errors++;
269
+ this._log('ERROR', `Failed to fetch schema for '${tableName}': ${e.message}`);
270
+ throw e;
271
+ }
272
+ }
273
+
274
+ _isExpired(schema) {
275
+ return Date.now() - schema.fetchedAt > this.cacheTTLMs;
276
+ }
277
+
278
+ _log(level, message) {
279
+ if (this.logger && typeof this.logger.log === 'function') {
280
+ this.logger.log(level, `[SchemaRegistry] ${message}`);
281
+ } else if (level === 'ERROR') {
282
+ console.error(`[SchemaRegistry] ${message}`);
283
+ }
284
+ }
285
+ }
286
+
287
+ module.exports = { SchemaRegistry };