@forzalabs/remora 0.1.5-nasco.3 → 0.1.7-nasco.3

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.
@@ -13,6 +13,7 @@ class ExecutionPlannerClas {
13
13
  switch (engine) {
14
14
  case 'aws-dynamodb': return 'no-sql';
15
15
  case 'aws-redshift':
16
+ case 'delta-share':
16
17
  case 'postgres': return 'sql';
17
18
  case 'aws-s3': return 'file';
18
19
  case 'local': return 'local';
@@ -65,6 +66,8 @@ class ExecutionPlannerClas {
65
66
  default:
66
67
  throw new Error(`Output format "${output.format}" not supported`);
67
68
  }
69
+ if (output.onSuccess && output.onSuccess.length > 0)
70
+ plan.push({ type: 'perform-on-success-actions', output });
68
71
  }
69
72
  plan.push({ type: 'clean-datasets' });
70
73
  plan.push({ type: 'save-execution-stats' });
@@ -95,6 +95,7 @@ class ProducerEngineClass {
95
95
  case 'XLS':
96
96
  case 'XLSX':
97
97
  return { data: lines, dataset, dataType: 'lines-of-text' };
98
+ case 'PARQUET':
98
99
  case 'JSONL':
99
100
  case 'JSON': {
100
101
  if (lines.length === 1) {
@@ -0,0 +1,215 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ var __importDefault = (this && this.__importDefault) || function (mod) {
45
+ return (mod && mod.__esModule) ? mod : { "default": mod };
46
+ };
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ const cron = __importStar(require("node-cron"));
49
+ const Environment_1 = __importDefault(require("../Environment"));
50
+ const ConsumerEngine_1 = __importDefault(require("../consumer/ConsumerEngine"));
51
+ const UserManager_1 = __importDefault(require("../UserManager"));
52
+ class CronScheduler {
53
+ constructor() {
54
+ this.scheduledJobs = new Map();
55
+ this.isInitialized = false;
56
+ }
57
+ /**
58
+ * Initialize the CRON scheduler by scanning all consumers and scheduling those with CRON triggers
59
+ */
60
+ initialize() {
61
+ if (this.isInitialized) {
62
+ console.log('CRON scheduler already initialized');
63
+ return;
64
+ }
65
+ console.log('Initializing CRON scheduler...');
66
+ try {
67
+ const consumers = Environment_1.default.getAllConsumers();
68
+ let cronJobCount = 0;
69
+ for (const consumer of consumers) {
70
+ if (this.hasCronTrigger(consumer)) {
71
+ this.scheduleConsumer(consumer);
72
+ cronJobCount++;
73
+ }
74
+ }
75
+ this.isInitialized = true;
76
+ console.log(`CRON scheduler initialized with ${cronJobCount} scheduled jobs`);
77
+ }
78
+ catch (error) {
79
+ console.error('Failed to initialize CRON scheduler:', error);
80
+ throw error;
81
+ }
82
+ }
83
+ /**
84
+ * Check if a consumer has any CRON triggers configured
85
+ */
86
+ hasCronTrigger(consumer) {
87
+ return consumer.outputs.some(output => { var _a; return ((_a = output.trigger) === null || _a === void 0 ? void 0 : _a.type) === 'CRON' && output.trigger.value; });
88
+ }
89
+ /**
90
+ * Schedule a consumer with CRON triggers
91
+ */
92
+ scheduleConsumer(consumer) {
93
+ consumer.outputs.forEach((output, index) => {
94
+ var _a;
95
+ if (((_a = output.trigger) === null || _a === void 0 ? void 0 : _a.type) === 'CRON' && output.trigger.value) {
96
+ const jobKey = `${consumer.name}_output_${index}`;
97
+ const cronExpression = output.trigger.value;
98
+ try {
99
+ // Validate CRON expression
100
+ if (!cron.validate(cronExpression)) {
101
+ console.error(`Invalid CRON expression for consumer ${consumer.name}: ${cronExpression}`);
102
+ return;
103
+ }
104
+ // Schedule the job
105
+ const task = cron.schedule(cronExpression, () => __awaiter(this, void 0, void 0, function* () {
106
+ yield this.executeConsumerOutput(consumer, output, index);
107
+ }));
108
+ // Don't start the task immediately, we'll start it manually
109
+ task.stop();
110
+ this.scheduledJobs.set(jobKey, task);
111
+ task.start();
112
+ console.log(`Scheduled CRON job for consumer "${consumer.name}" output ${index} with expression: ${cronExpression}`);
113
+ }
114
+ catch (error) {
115
+ console.error(`Failed to schedule CRON job for consumer ${consumer.name}:`, error);
116
+ }
117
+ }
118
+ });
119
+ }
120
+ /**
121
+ * Execute a consumer output when triggered by CRON
122
+ */
123
+ executeConsumerOutput(consumer, output, outputIndex) {
124
+ return __awaiter(this, void 0, void 0, function* () {
125
+ try {
126
+ console.log(`Executing CRON job for consumer "${consumer.name}" output ${outputIndex}`);
127
+ const user = UserManager_1.default.getUser();
128
+ // Execute the consumer with default options
129
+ const result = yield ConsumerEngine_1.default.execute(consumer, {}, user);
130
+ console.log(`CRON job completed successfully for consumer "${consumer.name}" output ${outputIndex}`);
131
+ // Log execution statistics
132
+ if (result && result._stats) {
133
+ console.log(`CRON job stats: ${result._stats.elapsedMS}ms, size: ${result._stats.size}, cycles: ${result._stats.cycles}`);
134
+ }
135
+ }
136
+ catch (error) {
137
+ console.error(`CRON job failed for consumer "${consumer.name}" output ${outputIndex}:`, error);
138
+ // Optionally, you could implement error handling strategies here:
139
+ // - Send notifications
140
+ // - Log to a monitoring system
141
+ // - Retry logic
142
+ // - Disable the job after repeated failures
143
+ }
144
+ });
145
+ }
146
+ /**
147
+ * Add or update a CRON job for a specific consumer
148
+ */
149
+ updateConsumerSchedule(consumer) {
150
+ // First, remove any existing schedules for this consumer
151
+ this.removeConsumerSchedule(consumer.name);
152
+ // Then, add new schedules if they have CRON triggers
153
+ if (this.hasCronTrigger(consumer)) {
154
+ this.scheduleConsumer(consumer);
155
+ }
156
+ }
157
+ /**
158
+ * Remove all scheduled jobs for a consumer
159
+ */
160
+ removeConsumerSchedule(consumerName) {
161
+ const jobsToRemove = Array.from(this.scheduledJobs.keys()).filter(key => key.startsWith(`${consumerName}_output_`));
162
+ jobsToRemove.forEach(jobKey => {
163
+ const task = this.scheduledJobs.get(jobKey);
164
+ if (task) {
165
+ task.stop();
166
+ task.destroy();
167
+ this.scheduledJobs.delete(jobKey);
168
+ console.log(`Removed CRON job: ${jobKey}`);
169
+ }
170
+ });
171
+ }
172
+ /**
173
+ * Get information about all scheduled jobs
174
+ */
175
+ getScheduledJobs() {
176
+ return Array.from(this.scheduledJobs.entries()).map(([jobKey, task]) => ({
177
+ jobKey,
178
+ isRunning: task.getStatus() === 'scheduled'
179
+ }));
180
+ }
181
+ /**
182
+ * Stop all scheduled jobs
183
+ */
184
+ stopAllJobs() {
185
+ console.log('Stopping all CRON jobs...');
186
+ this.scheduledJobs.forEach((task, jobKey) => {
187
+ task.stop();
188
+ task.destroy();
189
+ console.log(`Stopped CRON job: ${jobKey}`);
190
+ });
191
+ this.scheduledJobs.clear();
192
+ this.isInitialized = false;
193
+ console.log('All CRON jobs stopped');
194
+ }
195
+ /**
196
+ * Restart the scheduler (useful for configuration reloads)
197
+ */
198
+ restart() {
199
+ console.log('Restarting CRON scheduler...');
200
+ this.stopAllJobs();
201
+ this.initialize();
202
+ }
203
+ /**
204
+ * Get the scheduler status
205
+ */
206
+ getStatus() {
207
+ return {
208
+ initialized: this.isInitialized,
209
+ jobCount: this.scheduledJobs.size,
210
+ jobs: this.getScheduledJobs()
211
+ };
212
+ }
213
+ }
214
+ // Export a singleton instance
215
+ exports.default = new CronScheduler();
@@ -0,0 +1,307 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const client_sqs_1 = require("@aws-sdk/client-sqs");
16
+ const Environment_1 = __importDefault(require("../Environment"));
17
+ const ConsumerEngine_1 = __importDefault(require("../consumer/ConsumerEngine"));
18
+ const UserManager_1 = __importDefault(require("../UserManager"));
19
+ const SecretManager_1 = __importDefault(require("../SecretManager"));
20
+ class QueueManager {
21
+ constructor() {
22
+ this.queueMappings = new Map();
23
+ this.pollingIntervals = new Map();
24
+ this.isInitialized = false;
25
+ this.POLLING_INTERVAL_MS = 5000; // Poll every 5 seconds
26
+ this.MAX_MESSAGES = 10; // Maximum messages to receive in one poll
27
+ // Initialize SQS client with default configuration
28
+ // Will be reconfigured when we know the specific queue details
29
+ this.sqsClient = new client_sqs_1.SQSClient({});
30
+ }
31
+ /**
32
+ * Initialize the Queue Manager by scanning all consumers and setting up queue listeners for those with QUEUE triggers
33
+ */
34
+ initialize() {
35
+ if (this.isInitialized) {
36
+ console.log('Queue Manager already initialized');
37
+ return;
38
+ }
39
+ console.log('Initializing Queue Manager...');
40
+ try {
41
+ const consumers = Environment_1.default.getAllConsumers();
42
+ let queueTriggerCount = 0;
43
+ for (const consumer of consumers) {
44
+ if (this.hasQueueTrigger(consumer)) {
45
+ this.setupQueueListeners(consumer);
46
+ queueTriggerCount++;
47
+ }
48
+ }
49
+ this.isInitialized = true;
50
+ console.log(`Queue Manager initialized with ${queueTriggerCount} queue triggers`);
51
+ }
52
+ catch (error) {
53
+ console.error('Failed to initialize Queue Manager:', error);
54
+ throw error;
55
+ }
56
+ }
57
+ /**
58
+ * Check if a consumer has any QUEUE triggers configured
59
+ */
60
+ hasQueueTrigger(consumer) {
61
+ return consumer.outputs.some(output => { var _a; return ((_a = output.trigger) === null || _a === void 0 ? void 0 : _a.type) === 'QUEUE' && output.trigger.value; });
62
+ }
63
+ /**
64
+ * Setup queue listeners for a consumer with QUEUE triggers
65
+ */
66
+ setupQueueListeners(consumer) {
67
+ consumer.outputs.forEach((output, index) => {
68
+ var _a;
69
+ if (((_a = output.trigger) === null || _a === void 0 ? void 0 : _a.type) === 'QUEUE' && output.trigger.value) {
70
+ try {
71
+ const queueConfig = this.parseQueueConfig(output.trigger.value, output.trigger.metadata);
72
+ const mapping = {
73
+ consumer,
74
+ outputIndex: index,
75
+ queueUrl: queueConfig.queueUrl,
76
+ messageType: queueConfig.messageType
77
+ };
78
+ // Add to mappings
79
+ if (!this.queueMappings.has(queueConfig.queueUrl)) {
80
+ this.queueMappings.set(queueConfig.queueUrl, []);
81
+ }
82
+ this.queueMappings.get(queueConfig.queueUrl).push(mapping);
83
+ // Start polling for this queue if not already started
84
+ if (!this.pollingIntervals.has(queueConfig.queueUrl)) {
85
+ this.startQueuePolling(queueConfig.queueUrl, queueConfig.region, queueConfig.credentials);
86
+ }
87
+ console.log(`Setup queue listener for consumer "${consumer.name}" output ${index} on queue: ${queueConfig.queueUrl}`);
88
+ }
89
+ catch (error) {
90
+ console.error(`Failed to setup queue listener for consumer ${consumer.name}:`, error);
91
+ }
92
+ }
93
+ });
94
+ }
95
+ /**
96
+ * Parse queue configuration from trigger value and metadata
97
+ */
98
+ parseQueueConfig(triggerValue, metadata) {
99
+ // triggerValue should be the queue URL or queue name
100
+ let queueUrl = triggerValue;
101
+ // If it's not a full URL, construct it
102
+ if (!queueUrl.startsWith('https://')) {
103
+ const region = (metadata === null || metadata === void 0 ? void 0 : metadata.region) || process.env.AWS_DEFAULT_REGION || 'us-east-1';
104
+ const accountId = (metadata === null || metadata === void 0 ? void 0 : metadata.accountId) || process.env.AWS_ACCOUNT_ID;
105
+ if (!accountId) {
106
+ throw new Error('AWS Account ID is required for queue trigger. Set it in metadata.accountId or AWS_ACCOUNT_ID environment variable');
107
+ }
108
+ queueUrl = `https://sqs.${region}.amazonaws.com/${accountId}/${triggerValue}`;
109
+ }
110
+ // Extract region from URL if not provided in metadata
111
+ const urlParts = queueUrl.match(/https:\/\/sqs\.([^.]+)\.amazonaws\.com\//);
112
+ const region = (metadata === null || metadata === void 0 ? void 0 : metadata.region) || (urlParts === null || urlParts === void 0 ? void 0 : urlParts[1]) || 'us-east-1';
113
+ // Get credentials from metadata or environment
114
+ let credentials;
115
+ const accessKeyId = (metadata === null || metadata === void 0 ? void 0 : metadata.accessKeyId) || process.env.AWS_ACCESS_KEY_ID;
116
+ const secretAccessKey = (metadata === null || metadata === void 0 ? void 0 : metadata.secretAccessKey) || process.env.AWS_SECRET_ACCESS_KEY;
117
+ const sessionToken = (metadata === null || metadata === void 0 ? void 0 : metadata.sessionToken) || process.env.AWS_SESSION_TOKEN;
118
+ if (accessKeyId && secretAccessKey) {
119
+ credentials = {
120
+ accessKeyId: SecretManager_1.default.replaceSecret(accessKeyId),
121
+ secretAccessKey: SecretManager_1.default.replaceSecret(secretAccessKey),
122
+ sessionToken: sessionToken ? SecretManager_1.default.replaceSecret(sessionToken) : undefined
123
+ };
124
+ }
125
+ return {
126
+ queueUrl,
127
+ messageType: metadata === null || metadata === void 0 ? void 0 : metadata.messageType,
128
+ region,
129
+ credentials
130
+ };
131
+ }
132
+ /**
133
+ * Start polling a specific queue for messages
134
+ */
135
+ startQueuePolling(queueUrl, region, credentials) {
136
+ // Create SQS client for this specific queue
137
+ const sqsClient = new client_sqs_1.SQSClient({
138
+ region,
139
+ credentials
140
+ });
141
+ const pollQueue = () => __awaiter(this, void 0, void 0, function* () {
142
+ try {
143
+ const command = new client_sqs_1.ReceiveMessageCommand({
144
+ QueueUrl: queueUrl,
145
+ MaxNumberOfMessages: this.MAX_MESSAGES,
146
+ WaitTimeSeconds: 20, // Long polling
147
+ VisibilityTimeout: 300 // 5 minutes to process the message
148
+ });
149
+ const response = yield sqsClient.send(command);
150
+ if (response.Messages && response.Messages.length > 0) {
151
+ console.log(`Received ${response.Messages.length} messages from queue: ${queueUrl}`);
152
+ for (const message of response.Messages) {
153
+ yield this.processMessage(queueUrl, message, sqsClient);
154
+ }
155
+ }
156
+ }
157
+ catch (error) {
158
+ console.error(`Error polling queue ${queueUrl}:`, error);
159
+ }
160
+ });
161
+ // Start continuous polling
162
+ const interval = setInterval(pollQueue, this.POLLING_INTERVAL_MS);
163
+ this.pollingIntervals.set(queueUrl, interval);
164
+ // Start immediately
165
+ pollQueue();
166
+ console.log(`Started polling queue: ${queueUrl}`);
167
+ }
168
+ /**
169
+ * Process a message from the queue
170
+ */
171
+ processMessage(queueUrl, message, sqsClient) {
172
+ return __awaiter(this, void 0, void 0, function* () {
173
+ try {
174
+ const mappings = this.queueMappings.get(queueUrl);
175
+ if (!mappings || mappings.length === 0) {
176
+ console.log(`No consumer mappings found for queue: ${queueUrl}`);
177
+ return;
178
+ }
179
+ // Parse message body
180
+ let messageData;
181
+ try {
182
+ messageData = JSON.parse(message.Body || '{}');
183
+ }
184
+ catch (_a) {
185
+ console.warn(`Failed to parse message body as JSON for queue ${queueUrl}. Using raw body.`);
186
+ messageData = { body: message.Body };
187
+ }
188
+ let messageProcessedByAnyConsumer = false;
189
+ // Process message for each mapped consumer that matches the message criteria
190
+ for (const mapping of mappings) {
191
+ try {
192
+ // Check if message type matches (if specified)
193
+ if (mapping.messageType) {
194
+ const messageType = messageData.type || messageData.messageType || messageData.eventType;
195
+ if (messageType !== mapping.messageType) {
196
+ console.log(`Message type ${messageType} does not match expected ${mapping.messageType} for consumer ${mapping.consumer.name} - skipping`);
197
+ continue;
198
+ }
199
+ }
200
+ console.log(`Processing queue message for consumer "${mapping.consumer.name}" output ${mapping.outputIndex}`);
201
+ const user = UserManager_1.default.getUser();
202
+ // Execute the consumer with default options
203
+ const result = yield ConsumerEngine_1.default.execute(mapping.consumer, {}, user);
204
+ console.log(`Queue trigger completed successfully for consumer "${mapping.consumer.name}" output ${mapping.outputIndex}`);
205
+ // Log execution statistics
206
+ if (result && result._stats) {
207
+ console.log(`Queue trigger stats: ${result._stats.elapsedMS}ms, size: ${result._stats.size}, cycles: ${result._stats.cycles}`);
208
+ }
209
+ messageProcessedByAnyConsumer = true;
210
+ }
211
+ catch (error) {
212
+ console.error(`Queue trigger failed for consumer "${mapping.consumer.name}" output ${mapping.outputIndex}:`, error);
213
+ // Continue processing for other consumers even if one fails
214
+ }
215
+ }
216
+ // Only delete message from queue if it was processed by at least one consumer
217
+ // This ensures messages intended for other consumers or systems remain in the queue
218
+ if (messageProcessedByAnyConsumer && message.ReceiptHandle) {
219
+ yield sqsClient.send(new client_sqs_1.DeleteMessageCommand({
220
+ QueueUrl: queueUrl,
221
+ ReceiptHandle: message.ReceiptHandle
222
+ }));
223
+ console.log(`Deleted processed message ${message.MessageId} from queue`);
224
+ }
225
+ else if (!messageProcessedByAnyConsumer) {
226
+ console.log(`Message ${message.MessageId} was not processed by any consumer - leaving in queue for other consumers or systems`);
227
+ }
228
+ }
229
+ catch (error) {
230
+ console.error(`Error processing message from queue ${queueUrl}:`, error);
231
+ // Message will remain in queue and be retried or go to DLQ based on queue configuration
232
+ }
233
+ });
234
+ }
235
+ /**
236
+ * Add or update queue listeners for a specific consumer
237
+ */
238
+ updateConsumerSchedule(consumer) {
239
+ // First, remove any existing listeners for this consumer
240
+ this.removeConsumerSchedule(consumer.name);
241
+ // Then, add new listeners if they have QUEUE triggers
242
+ if (this.hasQueueTrigger(consumer)) {
243
+ this.setupQueueListeners(consumer);
244
+ }
245
+ }
246
+ /**
247
+ * Remove all queue listeners for a consumer
248
+ */
249
+ removeConsumerSchedule(consumerName) {
250
+ // Remove mappings for this consumer
251
+ for (const [queueUrl, mappings] of this.queueMappings.entries()) {
252
+ const updatedMappings = mappings.filter(mapping => mapping.consumer.name !== consumerName);
253
+ if (updatedMappings.length === 0) {
254
+ // No more consumers listening to this queue, stop polling
255
+ const interval = this.pollingIntervals.get(queueUrl);
256
+ if (interval) {
257
+ clearInterval(interval);
258
+ this.pollingIntervals.delete(queueUrl);
259
+ console.log(`Stopped polling queue: ${queueUrl}`);
260
+ }
261
+ this.queueMappings.delete(queueUrl);
262
+ }
263
+ else {
264
+ this.queueMappings.set(queueUrl, updatedMappings);
265
+ }
266
+ }
267
+ }
268
+ /**
269
+ * Stop all queue polling
270
+ */
271
+ stopAllQueues() {
272
+ console.log('Stopping all queue polling...');
273
+ this.pollingIntervals.forEach((interval, queueUrl) => {
274
+ clearInterval(interval);
275
+ console.log(`Stopped polling queue: ${queueUrl}`);
276
+ });
277
+ this.pollingIntervals.clear();
278
+ this.queueMappings.clear();
279
+ this.isInitialized = false;
280
+ console.log('All queue polling stopped');
281
+ }
282
+ /**
283
+ * Restart the queue manager (useful for configuration reloads)
284
+ */
285
+ restart() {
286
+ console.log('Restarting Queue Manager...');
287
+ this.stopAllQueues();
288
+ this.initialize();
289
+ }
290
+ /**
291
+ * Get the queue manager status
292
+ */
293
+ getStatus() {
294
+ const queues = Array.from(this.queueMappings.entries()).map(([queueUrl, mappings]) => ({
295
+ queueUrl,
296
+ consumerCount: mappings.length
297
+ }));
298
+ return {
299
+ initialized: this.isInitialized,
300
+ queueCount: this.queueMappings.size,
301
+ consumerCount: Array.from(this.queueMappings.values()).flat().length,
302
+ queues
303
+ };
304
+ }
305
+ }
306
+ // Export a singleton instance
307
+ exports.default = new QueueManager();
@@ -283,11 +283,11 @@ class TransformationEngineClass {
283
283
  }
284
284
  // Single transformation
285
285
  if ('cast' in transformations) {
286
- const { cast } = transformations;
286
+ const { cast, format } = transformations;
287
287
  let oldDimension = dataset.getDimensions().find(x => x.name === field.key);
288
288
  if (!oldDimension)
289
289
  oldDimension = dataset.getDimensions().find(x => x.key === field.key);
290
- const newDimension = Object.assign(Object.assign({}, structuredClone(oldDimension)), { type: cast });
290
+ const newDimension = Object.assign(Object.assign({}, structuredClone(oldDimension)), { type: cast, format: format });
291
291
  dataset.setSingleDimension(newDimension, oldDimension);
292
292
  }
293
293
  return dataset;
@@ -28,11 +28,19 @@ class TypeCasterClass {
28
28
  case 'datetime':
29
29
  case 'date': {
30
30
  let dateValue = null;
31
- if (format && typeof value === 'string')
32
- dateValue = dayjs_1.default.utc(value, format, true).toDate();
33
- else
31
+ try {
32
+ if (format && typeof value === 'string')
33
+ dateValue = dayjs_1.default.utc(value, format, true).toDate();
34
+ else
35
+ dateValue = new Date(value);
36
+ return dateValue.toISOString();
37
+ }
38
+ catch (error) {
34
39
  dateValue = new Date(value);
35
- return dateValue.toISOString();
40
+ if (!isNaN(dateValue))
41
+ return dateValue.toISOString();
42
+ throw new Error(`Error casting "${value}" to date with format "${format}": ${error}`);
43
+ }
36
44
  }
37
45
  case 'number': {
38
46
  if (typeof value === 'number')
@@ -106,5 +106,46 @@ class UsageDataManager {
106
106
  };
107
107
  });
108
108
  }
109
+ getFilteredUsageDetails(from, to, filters) {
110
+ return __awaiter(this, void 0, void 0, function* () {
111
+ const now = DSTE_1.default.now();
112
+ const fromDate = from || new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
113
+ const toDate = to || now;
114
+ const collection = 'usage';
115
+ // Build match criteria
116
+ const matchCriteria = {
117
+ startedAt: { $gte: fromDate, $lte: toDate }
118
+ };
119
+ if (filters === null || filters === void 0 ? void 0 : filters.consumer) {
120
+ matchCriteria.consumer = filters.consumer;
121
+ }
122
+ if (filters === null || filters === void 0 ? void 0 : filters.user) {
123
+ matchCriteria['executedBy.name'] = filters.user;
124
+ }
125
+ if (filters === null || filters === void 0 ? void 0 : filters.status) {
126
+ matchCriteria.status = filters.status;
127
+ }
128
+ // Query with filters and sorting
129
+ const usageDetails = yield DatabaseEngine_1.default.query(collection, matchCriteria, {
130
+ sort: { startedAt: -1 },
131
+ limit: 1000 // Reasonable limit to prevent large result sets
132
+ });
133
+ return usageDetails;
134
+ });
135
+ }
136
+ getUsageById(id) {
137
+ return __awaiter(this, void 0, void 0, function* () {
138
+ const collection = 'usage';
139
+ try {
140
+ const usageDetail = yield DatabaseEngine_1.default.get(collection, id);
141
+ return usageDetail;
142
+ }
143
+ catch (error) {
144
+ // If document not found or invalid ID, return null
145
+ console.error('Error fetching usage by ID:', error);
146
+ return null;
147
+ }
148
+ });
149
+ }
109
150
  }
110
151
  exports.default = new UsageDataManager();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forzalabs/remora",
3
- "version": "0.1.5-nasco.3",
3
+ "version": "0.1.7-nasco.3",
4
4
  "description": "A powerful CLI tool for seamless data translation.",
5
5
  "main": "index.js",
6
6
  "private": false,
@@ -35,6 +35,7 @@
35
35
  "dependencies": {
36
36
  "@aws-sdk/client-redshift-data": "^3.699.0",
37
37
  "@aws-sdk/client-s3": "^3.701.0",
38
+ "@aws-sdk/client-sqs": "^3.886.0",
38
39
  "adm-zip": "^0.5.16",
39
40
  "ajv": "^8.17.1",
40
41
  "ajv-formats": "^3.0.1",
@@ -54,6 +55,7 @@
54
55
  "knex": "^2.4.2",
55
56
  "mongodb": "^6.15.0",
56
57
  "next": "^13.4.1",
58
+ "node-cron": "^4.2.1",
57
59
  "ora": "^5.4.1",
58
60
  "react": "^18.2.0",
59
61
  "react-dom": "^18.2.0",