bulltrackers-module 1.0.126 → 1.0.128

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.
@@ -12,231 +12,142 @@
12
12
  * @param {string} dateString - The date in YYYY-MM-DD format.
13
13
  * @returns {Promise<Firestore.DocumentReference[]>} An array of DocumentReferences.
14
14
  */
15
- async function getPortfolioPartRefs(config, dependencies, dateString) {
16
- // <<< FIX: Destructure all dependencies here, inside the function >>>
17
- const { db, logger, calculationUtils } = dependencies;
15
+ /** --- Data Loader Sub-Pipes (Stateless, Dependency-Injection) --- */
16
+
17
+ /** Stage 1: Get portfolio part document references for a given date */
18
+ async function getPortfolioPartRefs(config, deps, dateString) {
19
+ const { db, logger, calculationUtils } = deps;
18
20
  const { withRetry } = calculationUtils;
19
- // <<< END FIX >>>
20
21
 
21
22
  logger.log('INFO', `Getting portfolio part references for date: ${dateString}`);
22
23
  const allPartRefs = [];
23
24
  const collectionsToQuery = [config.normalUserPortfolioCollection, config.speculatorPortfolioCollection];
24
25
 
25
26
  for (const collectionName of collectionsToQuery) {
26
- const blockDocsQuery = db.collection(collectionName); // Use db
27
- const blockDocRefs = await withRetry(
28
- () => blockDocsQuery.listDocuments(),
29
- `listDocuments(${collectionName})`
30
- );
31
-
32
- if (blockDocRefs.length === 0) {
33
- logger.log('WARN', `No block documents found in collection: ${collectionName}`);
34
- continue;
35
- }
27
+ const blockDocsQuery = db.collection(collectionName);
28
+ const blockDocRefs = await withRetry(() => blockDocsQuery.listDocuments(), `listDocuments(${collectionName})`);
29
+ if (!blockDocRefs.length) { logger.log('WARN', `No block documents in ${collectionName}`); continue; }
36
30
 
37
31
  for (const blockDocRef of blockDocRefs) {
38
32
  const partsCollectionRef = blockDocRef.collection(config.snapshotsSubcollection).doc(dateString).collection(config.partsSubcollection);
39
- const partDocs = await withRetry(
40
- () => partsCollectionRef.listDocuments(),
41
- `listDocuments(${partsCollectionRef.path})`
42
- );
33
+ const partDocs = await withRetry(() => partsCollectionRef.listDocuments(), `listDocuments(${partsCollectionRef.path})`);
43
34
  allPartRefs.push(...partDocs);
44
35
  }
45
36
  }
46
37
 
47
- logger.log('INFO', `Found ${allPartRefs.length} part document references for ${dateString}.`);
38
+ logger.log('INFO', `Found ${allPartRefs.length} portfolio part refs for ${dateString}`);
48
39
  return allPartRefs;
49
40
  }
50
41
 
51
- /**
52
- * Sub-pipe: pipe.computationSystem.dataLoader.loadDataByRefs
53
- * @param {object} config - The computation system configuration object.
54
- * @param {object} dependencies - Contains db, logger, calculationUtils.
55
- * @param {Firestore.DocumentReference[]} refs - An array of DocumentReferences to load.
56
- * @returns {Promise<object>} A single map of { [userId]: portfolioData }.
57
- */
58
- async function loadDataByRefs(config, dependencies, refs) {
59
- // <<< FIX: Destructure all dependencies here, inside the function >>>
60
- const { db, logger, calculationUtils } = dependencies;
42
+ /** Stage 2: Load data from an array of document references */
43
+ async function loadDataByRefs(config, deps, refs) {
44
+ const { db, logger, calculationUtils } = deps;
61
45
  const { withRetry } = calculationUtils;
62
- // <<< END FIX >>>
63
46
 
64
- if (!refs || refs.length === 0) { return {}; }
47
+ if (!refs || !refs.length) return {};
65
48
  const mergedPortfolios = {};
66
49
  const batchSize = config.partRefBatchSize || 50;
67
50
 
68
51
  for (let i = 0; i < refs.length; i += batchSize) {
69
52
  const batchRefs = refs.slice(i, i + batchSize);
70
- // Use db from dependencies
71
- const snapshots = await withRetry(
72
- () => db.getAll(...batchRefs),
73
- `getAll(batch ${Math.floor(i / batchSize)})`
74
- );
53
+ const snapshots = await withRetry(() => db.getAll(...batchRefs), `getAll(batch ${Math.floor(i / batchSize)})`);
54
+
75
55
  for (const doc of snapshots) {
76
- if (doc.exists) {
77
- const data = doc.data();
78
- if (data && typeof data === 'object') {
79
- Object.assign(mergedPortfolios, data);
80
- } else {
81
- logger.log('WARN', `Document ${doc.id} exists but data is not an object. Data:`, data);
82
- }
83
- }
56
+ if (!doc.exists) continue;
57
+ const data = doc.data();
58
+ if (data && typeof data === 'object') Object.assign(mergedPortfolios, data);
59
+ else logger.log('WARN', `Doc ${doc.id} exists but data is not an object`, data);
84
60
  }
85
61
  }
62
+
86
63
  return mergedPortfolios;
87
64
  }
88
65
 
89
- /**
90
- * Sub-pipe: pipe.computationSystem.dataLoader.loadFullDayMap
91
- * @param {object} config - The computation system configuration object.
92
- * @param {object} dependencies - Contains db, logger.
93
- * @param {Firestore.DocumentReference[]} partRefs - Array of part document references.
94
- * @returns {Promise<object>} A single map of { [userId]: portfolioData }.
95
- */
96
- async function loadFullDayMap(config, dependencies, partRefs) {
97
- // <<< FIX: Destructure only what's needed for this specific function >>>
98
- const { logger } = dependencies;
99
- // <<< END FIX >>>
66
+ /** Stage 3: Load a full day map by delegating to loadDataByRefs */
67
+ async function loadFullDayMap(config, deps, partRefs) {
68
+ const { logger } = deps;
69
+ if (!partRefs.length) return {};
100
70
 
101
- if (partRefs.length === 0) return {};
102
71
  logger.log('TRACE', `Loading full day map from ${partRefs.length} references...`);
103
-
104
- // Pass config and dependencies to sub-pipe
105
- const fullMap = await loadDataByRefs(config, dependencies, partRefs);
106
- logger.log('TRACE', `Full day map loaded with ${Object.keys(fullMap).length} users.`);
72
+ const fullMap = await loadDataByRefs(config, deps, partRefs);
73
+ logger.log('TRACE', `Full day map loaded with ${Object.keys(fullMap).length} users`);
107
74
  return fullMap;
108
75
  }
109
76
 
110
- /**
111
- * Sub-pipe: pipe.computationSystem.dataLoader.loadDailyInsights
112
- * Fetches the daily instrument insights document for a specific date.
113
- * @param {object} config - The computation system configuration object.
114
- * @param {object} dependencies - Contains db, logger, calculationUtils.
115
- * @param {string} dateString - The date in YYYY-MM-DD format.
116
- * @returns {Promise<object|null>} The insights data object or null if not found/error.
117
- */
118
- async function loadDailyInsights(config, dependencies, dateString) {
119
- // <<< FIX: Destructure all dependencies here, inside the function >>>
120
- const { db, logger, calculationUtils } = dependencies;
77
+ /** Stage 4: Load daily instrument insights */
78
+ async function loadDailyInsights(config, deps, dateString) {
79
+ const { db, logger, calculationUtils } = deps;
121
80
  const { withRetry } = calculationUtils;
122
- // <<< END FIX >>>
123
81
 
124
- const insightsCollectionName = config.insightsCollectionName || 'daily_instrument_insights'; // Use config or default
125
- logger.log('INFO', `Loading daily insights for date: ${dateString} from ${insightsCollectionName}`);
82
+ const insightsCollectionName = config.insightsCollectionName || 'daily_instrument_insights';
83
+ logger.log('INFO', `Loading daily insights for ${dateString} from ${insightsCollectionName}`);
84
+
126
85
  try {
127
86
  const docRef = db.collection(insightsCollectionName).doc(dateString);
128
87
  const docSnap = await withRetry(() => docRef.get(), `getInsights(${dateString})`);
129
-
130
- if (!docSnap.exists) {
131
- logger.log('WARN', `Daily insights document not found for ${dateString}`);
132
- return null;
133
- }
134
- logger.log('TRACE', `Successfully loaded insights for ${dateString}.`);
88
+ if (!docSnap.exists) { logger.log('WARN', `Insights not found for ${dateString}`); return null; }
89
+ logger.log('TRACE', `Successfully loaded insights for ${dateString}`);
135
90
  return docSnap.data();
136
91
  } catch (error) {
137
92
  logger.log('ERROR', `Failed to load daily insights for ${dateString}`, { errorMessage: error.message });
138
- return null; // Return null on error to allow computations to proceed partially if possible
93
+ return null;
139
94
  }
140
95
  }
141
96
 
142
- /**
143
- * --- NEW ---
144
- * Sub-pipe: pipe.computationSystem.dataLoader.loadDailySocialPostInsights
145
- * Fetches all analyzed social post documents for a specific date.
146
- * @param {object} config - The computation system configuration object.
147
- * @param {object} dependencies - Contains db, logger, calculationUtils.
148
- * @param {string} dateString - The date in YYYY-MM-DD format.
149
- * @returns {Promise<object|null>} An object map of { [postId]: postData } or null.
150
- */
151
- async function loadDailySocialPostInsights(config, dependencies, dateString) {
152
- // <<< FIX: Destructure all dependencies here, inside the function >>>
153
- const { db, logger, calculationUtils } = dependencies;
97
+ /** Stage 5: Load daily social post insights */
98
+ async function loadDailySocialPostInsights(config, deps, dateString) {
99
+ const { db, logger, calculationUtils } = deps;
154
100
  const { withRetry } = calculationUtils;
155
- // <<< END FIX >>>
156
101
 
157
- // Use the new config property, or fall back to a default
158
- const socialInsightsCollectionName = config.socialInsightsCollectionName || 'daily_social_insights';
159
- logger.log('INFO', `Loading social post insights for date: ${dateString} from ${socialInsightsCollectionName}`);
160
-
102
+ const collectionName = config.socialInsightsCollectionName || 'daily_social_insights';
103
+ logger.log('INFO', `Loading social post insights for ${dateString} from ${collectionName}`);
104
+
161
105
  try {
162
- const postsCollectionRef = db.collection(socialInsightsCollectionName).doc(dateString).collection('posts');
106
+ const postsCollectionRef = db.collection(collectionName).doc(dateString).collection('posts');
163
107
  const querySnapshot = await withRetry(() => postsCollectionRef.get(), `getSocialPosts(${dateString})`);
108
+ if (querySnapshot.empty) { logger.log('WARN', `No social post insights for ${dateString}`); return null; }
164
109
 
165
- if (querySnapshot.empty) {
166
- logger.log('WARN', `No social post insights found for ${dateString}.`);
167
- return null;
168
- }
169
-
170
110
  const postsMap = {};
171
- querySnapshot.forEach(doc => {
172
- postsMap[doc.id] = doc.data();
173
- });
174
-
175
- logger.log('TRACE', `Successfully loaded ${Object.keys(postsMap).length} social post insights for ${dateString}.`);
111
+ querySnapshot.forEach(doc => { postsMap[doc.id] = doc.data(); });
112
+ logger.log('TRACE', `Loaded ${Object.keys(postsMap).length} social post insights`);
176
113
  return postsMap;
177
-
178
114
  } catch (error) {
179
115
  logger.log('ERROR', `Failed to load social post insights for ${dateString}`, { errorMessage: error.message });
180
116
  return null;
181
117
  }
182
118
  }
183
- // --- END NEW ---
184
-
185
119
 
186
-
187
- /**
188
- * --- NEW ---
189
- * Sub-pipe: pipe.computationSystem.dataLoader.getHistoryPartRefs
190
- * @param {object} config - The computation system configuration object.
191
- * @param {object} dependencies - Contains db, logger, calculationUtils.
192
- * @param {string} dateString - The date in YYYY-MM-DD format.
193
- * @returns {Promise<Firestore.DocumentReference[]>} An array of DocumentReferences.
194
- */
195
- async function getHistoryPartRefs(config, dependencies, dateString) {
196
- const { db, logger, calculationUtils } = dependencies;
120
+ /** Stage 6: Get history part references for a given date */
121
+ async function getHistoryPartRefs(config, deps, dateString) {
122
+ const { db, logger, calculationUtils } = deps;
197
123
  const { withRetry } = calculationUtils;
198
124
 
199
- logger.log('INFO', `Getting history part references for date: ${dateString}`);
125
+ logger.log('INFO', `Getting history part references for ${dateString}`);
200
126
  const allPartRefs = [];
201
- // --- MODIFIED: Use new history collection config keys ---
202
127
  const collectionsToQuery = [config.normalUserHistoryCollection, config.speculatorHistoryCollection];
203
128
 
204
129
  for (const collectionName of collectionsToQuery) {
205
- if (!collectionName) { // Add a check in case config is missing
206
- logger.log('WARN', `History collection name is undefined. Skipping.`);
207
- continue;
208
- }
130
+ if (!collectionName) { logger.log('WARN', `History collection undefined. Skipping.`); continue; }
209
131
  const blockDocsQuery = db.collection(collectionName);
210
- const blockDocRefs = await withRetry(
211
- () => blockDocsQuery.listDocuments(),
212
- `listDocuments(${collectionName})`
213
- );
214
-
215
- if (blockDocRefs.length === 0) {
216
- logger.log('WARN', `No block documents found in collection: ${collectionName}`);
217
- continue;
218
- }
132
+ const blockDocRefs = await withRetry(() => blockDocsQuery.listDocuments(), `listDocuments(${collectionName})`);
133
+ if (!blockDocRefs.length) { logger.log('WARN', `No block documents in ${collectionName}`); continue; }
219
134
 
220
135
  for (const blockDocRef of blockDocRefs) {
221
136
  const partsCollectionRef = blockDocRef.collection(config.snapshotsSubcollection).doc(dateString).collection(config.partsSubcollection);
222
- const partDocs = await withRetry(
223
- () => partsCollectionRef.listDocuments(),
224
- `listDocuments(${partsCollectionRef.path})`
225
- );
137
+ const partDocs = await withRetry(() => partsCollectionRef.listDocuments(), `listDocuments(${partsCollectionRef.path})`);
226
138
  allPartRefs.push(...partDocs);
227
139
  }
228
140
  }
229
141
 
230
- logger.log('INFO', `Found ${allPartRefs.length} history part document references for ${dateString}.`);
142
+ logger.log('INFO', `Found ${allPartRefs.length} history part refs for ${dateString}`);
231
143
  return allPartRefs;
232
144
  }
233
145
 
234
-
235
146
  module.exports = {
236
147
  getPortfolioPartRefs,
237
148
  loadDataByRefs,
238
149
  loadFullDayMap,
239
150
  loadDailyInsights,
240
- loadDailySocialPostInsights,
241
- getHistoryPartRefs, // <-- EXPORT NEW FUNCTION
242
- };
151
+ loadDailySocialPostInsights,
152
+ getHistoryPartRefs,
153
+ };
@@ -3,56 +3,37 @@
3
3
  * REFACTORED: Now stateless and receive dependencies where needed.
4
4
  * DYNAMIC: Categorization logic is removed, replaced by manifest.
5
5
  */
6
+ /** --- Computation System Sub-Pipes & Utils (Stateless) --- */
6
7
 
7
8
  const { FieldValue, FieldPath } = require('@google-cloud/firestore');
8
9
 
9
- /**
10
- * Normalizes a calculation name to kebab-case.
11
- * @param {string} name
12
- * @returns {string}
13
- */
10
+ /** Stage 1: Normalize a calculation name to kebab-case */
14
11
  function normalizeName(name) {
15
12
  return name.replace(/_/g, '-');
16
13
  }
17
14
 
18
- /**
19
- * Sub-pipe: pipe.computationSystem.computationUtils.commitBatchInChunks
20
- * @param {object} config - The computation system configuration object.
21
- * @param {object} dependencies - Contains db, logger, calculationUtils.
22
- * @param {Array<object>} writes - Array of { ref: DocumentReference, data: object }.
23
- * @param {string} operationName - Name for logging.
24
- */
25
- async function commitBatchInChunks(config, dependencies, writes, operationName) {
26
- // --- MODIFIED: Get withRetry from dependencies ---
27
- const { db, logger, calculationUtils } = dependencies;
15
+ /** Stage 2: Commit a batch of writes in chunks */
16
+ async function commitBatchInChunks(config, deps, writes, operationName) {
17
+ const { db, logger, calculationUtils } = deps;
28
18
  const { withRetry } = calculationUtils;
29
- // --- END MODIFIED ---
30
-
19
+
31
20
  const batchSizeLimit = config.batchSizeLimit || 450;
32
-
33
- if (writes.length === 0) {
34
- logger.log('WARN', `[${operationName}] No writes to commit.`);
35
- return;
36
- }
21
+ if (!writes.length) { logger.log('WARN', `[${operationName}] No writes to commit.`); return; }
22
+
37
23
  for (let i = 0; i < writes.length; i += batchSizeLimit) {
38
- const batch = db.batch(); // Use db
39
24
  const chunk = writes.slice(i, i + batchSizeLimit);
25
+ const batch = db.batch();
40
26
  chunk.forEach(write => batch.set(write.ref, write.data, { merge: true }));
41
27
 
42
28
  const chunkNum = Math.floor(i / batchSizeLimit) + 1;
43
29
  const totalChunks = Math.ceil(writes.length / batchSizeLimit);
44
- await withRetry(
45
- () => batch.commit(),
46
- `${operationName} (Chunk ${chunkNum}/${totalChunks})`
47
- );
30
+ await withRetry(() => batch.commit(), `${operationName} (Chunk ${chunkNum}/${totalChunks})`);
31
+
48
32
  logger.log('INFO', `[${operationName}] Committed chunk ${chunkNum}/${totalChunks} (${chunk.length} ops).`);
49
33
  }
50
34
  }
51
35
 
52
- /**
53
- * Sub-pipe: pipe.computationSystem.computationUtils.getExpectedDateStrings
54
- * (Stateless)
55
- */
36
+ /** Stage 3: Generate an array of expected date strings between two dates */
56
37
  function getExpectedDateStrings(startDate, endDate) {
57
38
  const dateStrings = [];
58
39
  if (startDate <= endDate) {
@@ -65,86 +46,62 @@ function getExpectedDateStrings(startDate, endDate) {
65
46
  return dateStrings;
66
47
  }
67
48
 
68
- /**
69
- * Internal helper: Finds the earliest date document in a collection.
70
- */
71
- async function getFirstDateFromCollection(config, dependencies, collectionName) {
72
- // --- MODIFIED: Get withRetry from dependencies ---
73
- const { db, logger, calculationUtils } = dependencies;
49
+ /** Stage 4: Get the earliest date in a collection */
50
+ async function getFirstDateFromCollection(config, deps, collectionName) {
51
+ const { db, logger, calculationUtils } = deps;
74
52
  const { withRetry } = calculationUtils;
75
- // --- END MODIFIED ---
53
+
76
54
  let earliestDate = null;
77
55
  try {
78
- const blockDocRefs = await withRetry(
79
- () => db.collection(collectionName).listDocuments(), // Use db
80
- `GetBlocks(${collectionName})`
81
- );
82
-
83
- if (blockDocRefs.length === 0) {
84
- logger.log('WARN', `No block documents found in collection: ${collectionName}`);
85
- return null;
86
- }
87
-
88
- for (const blockDocRef of blockDocRefs) {
89
- const snapshotQuery = blockDocRef.collection(config.snapshotsSubcollection)
90
- .where(FieldPath.documentId(), '>=', '2000-01-01')
91
- .orderBy(FieldPath.documentId(), 'asc')
92
- .limit(1);
93
-
94
- const snapshotSnap = await withRetry(
95
- () => snapshotQuery.get(),
96
- `GetEarliestSnapshot(${blockDocRef.path})`
97
- );
98
-
99
- if (!snapshotSnap.empty && /^\d{4}-\d{2}-\d{2}$/.test(snapshotSnap.docs[0].id)) {
100
- const foundDate = new Date(snapshotSnap.docs[0].id + 'T00:00:00Z');
101
- if (!earliestDate || foundDate < earliestDate) {
102
- earliestDate = foundDate;
103
- }
104
- }
105
- }
56
+ const blockDocRefs = await withRetry(() => db.collection(collectionName).listDocuments(), `GetBlocks(${collectionName})`);
57
+ if (!blockDocRefs.length) { logger.log('WARN', `No block documents in collection: ${collectionName}`); return null; }
58
+
59
+ for (const blockDocRef of blockDocRefs) {
60
+ const snapshotQuery = blockDocRef.collection(config.snapshotsSubcollection)
61
+ .where(FieldPath.documentId(), '>=', '2000-01-01')
62
+ .orderBy(FieldPath.documentId(), 'asc')
63
+ .limit(1);
106
64
 
65
+ const snapshotSnap = await withRetry(() => snapshotQuery.get(), `GetEarliestSnapshot(${blockDocRef.path})`);
66
+ if (!snapshotSnap.empty && /^\d{4}-\d{2}-\d{2}$/.test(snapshotSnap.docs[0].id)) {
67
+ const foundDate = new Date(snapshotSnap.docs[0].id + 'T00:00:00Z');
68
+ if (!earliestDate || foundDate < earliestDate) earliestDate = foundDate;
69
+ }
70
+ }
107
71
  } catch (e) {
108
72
  logger.log('ERROR', `GetFirstDate failed for ${collectionName}`, { errorMessage: e.message });
109
73
  }
74
+
110
75
  return earliestDate;
111
76
  }
112
77
 
113
- /**
114
- * Sub-pipe: pipe.computationSystem.computationUtils.getFirstDateFromSourceData
115
- * @param {object} config - The computation system configuration object.
116
- * @param {object} dependencies - Contains db, logger, calculationUtils.
117
- * @returns {Promise<Date>} The earliest date found or a default fallback date.
118
- */
119
- async function getFirstDateFromSourceData(config, dependencies) {
120
- const { logger } = dependencies;
121
- logger.log('INFO', 'Querying for the earliest date from source portfolio data...');
122
-
123
- // Pass dependencies to sub-pipe
124
- const investorDate = await getFirstDateFromCollection(config, dependencies, config.normalUserPortfolioCollection);
125
- const speculatorDate = await getFirstDateFromCollection(config, dependencies, config.speculatorPortfolioCollection);
126
-
127
- let earliestDate;
128
- if (investorDate && speculatorDate) {
129
- earliestDate = investorDate < speculatorDate ? investorDate : speculatorDate;
130
- } else {
131
- earliestDate = investorDate || speculatorDate;
132
- }
133
-
134
- if (earliestDate) {
135
- logger.log('INFO', `Found earliest source data date: ${earliestDate.toISOString().slice(0, 10)}`);
136
- return earliestDate;
137
- } else {
138
- const fallbackDate = new Date(config.earliestComputationDate + 'T00:00:00Z' || '2023-01-01T00:00:00Z');
139
- logger.log('WARN', `No source data found. Defaulting first date to: ${fallbackDate.toISOString().slice(0, 10)}`);
140
- return fallbackDate;
141
- }
78
+ /** Stage 5: Determine the earliest date from source data across both user types */
79
+ async function getFirstDateFromSourceData(config, deps) {
80
+ const { logger } = deps;
81
+ logger.log('INFO', 'Querying for earliest date from source portfolio data...');
82
+
83
+ const investorDate = await getFirstDateFromCollection(config, deps, config.normalUserPortfolioCollection);
84
+ const speculatorDate = await getFirstDateFromCollection(config, deps, config.speculatorPortfolioCollection);
85
+
86
+ let earliestDate;
87
+ if (investorDate && speculatorDate) earliestDate = investorDate < speculatorDate ? investorDate : speculatorDate;
88
+ else earliestDate = investorDate || speculatorDate;
89
+
90
+ if (earliestDate) {
91
+ logger.log('INFO', `Found earliest source data date: ${earliestDate.toISOString().slice(0, 10)}`);
92
+ return earliestDate;
93
+ } else {
94
+ const fallbackDate = new Date(config.earliestComputationDate + 'T00:00:00Z' || '2023-01-01T00:00:00Z');
95
+ logger.log('WARN', `No source data found. Defaulting first date to: ${fallbackDate.toISOString().slice(0, 10)}`);
96
+ return fallbackDate;
97
+ }
142
98
  }
143
99
 
144
100
  module.exports = {
145
- FieldValue, FieldPath,
101
+ FieldValue,
102
+ FieldPath,
146
103
  normalizeName,
147
104
  commitBatchInChunks,
148
- getExpectedDateStrings,
105
+ getExpectedDateStrings,
149
106
  getFirstDateFromSourceData,
150
- };
107
+ };