bulltrackers-module 1.0.768 → 1.0.769

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 (51) hide show
  1. package/functions/computation-system-v2/UserPortfolioMetrics.js +50 -0
  2. package/functions/computation-system-v2/computations/BehavioralAnomaly.js +557 -337
  3. package/functions/computation-system-v2/computations/GlobalAumPerAsset30D.js +103 -0
  4. package/functions/computation-system-v2/computations/PIDailyAssetAUM.js +134 -0
  5. package/functions/computation-system-v2/computations/PiFeatureVectors.js +227 -0
  6. package/functions/computation-system-v2/computations/PiRecommender.js +359 -0
  7. package/functions/computation-system-v2/computations/SignedInUserList.js +51 -0
  8. package/functions/computation-system-v2/computations/SignedInUserMirrorHistory.js +138 -0
  9. package/functions/computation-system-v2/computations/SignedInUserPIProfileMetrics.js +106 -0
  10. package/functions/computation-system-v2/computations/SignedInUserProfileMetrics.js +324 -0
  11. package/functions/computation-system-v2/config/bulltrackers.config.js +30 -128
  12. package/functions/computation-system-v2/core-api.js +17 -9
  13. package/functions/computation-system-v2/data_schema_reference.MD +108 -0
  14. package/functions/computation-system-v2/devtools/builder/builder.js +362 -0
  15. package/functions/computation-system-v2/devtools/builder/examples/user-metrics.yaml +26 -0
  16. package/functions/computation-system-v2/devtools/index.js +36 -0
  17. package/functions/computation-system-v2/devtools/shared/MockDataFactory.js +235 -0
  18. package/functions/computation-system-v2/devtools/shared/SchemaTemplates.js +475 -0
  19. package/functions/computation-system-v2/devtools/shared/SystemIntrospector.js +517 -0
  20. package/functions/computation-system-v2/devtools/shared/index.js +16 -0
  21. package/functions/computation-system-v2/devtools/simulation/DAGAnalyzer.js +243 -0
  22. package/functions/computation-system-v2/devtools/simulation/MockDataFetcher.js +306 -0
  23. package/functions/computation-system-v2/devtools/simulation/MockStorageManager.js +336 -0
  24. package/functions/computation-system-v2/devtools/simulation/SimulationEngine.js +525 -0
  25. package/functions/computation-system-v2/devtools/simulation/SimulationServer.js +581 -0
  26. package/functions/computation-system-v2/devtools/simulation/index.js +17 -0
  27. package/functions/computation-system-v2/devtools/simulation/simulate.js +324 -0
  28. package/functions/computation-system-v2/devtools/vscode-computation/package.json +90 -0
  29. package/functions/computation-system-v2/devtools/vscode-computation/snippets/computation.json +128 -0
  30. package/functions/computation-system-v2/devtools/vscode-computation/src/extension.ts +401 -0
  31. package/functions/computation-system-v2/devtools/vscode-computation/src/providers/codeActions.ts +152 -0
  32. package/functions/computation-system-v2/devtools/vscode-computation/src/providers/completions.ts +207 -0
  33. package/functions/computation-system-v2/devtools/vscode-computation/src/providers/diagnostics.ts +205 -0
  34. package/functions/computation-system-v2/devtools/vscode-computation/src/providers/hover.ts +205 -0
  35. package/functions/computation-system-v2/devtools/vscode-computation/tsconfig.json +22 -0
  36. package/functions/computation-system-v2/docs/HowToCreateComputations.MD +602 -0
  37. package/functions/computation-system-v2/framework/data/DataFetcher.js +250 -184
  38. package/functions/computation-system-v2/framework/data/MaterializedViewManager.js +84 -0
  39. package/functions/computation-system-v2/framework/data/QueryBuilder.js +38 -38
  40. package/functions/computation-system-v2/framework/execution/Orchestrator.js +215 -129
  41. package/functions/computation-system-v2/framework/scheduling/ScheduleValidator.js +17 -19
  42. package/functions/computation-system-v2/framework/storage/StateRepository.js +32 -2
  43. package/functions/computation-system-v2/framework/storage/StorageManager.js +105 -67
  44. package/functions/computation-system-v2/framework/testing/ComputationTester.js +12 -6
  45. package/functions/computation-system-v2/handlers/dispatcher.js +57 -29
  46. package/functions/computation-system-v2/legacy/PiAssetRecommender.js.old +115 -0
  47. package/functions/computation-system-v2/legacy/PiSimilarityMatrix.js +104 -0
  48. package/functions/computation-system-v2/legacy/PiSimilarityVector.js +71 -0
  49. package/functions/computation-system-v2/scripts/debug_aggregation.js +25 -0
  50. package/functions/computation-system-v2/scripts/test-invalidation-scenarios.js +234 -0
  51. package/package.json +1 -1
@@ -0,0 +1,84 @@
1
+ /**
2
+ * @fileoverview Materialized View Manager
3
+ * * V2.1 UPDATE: Added verbose logging for long-running MV creation operations.
4
+ */
5
+ class MaterializedViewManager {
6
+ constructor(bigqueryClient, logger, config) {
7
+ this.bq = bigqueryClient;
8
+ this.logger = logger || console;
9
+ this.config = config;
10
+ this.checkedViews = new Set();
11
+ }
12
+
13
+ async ensureMetricView(baseTable, spec) {
14
+ const suffix = spec.jsonPath ? `_${this._hash(spec.jsonPath)}` : '';
15
+ const viewName = `mv_${baseTable}_${spec.func}_${spec.field}${suffix}_by_day`.toLowerCase();
16
+
17
+ if (this.checkedViews.has(viewName)) return viewName;
18
+
19
+ const dataset = this.bq.dataset(this.config.dataset);
20
+ const table = dataset.table(viewName);
21
+
22
+ try {
23
+ const [exists] = await table.exists();
24
+ if (exists) {
25
+ this.checkedViews.add(viewName);
26
+ return viewName;
27
+ }
28
+ } catch (e) {
29
+ // Ignore 404s
30
+ }
31
+
32
+ await this._createView(dataset, viewName, baseTable, spec);
33
+ this.checkedViews.add(viewName);
34
+ return viewName;
35
+ }
36
+
37
+ async _createView(dataset, viewName, baseTable, spec) {
38
+ // LOGGING ADDED HERE
39
+ this.logger.log('INFO', `[MV-Manager] ⏳ Creating Materialized View: ${viewName}...`);
40
+ this.logger.log('INFO', `[MV-Manager] ⚠️ This triggers a historical backfill and may take 30-60 seconds. Please wait.`);
41
+
42
+ const { field, func, entityField, dateField, jsonPath } = spec;
43
+
44
+ let valueExpression = field;
45
+ if (jsonPath) {
46
+ valueExpression = `CAST(JSON_VALUE(${field}, '${jsonPath}') AS FLOAT64)`;
47
+ }
48
+
49
+ const query = `
50
+ CREATE MATERIALIZED VIEW \`${dataset.id}.${viewName}\`
51
+ OPTIONS (enable_refresh = true, refresh_interval_minutes = 60)
52
+ AS SELECT
53
+ ${entityField} as entity_id,
54
+ ${dateField} as date,
55
+ ${func}(${valueExpression}) as value,
56
+ COUNT(*) as count
57
+ FROM \`${dataset.id}.${baseTable}\`
58
+ GROUP BY 1, 2
59
+ `;
60
+
61
+ try {
62
+ await dataset.query({ query });
63
+ this.logger.log('INFO', `[MV-Manager] ✅ Successfully created ${viewName}. Backfill running in background.`);
64
+ } catch (e) {
65
+ if (e.message.includes('Already Exists')) {
66
+ this.logger.log('INFO', `[MV-Manager] View ${viewName} was created concurrently.`);
67
+ return;
68
+ }
69
+ throw new Error(`Failed to create Materialized View ${viewName}: ${e.message}`);
70
+ }
71
+ }
72
+
73
+ _hash(str) {
74
+ let hash = 0;
75
+ for (let i = 0; i < str.length; i++) {
76
+ const char = str.charCodeAt(i);
77
+ hash = (hash << 5) - hash + char;
78
+ hash |= 0;
79
+ }
80
+ return Math.abs(hash).toString(16);
81
+ }
82
+ }
83
+
84
+ module.exports = { MaterializedViewManager };
@@ -1,44 +1,20 @@
1
1
  /**
2
2
  * @fileoverview Query Builder - Builds validated SQL queries
3
- *
4
- * All queries are validated against the SchemaRegistry BEFORE execution.
3
+ * * All queries are validated against the SchemaRegistry BEFORE execution.
5
4
  * This catches errors early and prevents runtime failures.
6
- */
7
-
8
- /**
9
- * @typedef {Object} QuerySpec
10
- * @property {string} table - Table name
11
- * @property {string[]} [select] - Columns to select (null = *)
12
- * @property {Object} [where] - WHERE conditions as { column: value }
13
- * @property {string} [dateField] - Date field name for date filtering
14
- * @property {string} [targetDate] - Target date (YYYY-MM-DD)
15
- * @property {number} [lookback=0] - Days to look back (0 = target date only)
16
- * @property {string} [entityField] - Entity field name for entity filtering
17
- * @property {string[]} [entities] - Entity IDs to filter
18
- * @property {string} [orderBy] - Column to order by
19
- * @property {string} [orderDir='DESC'] - Order direction
20
- * @property {number} [limit] - Limit number of rows
21
- */
22
-
23
- /**
24
- * @typedef {Object} BuiltQuery
25
- * @property {string} sql - The SQL query string
26
- * @property {Object} params - Query parameters
27
- * @property {string} table - Table that was queried
28
- * @property {Object} spec - Original query spec
5
+ * * V2.0 UPDATE: Safety features (Ban SELECT * and enforce partitions).
29
6
  */
30
7
 
31
8
  class QueryBuilder {
32
9
  /**
33
10
  * @param {Object} config - Configuration object
34
- * @param {string} config.projectId - GCP project ID
35
- * @param {string} config.dataset - BigQuery dataset name
36
11
  * @param {SchemaRegistry} schemaRegistry - Schema registry instance
37
12
  * @param {Object} [logger] - Logger instance
38
13
  */
39
14
  constructor(config, schemaRegistry, logger = null) {
40
15
  this.projectId = config.projectId;
41
16
  this.dataset = config.dataset;
17
+ this.tables = config.tables || {}; // Store table config for validation
42
18
  this.schemaRegistry = schemaRegistry;
43
19
  this.logger = logger;
44
20
  }
@@ -47,7 +23,7 @@ class QueryBuilder {
47
23
  * Build a validated query.
48
24
  * @param {QuerySpec} spec - Query specification
49
25
  * @returns {Promise<BuiltQuery>}
50
- * @throws {Error} If validation fails
26
+ * @throws {Error} If validation fails or safety checks trigger
51
27
  */
52
28
  async build(spec) {
53
29
  const { table } = spec;
@@ -56,6 +32,27 @@ class QueryBuilder {
56
32
  throw new Error('[QueryBuilder] Table name is required');
57
33
  }
58
34
 
35
+ // --- 1. PARTITION SAFETY CHECK ---
36
+ // If the table is configured with a Date Field (Partition),
37
+ // we MUST see a targetDate or a custom where-clause using that field.
38
+ // Otherwise, it's a guaranteed full-table scan over all history.
39
+ const tableConfig = this.tables[table];
40
+
41
+ if (tableConfig && tableConfig.dateField) {
42
+ const hasTargetDate = !!spec.targetDate;
43
+ // Check if 'where' clause contains the date field key
44
+ const hasCustomDateFilter = spec.where && Object.keys(spec.where).includes(tableConfig.dateField);
45
+
46
+ if (!hasTargetDate && !hasCustomDateFilter) {
47
+ throw new Error(
48
+ `[QueryBuilder] 🛑 UNSAFE QUERY BLOCKED: Table '${table}' is partitioned by '${tableConfig.dateField}', ` +
49
+ `but no date filter was provided. This would trigger a full table scan. ` +
50
+ `Please provide a 'lookback' or explicit filter in your computation config.`
51
+ );
52
+ }
53
+ }
54
+ // ---------------------------------
55
+
59
56
  // Validate table exists and get schema
60
57
  const schema = await this.schemaRegistry.getSchema(table);
61
58
 
@@ -99,13 +96,8 @@ class QueryBuilder {
99
96
 
100
97
  /**
101
98
  * Build a simple existence check query.
102
- * @param {string} table - Table name
103
- * @param {string} dateField - Date field name
104
- * @param {string} targetDate - Target date
105
- * @returns {Promise<BuiltQuery>}
106
99
  */
107
100
  async buildExistsQuery(table, dateField, targetDate) {
108
- // Validate table and date field
109
101
  await this.schemaRegistry.getSchema(table);
110
102
  if (dateField) {
111
103
  await this.schemaRegistry.validateColumns(table, [dateField]);
@@ -148,10 +140,18 @@ class QueryBuilder {
148
140
  const params = {};
149
141
  const conditions = [];
150
142
 
151
- // SELECT clause
152
- const selectClause = select && select.length > 0
153
- ? select.join(', ')
154
- : '*';
143
+ // --- 2. SELECT * SAFETY CHECK ---
144
+ // BigQuery charges by column. SELECT * on a 100-col table costs 100x.
145
+ // We force developers to be specific.
146
+ if (!select || select.length === 0 || select.includes('*')) {
147
+ throw new Error(
148
+ `[QueryBuilder] 🛑 LAZY SELECT BLOCKED: 'SELECT *' or empty select is forbidden to save costs. ` +
149
+ `Table: ${table}. Please list the specific columns you need in the 'fields' array of your computation config.`
150
+ );
151
+ }
152
+
153
+ const selectClause = select.join(', ');
154
+ // --------------------------------
155
155
 
156
156
  // Date filtering
157
157
  if (dateField && targetDate) {
@@ -229,4 +229,4 @@ class QueryBuilder {
229
229
  }
230
230
  }
231
231
 
232
- module.exports = { QueryBuilder };
232
+ module.exports = { QueryBuilder };