bulltrackers-module 1.0.768 → 1.0.770

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 (52) 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/RiskScoreIncrease.js +13 -13
  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/handlers/scheduler.js +172 -203
  47. package/functions/computation-system-v2/legacy/PiAssetRecommender.js.old +115 -0
  48. package/functions/computation-system-v2/legacy/PiSimilarityMatrix.js +104 -0
  49. package/functions/computation-system-v2/legacy/PiSimilarityVector.js +71 -0
  50. package/functions/computation-system-v2/scripts/debug_aggregation.js +25 -0
  51. package/functions/computation-system-v2/scripts/test-invalidation-scenarios.js +234 -0
  52. package/package.json +1 -1
@@ -0,0 +1,517 @@
1
+ /**
2
+ * @fileoverview System Introspector
3
+ *
4
+ * Central intelligence layer for the developer tooling platform.
5
+ * Extracts and exposes knowledge about the computation system:
6
+ * - Available tables and their schemas
7
+ * - Business rule modules and functions
8
+ * - System constraints (lookback limits, valid types)
9
+ * - Computation dependency graph
10
+ *
11
+ * Used by: VS Code Extension, Simulation Engine, Builder
12
+ */
13
+
14
+ const path = require('path');
15
+ const fs = require('fs');
16
+
17
+ class SystemIntrospector {
18
+ /**
19
+ * @param {Object} config - The bulltrackers.config.js exports
20
+ * @param {Object} [options] - Optional overrides
21
+ * @param {Object} [options.schemaRegistry] - SchemaRegistry instance for live column data
22
+ * @param {Object} [options.rulesRegistry] - RulesRegistry instance for rule metadata
23
+ */
24
+ constructor(config, options = {}) {
25
+ this.config = config;
26
+ this.schemaRegistry = options.schemaRegistry || null;
27
+ this.rulesRegistry = options.rulesRegistry || null;
28
+
29
+ // Cache for computed values
30
+ this._cache = {
31
+ computationConfigs: null,
32
+ dependencyGraph: null,
33
+ passLevels: null,
34
+ ruleIndex: null
35
+ };
36
+
37
+ // System constraints
38
+ this.constraints = {
39
+ maxLookback: 90,
40
+ validTypes: ['global', 'per-entity'],
41
+ validCategories: ['popular_investor', 'signed_in_user', 'global', 'instrument']
42
+ };
43
+ }
44
+
45
+ // =========================================================================
46
+ // TABLE INTROSPECTION
47
+ // =========================================================================
48
+
49
+ /**
50
+ * Get list of all configured table names.
51
+ * @returns {string[]}
52
+ */
53
+ getAvailableTables() {
54
+ return Object.keys(this.config.tables || {});
55
+ }
56
+
57
+ /**
58
+ * Get metadata for a specific table.
59
+ * @param {string} tableName
60
+ * @returns {Object|null} { dateField, entityField, clusterFields, description }
61
+ */
62
+ getTableMetadata(tableName) {
63
+ const table = this.config.tables?.[tableName];
64
+ if (!table) return null;
65
+
66
+ return {
67
+ tableName: table.tableName || tableName,
68
+ dateField: table.dateField || null,
69
+ entityField: table.entityField || null,
70
+ clusterFields: table.clusterFields || [],
71
+ description: table.description || null,
72
+ isPartitioned: !!table.dateField
73
+ };
74
+ }
75
+
76
+ /**
77
+ * Check if a table exists in configuration.
78
+ * @param {string} tableName
79
+ * @returns {boolean}
80
+ */
81
+ tableExists(tableName) {
82
+ return tableName in (this.config.tables || {});
83
+ }
84
+
85
+ /**
86
+ * Get table columns. Uses SchemaRegistry if available, otherwise returns null.
87
+ * @param {string} tableName
88
+ * @returns {Promise<string[]|null>}
89
+ */
90
+ async getTableColumns(tableName) {
91
+ if (!this.schemaRegistry) return null;
92
+
93
+ try {
94
+ const schema = await this.schemaRegistry.getSchema(tableName);
95
+ return schema?.columns?.map(c => c.name) || null;
96
+ } catch (e) {
97
+ return null;
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Validate that a column exists in a table.
103
+ * @param {string} tableName
104
+ * @param {string} columnName
105
+ * @returns {Promise<boolean>}
106
+ */
107
+ async columnExists(tableName, columnName) {
108
+ const columns = await this.getTableColumns(tableName);
109
+ if (!columns) return true; // Can't validate without schema, assume valid
110
+ return columns.includes(columnName);
111
+ }
112
+
113
+ // =========================================================================
114
+ // RULE INTROSPECTION
115
+ // =========================================================================
116
+
117
+ /**
118
+ * Get list of all rule module names.
119
+ * @returns {string[]}
120
+ */
121
+ getRuleModules() {
122
+ const rules = this.config.rules || {};
123
+ return Object.keys(rules);
124
+ }
125
+
126
+ /**
127
+ * Get list of functions exported by a rule module.
128
+ * @param {string} moduleName
129
+ * @returns {string[]}
130
+ */
131
+ getRuleFunctions(moduleName) {
132
+ const module = this.config.rules?.[moduleName];
133
+ if (!module) return [];
134
+
135
+ return Object.keys(module).filter(key => typeof module[key] === 'function');
136
+ }
137
+
138
+ /**
139
+ * Check if a rule function exists.
140
+ * @param {string} moduleName
141
+ * @param {string} functionName
142
+ * @returns {boolean}
143
+ */
144
+ ruleFunctionExists(moduleName, functionName) {
145
+ const module = this.config.rules?.[moduleName];
146
+ if (!module) return false;
147
+ return typeof module[functionName] === 'function';
148
+ }
149
+
150
+ /**
151
+ * Get all rule functions as a flat index.
152
+ * @returns {Map<string, { module: string, path: string }>}
153
+ */
154
+ getRuleIndex() {
155
+ if (this._cache.ruleIndex) return this._cache.ruleIndex;
156
+
157
+ const index = new Map();
158
+ for (const moduleName of this.getRuleModules()) {
159
+ for (const funcName of this.getRuleFunctions(moduleName)) {
160
+ index.set(funcName, {
161
+ module: moduleName,
162
+ path: `rules.${moduleName}.${funcName}`
163
+ });
164
+ }
165
+ }
166
+
167
+ this._cache.ruleIndex = index;
168
+ return index;
169
+ }
170
+
171
+ /**
172
+ * Find the closest matching rule function (for "did you mean" suggestions).
173
+ * @param {string} moduleName
174
+ * @param {string} attemptedName
175
+ * @returns {string|null}
176
+ */
177
+ findClosestRuleFunction(moduleName, attemptedName) {
178
+ const functions = this.getRuleFunctions(moduleName);
179
+ if (functions.length === 0) return null;
180
+
181
+ // Simple Levenshtein-based matching
182
+ let closest = null;
183
+ let minDistance = Infinity;
184
+
185
+ for (const fn of functions) {
186
+ const distance = this._levenshtein(attemptedName.toLowerCase(), fn.toLowerCase());
187
+ if (distance < minDistance && distance <= 3) {
188
+ minDistance = distance;
189
+ closest = fn;
190
+ }
191
+ }
192
+
193
+ return closest;
194
+ }
195
+
196
+ /**
197
+ * Find the closest matching table name.
198
+ * @param {string} attemptedName
199
+ * @returns {string|null}
200
+ */
201
+ findClosestTable(attemptedName) {
202
+ const tables = this.getAvailableTables();
203
+ let closest = null;
204
+ let minDistance = Infinity;
205
+
206
+ for (const table of tables) {
207
+ const distance = this._levenshtein(attemptedName.toLowerCase(), table.toLowerCase());
208
+ if (distance < minDistance && distance <= 5) {
209
+ minDistance = distance;
210
+ closest = table;
211
+ }
212
+ }
213
+
214
+ return closest;
215
+ }
216
+
217
+ // =========================================================================
218
+ // CONSTRAINT INTROSPECTION
219
+ // =========================================================================
220
+
221
+ /**
222
+ * Get the maximum allowed lookback value.
223
+ * @returns {number}
224
+ */
225
+ getMaxLookback() {
226
+ return this.constraints.maxLookback;
227
+ }
228
+
229
+ /**
230
+ * Get valid computation types.
231
+ * @returns {string[]}
232
+ */
233
+ getValidTypes() {
234
+ return [...this.constraints.validTypes];
235
+ }
236
+
237
+ /**
238
+ * Get valid computation categories.
239
+ * @returns {string[]}
240
+ */
241
+ getValidCategories() {
242
+ // Extract from existing computations + defaults
243
+ const categories = new Set(this.constraints.validCategories);
244
+
245
+ for (const comp of this.config.computations || []) {
246
+ try {
247
+ const cfg = comp.getConfig();
248
+ if (cfg.category) categories.add(cfg.category);
249
+ } catch (e) { /* skip */ }
250
+ }
251
+
252
+ return Array.from(categories);
253
+ }
254
+
255
+ // =========================================================================
256
+ // COMPUTATION INTROSPECTION
257
+ // =========================================================================
258
+
259
+ /**
260
+ * Get list of all computation names.
261
+ * @returns {string[]}
262
+ */
263
+ getComputationNames() {
264
+ const names = [];
265
+ for (const comp of this.config.computations || []) {
266
+ try {
267
+ names.push(comp.getConfig().name);
268
+ } catch (e) { /* skip */ }
269
+ }
270
+ return names;
271
+ }
272
+
273
+ /**
274
+ * Get config for a specific computation.
275
+ * @param {string} name
276
+ * @returns {Object|null}
277
+ */
278
+ getComputationConfig(name) {
279
+ for (const comp of this.config.computations || []) {
280
+ try {
281
+ const cfg = comp.getConfig();
282
+ if (cfg.name === name) return cfg;
283
+ } catch (e) { /* skip */ }
284
+ }
285
+ return null;
286
+ }
287
+
288
+ /**
289
+ * Get all computation configs, cached.
290
+ * @returns {Map<string, Object>}
291
+ */
292
+ getAllComputationConfigs() {
293
+ if (this._cache.computationConfigs) return this._cache.computationConfigs;
294
+
295
+ const map = new Map();
296
+ for (const comp of this.config.computations || []) {
297
+ try {
298
+ const cfg = comp.getConfig();
299
+ map.set(cfg.name, cfg);
300
+ } catch (e) { /* skip */ }
301
+ }
302
+
303
+ this._cache.computationConfigs = map;
304
+ return map;
305
+ }
306
+
307
+ /**
308
+ * Build dependency graph from computation configs.
309
+ * @returns {Map<string, string[]>} computation name -> dependencies
310
+ */
311
+ getDependencyGraph() {
312
+ if (this._cache.dependencyGraph) return this._cache.dependencyGraph;
313
+
314
+ const graph = new Map();
315
+ const configs = this.getAllComputationConfigs();
316
+
317
+ for (const [name, cfg] of configs) {
318
+ const deps = [];
319
+
320
+ // Explicit dependencies
321
+ if (cfg.dependencies) {
322
+ deps.push(...cfg.dependencies);
323
+ }
324
+
325
+ // Conditional dependencies
326
+ if (cfg.conditionalDependencies) {
327
+ for (const cd of cfg.conditionalDependencies) {
328
+ if (cd.computation) deps.push(cd.computation);
329
+ }
330
+ }
331
+
332
+ graph.set(name, deps);
333
+ }
334
+
335
+ this._cache.dependencyGraph = graph;
336
+ return graph;
337
+ }
338
+
339
+ /**
340
+ * Calculate pass levels (topological depth) for all computations.
341
+ * @returns {Map<string, number>}
342
+ */
343
+ getPassLevels() {
344
+ if (this._cache.passLevels) return this._cache.passLevels;
345
+
346
+ const graph = this.getDependencyGraph();
347
+ const levels = new Map();
348
+ const visited = new Set();
349
+
350
+ const calculateLevel = (name) => {
351
+ if (levels.has(name)) return levels.get(name);
352
+ if (visited.has(name)) return 0; // Cycle detected
353
+ visited.add(name);
354
+
355
+ const deps = graph.get(name) || [];
356
+ if (deps.length === 0) {
357
+ levels.set(name, 1);
358
+ return 1;
359
+ }
360
+
361
+ let maxDepLevel = 0;
362
+ for (const dep of deps) {
363
+ maxDepLevel = Math.max(maxDepLevel, calculateLevel(dep));
364
+ }
365
+
366
+ const level = maxDepLevel + 1;
367
+ levels.set(name, level);
368
+ return level;
369
+ };
370
+
371
+ for (const name of graph.keys()) {
372
+ calculateLevel(name);
373
+ }
374
+
375
+ this._cache.passLevels = levels;
376
+ return levels;
377
+ }
378
+
379
+ /**
380
+ * Get the pass level for a specific computation.
381
+ * @param {string} name
382
+ * @returns {number}
383
+ */
384
+ getPassLevel(name) {
385
+ return this.getPassLevels().get(name) || 1;
386
+ }
387
+
388
+ /**
389
+ * Get total number of passes needed to run all computations.
390
+ * @returns {number}
391
+ */
392
+ getTotalPasses() {
393
+ const levels = this.getPassLevels();
394
+ return Math.max(...levels.values(), 1);
395
+ }
396
+
397
+ // =========================================================================
398
+ // VALIDATION HELPERS
399
+ // =========================================================================
400
+
401
+ /**
402
+ * Validate a computation config object.
403
+ * @param {Object} config
404
+ * @returns {{ valid: boolean, errors: string[], warnings: string[] }}
405
+ */
406
+ validateComputationConfig(config) {
407
+ const errors = [];
408
+ const warnings = [];
409
+
410
+ // Required fields
411
+ if (!config.name) errors.push('Missing required field: name');
412
+ if (!config.requires) errors.push('Missing required field: requires');
413
+
414
+ // Type validation
415
+ if (config.type && !this.constraints.validTypes.includes(config.type)) {
416
+ errors.push(`Invalid type: "${config.type}". Must be one of: ${this.constraints.validTypes.join(', ')}`);
417
+ }
418
+
419
+ // Table validation
420
+ if (config.requires) {
421
+ for (const tableName of Object.keys(config.requires)) {
422
+ if (!this.tableExists(tableName)) {
423
+ const closest = this.findClosestTable(tableName);
424
+ const suggestion = closest ? ` Did you mean "${closest}"?` : '';
425
+ errors.push(`Unknown table: "${tableName}".${suggestion}`);
426
+ }
427
+
428
+ // Lookback validation
429
+ const tableReq = config.requires[tableName];
430
+ if (tableReq.lookback !== undefined) {
431
+ if (tableReq.lookback > this.constraints.maxLookback) {
432
+ errors.push(`Lookback ${tableReq.lookback} for "${tableName}" exceeds maximum of ${this.constraints.maxLookback}`);
433
+ } else if (tableReq.lookback > this.constraints.maxLookback * 0.7) {
434
+ warnings.push(`Lookback ${tableReq.lookback} for "${tableName}" is ${Math.round(tableReq.lookback / this.constraints.maxLookback * 100)}% of maximum`);
435
+ }
436
+
437
+ // Check lookback on non-partitioned tables
438
+ const meta = this.getTableMetadata(tableName);
439
+ if (meta && !meta.isPartitioned && tableReq.lookback > 0) {
440
+ warnings.push(`Lookback on non-partitioned table "${tableName}" will fetch all rows`);
441
+ }
442
+ }
443
+ }
444
+ }
445
+
446
+ // Dependency validation
447
+ if (config.dependencies) {
448
+ for (const dep of config.dependencies) {
449
+ if (!this.getComputationNames().includes(dep)) {
450
+ errors.push(`Unknown dependency: "${dep}"`);
451
+ }
452
+ }
453
+ }
454
+
455
+ return { valid: errors.length === 0, errors, warnings };
456
+ }
457
+
458
+ // =========================================================================
459
+ // HELPERS
460
+ // =========================================================================
461
+
462
+ /**
463
+ * Levenshtein distance for fuzzy matching.
464
+ */
465
+ _levenshtein(a, b) {
466
+ const matrix = [];
467
+ for (let i = 0; i <= b.length; i++) {
468
+ matrix[i] = [i];
469
+ }
470
+ for (let j = 0; j <= a.length; j++) {
471
+ matrix[0][j] = j;
472
+ }
473
+ for (let i = 1; i <= b.length; i++) {
474
+ for (let j = 1; j <= a.length; j++) {
475
+ if (b.charAt(i - 1) === a.charAt(j - 1)) {
476
+ matrix[i][j] = matrix[i - 1][j - 1];
477
+ } else {
478
+ matrix[i][j] = Math.min(
479
+ matrix[i - 1][j - 1] + 1,
480
+ matrix[i][j - 1] + 1,
481
+ matrix[i - 1][j] + 1
482
+ );
483
+ }
484
+ }
485
+ }
486
+ return matrix[b.length][a.length];
487
+ }
488
+
489
+ /**
490
+ * Clear all caches.
491
+ */
492
+ clearCache() {
493
+ this._cache = {
494
+ computationConfigs: null,
495
+ dependencyGraph: null,
496
+ passLevels: null,
497
+ ruleIndex: null
498
+ };
499
+ }
500
+
501
+ /**
502
+ * Get summary statistics about the system.
503
+ * @returns {Object}
504
+ */
505
+ getStats() {
506
+ return {
507
+ tables: this.getAvailableTables().length,
508
+ ruleModules: this.getRuleModules().length,
509
+ ruleFunctions: this.getRuleIndex().size,
510
+ computations: this.getComputationNames().length,
511
+ totalPasses: this.getTotalPasses(),
512
+ maxLookback: this.constraints.maxLookback
513
+ };
514
+ }
515
+ }
516
+
517
+ module.exports = { SystemIntrospector };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @fileoverview Shared DevTools Module Index
3
+ *
4
+ * Central exports for the developer tooling shared layer.
5
+ */
6
+
7
+ const { SystemIntrospector } = require('./SystemIntrospector');
8
+ const { MockDataFactory } = require('./MockDataFactory');
9
+ const { SchemaTemplates, helpers: schemaHelpers } = require('./SchemaTemplates');
10
+
11
+ module.exports = {
12
+ SystemIntrospector,
13
+ MockDataFactory,
14
+ SchemaTemplates,
15
+ schemaHelpers
16
+ };