bulltrackers-module 1.0.196 → 1.0.198
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.
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
* It validates metadata, resolves dependencies, and performs a topological sort
|
|
8
8
|
* to determine execution passes.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
10
|
+
* UPDATED LOGIC:
|
|
11
|
+
* - Core Computations: Metadata 'category' determines the output path/grouping.
|
|
12
|
+
* - Product Lines: Folder name STRICTLY determines the category (metadata ignored).
|
|
12
13
|
*/
|
|
13
14
|
|
|
14
15
|
/* --------------------------------------------------
|
|
@@ -31,7 +32,7 @@ const log = {
|
|
|
31
32
|
const normalizeName = (name) => { if (typeof name !== 'string') return name; return name.trim().replace(/,$/, '').replace(/_/g, '-').toLowerCase(); };
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
|
-
* Finds the closest string matches for a typo.
|
|
35
|
+
* Finds the closest string matches for a typo.
|
|
35
36
|
*/
|
|
36
37
|
function suggestClosest(name, candidates, n = 3) {
|
|
37
38
|
const levenshtein = (a = '', b = '') => {
|
|
@@ -97,30 +98,53 @@ function buildManifest(productLinesToRun = [], calculations) {
|
|
|
97
98
|
/* ---------------- 1. Load All Calculations ---------------- */
|
|
98
99
|
log.step('Loading and validating all calculation classes…');
|
|
99
100
|
const allCalculationClasses = new Map();
|
|
101
|
+
|
|
100
102
|
/**
|
|
101
103
|
* Processes a single calculation class, validates it, and adds to maps.
|
|
102
|
-
*
|
|
104
|
+
* @param {Class} Class - The calculation class structure.
|
|
105
|
+
* @param {string} name - The filename/key.
|
|
106
|
+
* @param {string} folderName - The folder this file came from (e.g. 'core', 'gem').
|
|
103
107
|
*/
|
|
104
|
-
function processCalc(Class, name,
|
|
108
|
+
function processCalc(Class, name, folderName) {
|
|
105
109
|
if (!Class || typeof Class !== 'function') return;
|
|
106
110
|
const normalizedName = normalizeName(name);
|
|
107
111
|
allCalculationClasses.set(normalizedName, Class);
|
|
112
|
+
|
|
108
113
|
// --- RULE 1: Check for static getMetadata() ---
|
|
109
114
|
if (typeof Class.getMetadata !== 'function') { log.fatal(`Calculation "${normalizedName}" is missing the static getMetadata() method. Build FAILED.`); hasFatalError = true; return; }
|
|
110
115
|
// --- RULE 2: Check for static getDependencies() ---
|
|
111
116
|
if (typeof Class.getDependencies !== 'function') { log.fatal(`Calculation "${normalizedName}" is missing the static getDependencies() method. Build FAILED.`); hasFatalError = true;return; }
|
|
112
117
|
// --- RULE 3: Check for static getSchema() ---
|
|
113
118
|
if (typeof Class.getSchema !== 'function') {log.warn(`Calculation "${normalizedName}" is missing the static getSchema() method. (Recommended)`); }
|
|
119
|
+
|
|
114
120
|
const metadata = Class.getMetadata();
|
|
115
121
|
const dependencies = Class.getDependencies().map(normalizeName);
|
|
122
|
+
|
|
116
123
|
// --- RULE 4: Check for isHistorical mismatch ---
|
|
117
|
-
if (metadata.isHistorical === true && !Class.toString().includes('yesterday')) {
|
|
124
|
+
if (metadata.isHistorical === true && !Class.toString().includes('yesterday')) {
|
|
118
125
|
log.warn(`Calculation "${normalizedName}" is marked 'isHistorical: true' but does not seem to reference 'yesterday' data.`);
|
|
119
126
|
}
|
|
127
|
+
|
|
128
|
+
// --- RULE 5: Category Enforcement Logic ---
|
|
129
|
+
let finalCategory = folderName; // Default to folder name
|
|
130
|
+
|
|
131
|
+
if (folderName === 'core') {
|
|
132
|
+
// For CORE: We respect metadata.category if it exists.
|
|
133
|
+
// This allows core computations to write to specific output paths (e.g. 'market_stats').
|
|
134
|
+
if (metadata.category) {
|
|
135
|
+
finalCategory = metadata.category;
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
// For PRODUCT LINES: We IGNORE metadata.category and enforce the folder name.
|
|
139
|
+
// This ensures product lines always group correctly regardless of typos in metadata.
|
|
140
|
+
finalCategory = folderName;
|
|
141
|
+
}
|
|
142
|
+
|
|
120
143
|
const manifestEntry = {
|
|
121
144
|
name: normalizedName,
|
|
122
145
|
class: Class,
|
|
123
|
-
category:
|
|
146
|
+
category: finalCategory, // The logic category / output path
|
|
147
|
+
sourcePackage: folderName, // The physical source folder (used for "Core Always Run" checks)
|
|
124
148
|
type: metadata.type,
|
|
125
149
|
isHistorical: metadata.isHistorical,
|
|
126
150
|
rootDataDependencies: metadata.rootDataDependencies || [],
|
|
@@ -131,19 +155,23 @@ function buildManifest(productLinesToRun = [], calculations) {
|
|
|
131
155
|
manifestMap.set(normalizedName, manifestEntry);
|
|
132
156
|
adjacency.set(normalizedName, dependencies);
|
|
133
157
|
inDegree.set(normalizedName, dependencies.length);
|
|
134
|
-
dependencies.forEach(dep => { if (!reverseAdjacency.has(dep)) reverseAdjacency.set(dep, []); reverseAdjacency.get(dep).push(normalizedName); });
|
|
158
|
+
dependencies.forEach(dep => { if (!reverseAdjacency.has(dep)) reverseAdjacency.set(dep, []); reverseAdjacency.get(dep).push(normalizedName); });
|
|
159
|
+
}
|
|
135
160
|
|
|
136
161
|
if (!calculations || typeof calculations !== 'object') {
|
|
137
162
|
log.fatal('Calculations object was not provided or is invalid.');
|
|
138
163
|
throw new Error('Manifest build failed: Invalid calculations object.');
|
|
139
164
|
}
|
|
140
165
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
166
|
+
// Iterate over folders (folderName becomes the default category)
|
|
167
|
+
for (const folderName in calculations) {
|
|
168
|
+
if (folderName === 'legacy') { log.info('Skipping "legacy" calculations package.'); continue; }
|
|
169
|
+
const group = calculations[folderName];
|
|
144
170
|
for (const key in group) {
|
|
145
171
|
const entry = group[key];
|
|
146
|
-
if (typeof entry === 'function') { processCalc(entry, key,
|
|
172
|
+
if (typeof entry === 'function') { processCalc(entry, key, folderName); }
|
|
173
|
+
}
|
|
174
|
+
}
|
|
147
175
|
|
|
148
176
|
if (hasFatalError) { throw new Error('Manifest build failed due to missing static methods in calculations.'); }
|
|
149
177
|
log.success(`Loaded and validated ${manifestMap.size} total calculations.`);
|
|
@@ -171,13 +199,24 @@ function buildManifest(productLinesToRun = [], calculations) {
|
|
|
171
199
|
|
|
172
200
|
/* ---------------- 3. Filter for Product Lines ---------------- */
|
|
173
201
|
log.divider('Filtering by Product Line');
|
|
174
|
-
|
|
175
|
-
|
|
202
|
+
|
|
203
|
+
// 1. Find all "endpoint" calculations in the target product lines.
|
|
204
|
+
// This checks the assigned category (which now matches the folder for all non-core lines).
|
|
176
205
|
const productLineEndpoints = [];
|
|
177
|
-
for (const [name, entry] of manifestMap.entries()) {
|
|
206
|
+
for (const [name, entry] of manifestMap.entries()) {
|
|
207
|
+
if (productLinesToRun.includes(entry.category)) {
|
|
208
|
+
productLineEndpoints.push(name);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
178
211
|
|
|
179
212
|
// 2. Add 'core' calculations as they are always included.
|
|
180
|
-
|
|
213
|
+
// We check 'sourcePackage' here so that even if a core calculation has a custom category
|
|
214
|
+
// (like 'market_stats'), it is still recognized as Core and included.
|
|
215
|
+
for (const [name, entry] of manifestMap.entries()) {
|
|
216
|
+
if (entry.sourcePackage === 'core') {
|
|
217
|
+
productLineEndpoints.push(name);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
181
220
|
|
|
182
221
|
// 3. Trace all dependencies upwards from these endpoints.
|
|
183
222
|
const requiredCalcs = getDependencySet(productLineEndpoints, adjacency);
|
|
@@ -227,9 +266,6 @@ function buildManifest(productLinesToRun = [], calculations) {
|
|
|
227
266
|
return sortedManifest;
|
|
228
267
|
}
|
|
229
268
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
269
|
/**
|
|
234
270
|
* Main entry point for building and exporting the manifest.
|
|
235
271
|
* @param {string[]} productLinesToRun - Array of product line categories (folder names) to build for.
|
|
@@ -245,5 +281,4 @@ function build(productLinesToRun, calculations) {
|
|
|
245
281
|
}
|
|
246
282
|
}
|
|
247
283
|
|
|
248
|
-
|
|
249
|
-
module.exports = { build};
|
|
284
|
+
module.exports = { build };
|