bulltrackers-module 1.0.210 → 1.0.212
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.
- package/functions/computation-system/controllers/computation_controller.js +199 -188
- package/functions/computation-system/helpers/computation_dispatcher.js +90 -90
- package/functions/computation-system/helpers/computation_manifest_builder.js +323 -283
- package/functions/computation-system/helpers/computation_pass_runner.js +185 -157
- package/functions/computation-system/helpers/computation_worker.js +85 -85
- package/functions/computation-system/helpers/orchestration_helpers.js +542 -558
- package/functions/computation-system/layers/extractors.js +415 -0
- package/functions/computation-system/layers/index.js +40 -0
- package/functions/computation-system/layers/math_primitives.js +743 -743
- package/functions/computation-system/layers/mathematics.js +397 -0
- package/functions/computation-system/layers/profiling.js +287 -0
- package/functions/computation-system/layers/validators.js +170 -0
- package/functions/computation-system/utils/schema_capture.js +63 -63
- package/functions/computation-system/utils/utils.js +22 -1
- package/functions/task-engine/helpers/update_helpers.js +34 -12
- package/package.json +1 -1
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Extractors Layer
|
|
3
|
+
* Core access methods to raw data (Portfolio, History, Prices, Insights).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { SCHEMAS } = require('./profiling');
|
|
7
|
+
|
|
8
|
+
class TradeSeriesBuilder {
|
|
9
|
+
/**
|
|
10
|
+
* Converts raw trade history into a time-series of Strategy Returns.
|
|
11
|
+
* Assumes a "Flat Bet" model (equal sizing) because absolute position size is not available in history.
|
|
12
|
+
* This creates a normalized performance curve for the user's *decisions*.
|
|
13
|
+
* @param {Array} historyTrades - PublicHistoryPositions array.
|
|
14
|
+
* @returns {Array<number>} Array of NetProfit% values sorted by close date.
|
|
15
|
+
*/
|
|
16
|
+
static buildReturnSeries(historyTrades) {
|
|
17
|
+
if (!historyTrades || !Array.isArray(historyTrades)) return [];
|
|
18
|
+
|
|
19
|
+
// 1. Filter valid closed trades
|
|
20
|
+
const closedTrades = historyTrades.filter(t => t.CloseDateTime && typeof t.NetProfit === 'number');
|
|
21
|
+
|
|
22
|
+
// 2. Sort by Close Date (Ascending)
|
|
23
|
+
closedTrades.sort((a, b) => new Date(a.CloseDateTime) - new Date(b.CloseDateTime));
|
|
24
|
+
|
|
25
|
+
// 3. Extract the PnL sequence
|
|
26
|
+
return closedTrades.map(t => t.NetProfit);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Builds a cumulative equity curve (starting at 100) based on compounding trade returns.
|
|
31
|
+
* Useful for visualising the trajectory of the strategy.
|
|
32
|
+
*/
|
|
33
|
+
static buildCumulativeCurve(returnSeries, startValue = 100) {
|
|
34
|
+
const curve = [startValue];
|
|
35
|
+
let current = startValue;
|
|
36
|
+
|
|
37
|
+
for (const ret of returnSeries) {
|
|
38
|
+
// Apply return (e.g. 5% profit -> * 1.05)
|
|
39
|
+
// Note: NetProfit in eToro history is usually percentage (e.g. 5.4954).
|
|
40
|
+
// We treat this as the return on *that specific position*.
|
|
41
|
+
// In a flat-bet model, we assume that position was X% of the portfolio.
|
|
42
|
+
// Simplified: We just accumulate the "points" captured.
|
|
43
|
+
current = current * (1 + (ret / 100));
|
|
44
|
+
curve.push(current);
|
|
45
|
+
}
|
|
46
|
+
return curve;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
class DataExtractor {
|
|
51
|
+
// ========================================================================
|
|
52
|
+
// 1. COLLECTION ACCESSORS
|
|
53
|
+
// ========================================================================
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Extract positions array based on User Type.
|
|
57
|
+
* - Normal: Uses 'AggregatedPositions' (Grouped by Asset + Direction)
|
|
58
|
+
* - Speculator: Uses 'PublicPositions' (Individual Trades)
|
|
59
|
+
*/
|
|
60
|
+
static getPositions(portfolio, userType) {
|
|
61
|
+
if (!portfolio) return []; // Handle empty portfolio
|
|
62
|
+
|
|
63
|
+
if (userType === SCHEMAS.USER_TYPES.SPECULATOR) {
|
|
64
|
+
return portfolio.PublicPositions || []; // SPECULATOR SCHEMA
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Default to Normal User Schema
|
|
68
|
+
return portfolio.AggregatedPositions || [];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ========================================================================
|
|
72
|
+
// 2. IDENTITY & KEYS
|
|
73
|
+
// ========================================================================
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Extract standardized Instrument ID.
|
|
77
|
+
*/
|
|
78
|
+
static getInstrumentId(position) {
|
|
79
|
+
if (!position) return null; // Handle empty position data
|
|
80
|
+
// Handle string or number variations safely
|
|
81
|
+
return position.InstrumentID || position.instrumentId || null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Extract a unique Identifier for the position.
|
|
86
|
+
* - Speculator: Uses 'PositionID'.
|
|
87
|
+
* - Normal: Generates Composite Key (InstrumentID_Direction) since they lack unique Trade IDs.
|
|
88
|
+
*/
|
|
89
|
+
static getPositionId(position) {
|
|
90
|
+
if (!position) return null; // Handle empty position data
|
|
91
|
+
|
|
92
|
+
// 1. Try Explicit ID (Speculators)
|
|
93
|
+
if (position.PositionID) return String(position.PositionID);
|
|
94
|
+
if (position.PositionId) return String(position.PositionId);
|
|
95
|
+
|
|
96
|
+
// 2. Fallback to Composite Key (Normal Users)
|
|
97
|
+
const instId = this.getInstrumentId(position);
|
|
98
|
+
const dir = this.getDirection(position);
|
|
99
|
+
if (instId) return `${instId}_${dir}`;
|
|
100
|
+
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ========================================================================
|
|
105
|
+
// 3. FINANCIAL METRICS (WEIGHTS & P&L)
|
|
106
|
+
// ========================================================================
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Extract Net Profit %.
|
|
110
|
+
* Schema: 'NetProfit' is the percentage profit relative to invested capital.
|
|
111
|
+
*/
|
|
112
|
+
static getNetProfit(position) {
|
|
113
|
+
return position ? (position.NetProfit || 0) : 0;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Extract Position Weight (Allocation %).
|
|
118
|
+
* Schema:
|
|
119
|
+
* - Normal: 'Invested' is % of initial capital.
|
|
120
|
+
* - Speculator: 'Invested' (or 'Amount' in some contexts) is % of initial capital.
|
|
121
|
+
*/
|
|
122
|
+
static getPositionWeight(position, userType) { // Agnostic on user type, unused.
|
|
123
|
+
if (!position) return 0;
|
|
124
|
+
|
|
125
|
+
// Both schemas use 'Invested' to represent the allocation percentage.
|
|
126
|
+
// Speculators might optionally have 'Amount', we prioritize 'Invested' for consistency.
|
|
127
|
+
return position.Invested || position.Amount || 0;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Extract Current Equity Value %.
|
|
132
|
+
* Schema: 'Value' is the current value as a % of total portfolio equity.
|
|
133
|
+
*/
|
|
134
|
+
static getPositionValuePct(position) {
|
|
135
|
+
return position ? (position.Value || 0) : 0;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* --- NEW PRIMITIVE ---
|
|
140
|
+
* Derives the approximate Entry Price of a position based on Current Price and Net Profit %.
|
|
141
|
+
* Formula: Entry = Current / (1 + (NetProfit / 100))
|
|
142
|
+
* @param {number} currentPrice - The current market price of the asset.
|
|
143
|
+
* @param {number} netProfitPct - The Net Profit percentage (e.g., -20.5).
|
|
144
|
+
* @returns {number} Estimated Entry Price.
|
|
145
|
+
*/
|
|
146
|
+
static deriveEntryPrice(currentPrice, netProfitPct) {
|
|
147
|
+
if (!currentPrice || currentPrice <= 0) return 0;
|
|
148
|
+
// Avoid division by zero if P&L is -100% (unlikely but possible in crypto/options)
|
|
149
|
+
if (netProfitPct <= -100) return Number.MAX_SAFE_INTEGER; // Effectively infinite entry price (lost everything)
|
|
150
|
+
return currentPrice / (1 + (netProfitPct / 100.0));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ========================================================================
|
|
154
|
+
// 4. PORTFOLIO LEVEL SUMMARY
|
|
155
|
+
// ========================================================================
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Calculate/Extract Daily Portfolio P&L %.
|
|
159
|
+
*/
|
|
160
|
+
static getPortfolioDailyPnl(portfolio, userType) {
|
|
161
|
+
if (!portfolio) return 0;
|
|
162
|
+
|
|
163
|
+
// 1. Speculator (Explicit 'NetProfit' field on root)
|
|
164
|
+
if (userType === SCHEMAS.USER_TYPES.SPECULATOR) {
|
|
165
|
+
return portfolio.NetProfit || 0;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// 2. Normal (Aggregated Calculation)
|
|
169
|
+
if (portfolio.AggregatedPositionsByInstrumentTypeID) {
|
|
170
|
+
return portfolio.AggregatedPositionsByInstrumentTypeID.reduce((sum, agg) => {
|
|
171
|
+
return sum + ((agg.Value || 0) - (agg.Invested || 0));
|
|
172
|
+
}, 0);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return 0;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ========================================================================
|
|
179
|
+
// 5. TRADE DETAILS (SPECULATOR SPECIFIC)
|
|
180
|
+
// ========================================================================
|
|
181
|
+
|
|
182
|
+
static getDirection(position) {
|
|
183
|
+
if (!position) return "Buy";
|
|
184
|
+
if (position.Direction) return position.Direction;
|
|
185
|
+
if (typeof position.IsBuy === 'boolean') return position.IsBuy ? "Buy" : "Sell";
|
|
186
|
+
return "Buy"; // Default
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
static getLeverage(position) {
|
|
190
|
+
return position ? (position.Leverage || 1) : 1; // Default 1 IF NOT FOUND
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
static getOpenRate(position) {
|
|
194
|
+
return position ? (position.OpenRate || 0) : 0; // Default 0 IF NOT FOUND
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
static getCurrentRate(position) {
|
|
198
|
+
return position ? (position.CurrentRate || 0) : 0; // Default 0 IF NOT FOUND
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
static getStopLossRate(position) {
|
|
202
|
+
const rate = position ? (position.StopLossRate || 0) : 0;
|
|
203
|
+
if (rate > 0 && rate <= 0.01) return 0; // Normalizes bug value to 0
|
|
204
|
+
if (rate < 0) return 0;
|
|
205
|
+
return rate;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
static getTakeProfitRate(position) {
|
|
209
|
+
const rate = position ? (position.TakeProfitRate || 0) : 0;
|
|
210
|
+
if (rate > 0 && rate <= 0.01) return 0; // Normalizes bug value to 0
|
|
211
|
+
return rate;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
static getHasTSL(position) {
|
|
215
|
+
return position ? (position.HasTrailingStopLoss === true) : false; // Default false IF NOT FOUND
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
static getOpenDateTime(position) {
|
|
219
|
+
if (!position || !position.OpenDateTime) return null;
|
|
220
|
+
return new Date(position.OpenDateTime);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
class priceExtractor {
|
|
225
|
+
static getHistory(pricesContext, tickerOrId) {
|
|
226
|
+
if (!pricesContext || !pricesContext.history) return [];
|
|
227
|
+
let assetData = pricesContext.history[tickerOrId];
|
|
228
|
+
|
|
229
|
+
if (!assetData) {
|
|
230
|
+
const id = Object.keys(pricesContext.history).find(key => {
|
|
231
|
+
const data = pricesContext.history[key];
|
|
232
|
+
return data.ticker === tickerOrId;
|
|
233
|
+
});
|
|
234
|
+
if (id) assetData = pricesContext.history[id];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (!assetData || !assetData.prices) return [];
|
|
238
|
+
|
|
239
|
+
const priceMap = assetData.prices;
|
|
240
|
+
const sortedDates = Object.keys(priceMap).sort((a, b) => a.localeCompare(b));
|
|
241
|
+
|
|
242
|
+
return sortedDates.map(date => ({
|
|
243
|
+
date: date,
|
|
244
|
+
price: priceMap[date]
|
|
245
|
+
})).filter(item => item.price > 0);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
static getAllHistories(pricesContext) {
|
|
249
|
+
if (!pricesContext || !pricesContext.history) return new Map();
|
|
250
|
+
|
|
251
|
+
const results = new Map();
|
|
252
|
+
for (const [id, data] of Object.entries(pricesContext.history)) {
|
|
253
|
+
const ticker = data.ticker || id;
|
|
254
|
+
const history = this.getHistory(pricesContext, id);
|
|
255
|
+
if (history.length > 0) {
|
|
256
|
+
results.set(ticker, history);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return results;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
class HistoryExtractor {
|
|
264
|
+
static getDailyHistory(user) {
|
|
265
|
+
return user?.history?.today || null;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
static getTradedAssets(historyDoc) {
|
|
269
|
+
const trades = historyDoc?.PublicHistoryPositions || [];
|
|
270
|
+
if (!trades.length) return [];
|
|
271
|
+
|
|
272
|
+
const assetsMap = new Map();
|
|
273
|
+
|
|
274
|
+
for (const t of trades) {
|
|
275
|
+
const instId = t.InstrumentID;
|
|
276
|
+
if (!instId) continue;
|
|
277
|
+
|
|
278
|
+
if (!assetsMap.has(instId)) {
|
|
279
|
+
assetsMap.set(instId, {
|
|
280
|
+
instrumentId: instId,
|
|
281
|
+
totalDuration: 0,
|
|
282
|
+
count: 0
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const asset = assetsMap.get(instId);
|
|
287
|
+
const open = new Date(t.OpenDateTime);
|
|
288
|
+
const close = new Date(t.CloseDateTime);
|
|
289
|
+
const durationMins = (close - open) / 60000;
|
|
290
|
+
|
|
291
|
+
if (durationMins > 0) {
|
|
292
|
+
asset.totalDuration += durationMins;
|
|
293
|
+
asset.count++;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return Array.from(assetsMap.values()).map(a => ({
|
|
298
|
+
instrumentId: a.instrumentId,
|
|
299
|
+
avgHoldingTimeInMinutes: a.count > 0 ? (a.totalDuration / a.count) : 0
|
|
300
|
+
}));
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
static getInstrumentId(asset) {
|
|
304
|
+
return asset ? asset.instrumentId : null;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
static getAvgHoldingTimeMinutes(asset) {
|
|
308
|
+
return asset ? (asset.avgHoldingTimeInMinutes || 0) : 0;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
static getSummary(historyDoc) {
|
|
312
|
+
const trades = historyDoc?.PublicHistoryPositions || [];
|
|
313
|
+
if (!trades.length) return null;
|
|
314
|
+
|
|
315
|
+
let totalTrades = trades.length;
|
|
316
|
+
let wins = 0;
|
|
317
|
+
let totalProf = 0;
|
|
318
|
+
let totalLoss = 0;
|
|
319
|
+
let profCount = 0;
|
|
320
|
+
let lossCount = 0;
|
|
321
|
+
let totalDur = 0;
|
|
322
|
+
|
|
323
|
+
for (const t of trades) {
|
|
324
|
+
if (t.NetProfit > 0) {
|
|
325
|
+
wins++;
|
|
326
|
+
totalProf += t.NetProfit;
|
|
327
|
+
profCount++;
|
|
328
|
+
} else if (t.NetProfit < 0) {
|
|
329
|
+
totalLoss += t.NetProfit;
|
|
330
|
+
lossCount++;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const open = new Date(t.OpenDateTime);
|
|
334
|
+
const close = new Date(t.CloseDateTime);
|
|
335
|
+
totalDur += (close - open) / 60000;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return {
|
|
339
|
+
totalTrades: totalTrades,
|
|
340
|
+
winRatio: totalTrades > 0 ? (wins / totalTrades) * 100 : 0,
|
|
341
|
+
avgProfitPct: profCount > 0 ? totalProf / profCount : 0,
|
|
342
|
+
avgLossPct: lossCount > 0 ? totalLoss / lossCount : 0,
|
|
343
|
+
avgHoldingTimeInMinutes: totalTrades > 0 ? totalDur / totalTrades : 0
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
class InsightsExtractor {
|
|
349
|
+
/**
|
|
350
|
+
* Extracts the raw array of insight objects from the context.
|
|
351
|
+
* Checks for standard context injection paths.
|
|
352
|
+
*/
|
|
353
|
+
static getInsights(context) {
|
|
354
|
+
// Support multiple potential injection paths depending on controller version
|
|
355
|
+
return context.insights || context.daily_instrument_insights || [];
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* returns the specific insight object for a given instrument ID.
|
|
360
|
+
*/
|
|
361
|
+
static getInsightForInstrument(insights, instrumentId) {
|
|
362
|
+
if (!insights || !Array.isArray(insights)) return null;
|
|
363
|
+
return insights.find(i => i.instrumentId === instrumentId) || null;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// --- Standard Metrics ---
|
|
367
|
+
|
|
368
|
+
static getTotalOwners(insight) {
|
|
369
|
+
return insight ? (insight.total || 0) : 0;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
static getLongPercent(insight) {
|
|
373
|
+
return insight ? (insight.buy || 0) : 0;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
static getShortPercent(insight) {
|
|
377
|
+
return insight ? (insight.sell || 0) : 0;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
static getGrowthPercent(insight) {
|
|
381
|
+
return insight ? (insight.growth || 0) : 0;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// --- Derived Counts (Estimated) ---
|
|
385
|
+
|
|
386
|
+
static getLongCount(insight) {
|
|
387
|
+
const total = this.getTotalOwners(insight);
|
|
388
|
+
const buyPct = this.getLongPercent(insight);
|
|
389
|
+
return Math.floor(total * (buyPct / 100));
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
static getShortCount(insight) {
|
|
393
|
+
const total = this.getTotalOwners(insight);
|
|
394
|
+
const sellPct = this.getShortPercent(insight);
|
|
395
|
+
return Math.floor(total * (sellPct / 100));
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Calculates the net change in users from yesterday based on growth %.
|
|
400
|
+
* Formula: NetChange = Total - (Total / (1 + Growth/100))
|
|
401
|
+
*/
|
|
402
|
+
static getNetOwnershipChange(insight) {
|
|
403
|
+
const total = this.getTotalOwners(insight);
|
|
404
|
+
const growth = this.getGrowthPercent(insight);
|
|
405
|
+
if (total === 0) return 0;
|
|
406
|
+
|
|
407
|
+
// Reverse engineer yesterday's count
|
|
408
|
+
// Today = Yesterday * (1 + growth)
|
|
409
|
+
// Yesterday = Today / (1 + growth)
|
|
410
|
+
const prevTotal = total / (1 + (growth / 100)); // TODO: Check precision issues
|
|
411
|
+
return Math.round(total - prevTotal);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
module.exports = { DataExtractor, priceExtractor, HistoryExtractor, InsightsExtractor, TradeSeriesBuilder };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Layers Barrel File
|
|
3
|
+
* Aggregates all mathematical, extractor, and profiling primitives.
|
|
4
|
+
* Utilizes require-all to dynamically load new modules added to this directory.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const requireAll = require('require-all');
|
|
8
|
+
|
|
9
|
+
// 1. Manually Require Core Modules to ensure order and presence
|
|
10
|
+
const profiling = require('./profiling');
|
|
11
|
+
const extractors = require('./extractors');
|
|
12
|
+
const mathematics = require('./mathematics');
|
|
13
|
+
const validators = require('./validators');
|
|
14
|
+
|
|
15
|
+
// 2. Aggregate explicit exports
|
|
16
|
+
const coreExports = {
|
|
17
|
+
...profiling,
|
|
18
|
+
...extractors,
|
|
19
|
+
...mathematics,
|
|
20
|
+
...validators
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// 3. Dynamic Loading (Optional/Future-Proofing)
|
|
24
|
+
// This loads any OTHER file in this directory that wasn't manually required above.
|
|
25
|
+
const dynamicModules = requireAll({
|
|
26
|
+
dirname: __dirname,
|
|
27
|
+
filter: /^(?!index\.js$|profiling\.js$|extractors\.js$|mathematics\.js$|validators\.js$).+\.js$/,
|
|
28
|
+
resolve: (mod) => mod
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Flatten dynamic modules into the export object
|
|
32
|
+
const dynamicExports = {};
|
|
33
|
+
Object.values(dynamicModules).forEach(moduleExports => {
|
|
34
|
+
Object.assign(dynamicExports, moduleExports);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
module.exports = {
|
|
38
|
+
...coreExports,
|
|
39
|
+
...dynamicExports
|
|
40
|
+
};
|