@forzalabs/remora 1.1.11 → 1.1.12

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.
@@ -1,53 +1,17 @@
1
1
  {
2
- "$schema": "https://raw.githubusercontent.com/ForzaLabs/remora-public/refs/heads/main/json_schemas/consumer-schema.json",
3
- "name": "<consumer name>",
4
- "description": "<consumer description>",
5
- "producers": [
6
- {
7
- "name": "<primary producer name>"
8
- },
9
- {
10
- "name": "<secondary producer name>",
11
- "joins": [
12
- {
13
- "otherName": "<primary producer name>",
14
- "relationship": "one-to-many",
15
- "sql": "${P.id} = ${<primary producer name>.fk_id}"
16
- }
17
- ]
18
- }
19
- ],
20
- "fields": [
21
- { "key": "<producer field name>", "from": "<producer name>" },
22
- { "key": "<original field name>", "from": "<producer name>", "alias": "<new field name>" },
23
- { "key": "<secondary producer field name>", "from": "<secondary producer name>" },
24
- { "key": "<another field name>", "from": "<producer name>" }
25
- ],
26
- "filters": [
27
- {
28
- "sql": "<filter condition>"
29
- }
30
- ],
31
- "outputs": [
32
- { "format": "API" },
33
- {
34
- "format": "JSON",
35
- "exportDestination": "<export destination>"
36
- },
37
- {
38
- "format": "CSV",
39
- "exportDestination": "<export destination>",
40
- "trigger": {
41
- "type": "CRON",
42
- "value": "0 0 * * *"
43
- }
44
- }
45
- ],
46
- "metadata": {
47
- "<metadata tag key>": "<metadata tag value>",
48
- "owner_email": "<owner email>"
49
- },
50
- "project": "<project name>",
51
- "schema": "<schema name>",
52
- "_version": 1
53
- }
2
+ "$schema": "https://raw.githubusercontent.com/ForzaLabs/remora-public/refs/heads/main/json_schemas/consumer-schema.json",
3
+ "name": "c_default",
4
+ "producers": [
5
+ { "name": "p_default" }
6
+ ],
7
+ "fields": [
8
+ { "key": "id" },
9
+ { "key": "name" },
10
+ { "key": "age" },
11
+ { "key": "country" },
12
+ { "key": "spend" }
13
+ ],
14
+ "outputs": [
15
+ { "format": "JSON", "exportDestination": "s_default" }
16
+ ]
17
+ }
@@ -0,0 +1,6 @@
1
+ id,name,age,country,spend
2
+ USR-00001,Alice,28,Italy,0,48
3
+ USR-00002,Bob,35,Portugal,191.75
4
+ USR-00003,Carla,36,Netherlands,23.5
5
+ USR-00004,Diego,54,France,35.25
6
+ USR-00005,Elisa,62,Germany,47,52
@@ -1,33 +1,16 @@
1
1
  {
2
- "$schema": "https://raw.githubusercontent.com/ForzaLabs/remora-public/refs/heads/main/json_schemas/producer-schema.json",
3
- "name": "<producer name>",
4
- "description": "<producer description>",
5
- "source": "<source name>",
6
- "dimensions": [
7
- {
8
- "name": "<primary key field name>",
9
- "type": "<number | string | datetime>",
10
- "pk": true,
11
- "description": "<field description>"
12
- },
13
- {
14
- "name": "<sensitive field name>",
15
- "type": "<number | string | datetime>",
16
- "classification": ["<'PHI' | 'PII' | 'GDPR'>"],
17
- "mask": "<hash | mask | crypt>",
18
- "description": "<field description>"
19
- }
20
- ],
21
- "measures": [
22
- {
23
- "name": "<calculated measure name>",
24
- "description": "<measure description>",
25
- "sql": "<sql expression>"
26
- }
27
- ],
28
- "settings": {
29
- "sqlTable": "<source table name>",
30
- "direct": true
31
- },
32
- "_version": 1
33
- }
2
+ "$schema": "https://raw.githubusercontent.com/ForzaLabs/remora-public/refs/heads/main/json_schemas/producer-schema.json",
3
+ "name": "p_default",
4
+ "source": "s_default",
5
+ "dimensions": [
6
+ { "name": "id", "type": "string" },
7
+ { "name": "name", "type": "string" },
8
+ { "name": "age", "type": "number" },
9
+ { "name": "country", "type": "string" },
10
+ { "name": "spend", "type": "number" }
11
+ ],
12
+ "settings": {
13
+ "fileType": "CSV",
14
+ "fileKey": "mock_data.csv"
15
+ }
16
+ }
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "https://raw.githubusercontent.com/ForzaLabs/remora-public/refs/heads/main/json_schemas/project-schema.json",
3
- "name": "PROJECT_NAME_EXAMPLE",
3
+ "name": "Default Remora Project",
4
4
  "version": "1.0.0",
5
- "description": "DESCRIPTION_OF_THIS_PROJECT",
5
+ "description": "This is the standard Remora project created when you run init.",
6
6
  "consumers": ["/consumers"],
7
7
  "producers": ["/producers"],
8
8
  "sources": ["/sources"],
@@ -1,16 +1,9 @@
1
1
  {
2
- "$schema": "https://raw.githubusercontent.com/ForzaLabs/remora-public/refs/heads/main/json_schemas/source-schema.json",
3
- "name": "<source name>",
4
- "description": "<source description>",
5
- "_version": 1,
6
- "authentication": {
7
- "method": "<iam | username-password | access-secret-key | arn | implicit>",
8
- "schema": "<database_schema_name - db schema>",
9
- "accessKey": "<access key var from env>",
10
- "secretKey": "<secret key var from env>",
11
- "region": "<aws region>",
12
- "database": "<db name>",
13
- "workgroup": "<db workgroup>"
14
- },
15
- "engine": "<aws-redshift | aws-dynamodb | aws-s3 | postgres | local>"
2
+ "$schema": "https://raw.githubusercontent.com/ForzaLabs/remora-public/refs/heads/main/json_schemas/source-schema.json",
3
+ "name": "s_default",
4
+ "authentication": {
5
+ "method": "implicit",
6
+ "path": "./default_remora_data"
7
+ },
8
+ "engine": "local"
16
9
  }
package/index.js CHANGED
@@ -13228,6 +13228,8 @@ var FileLogServiceClass = class {
13228
13228
  constructor() {
13229
13229
  this._enabled = false;
13230
13230
  this.enable = (folder = "logs", file = "remora.log") => {
13231
+ this._folder = folder;
13232
+ this._file = file;
13231
13233
  this._logger = import_winston.default.createLogger({
13232
13234
  level: "debug",
13233
13235
  format: import_winston.default.format.combine(
@@ -13260,6 +13262,23 @@ ${stack}` : base;
13260
13262
  });
13261
13263
  };
13262
13264
  this.flush = () => {
13265
+ if (!this._enabled || !this._logger) return Promise.resolve();
13266
+ return new Promise((resolve) => {
13267
+ let pending = this._logger.transports.length;
13268
+ if (pending === 0) return resolve();
13269
+ for (const transport of this._logger.transports) {
13270
+ if (typeof transport.on === "function") {
13271
+ transport.once("logged", () => {
13272
+ if (--pending === 0) resolve();
13273
+ });
13274
+ this._logger.info("");
13275
+ } else {
13276
+ if (--pending === 0) resolve();
13277
+ }
13278
+ }
13279
+ });
13280
+ };
13281
+ this.close = () => {
13263
13282
  if (!this._enabled || !this._logger) return Promise.resolve();
13264
13283
  return new Promise((resolve) => {
13265
13284
  this._logger.on("finish", resolve);
@@ -13339,6 +13358,7 @@ var Logger = class {
13339
13358
  FileLogService_default.write("INFO", String(message));
13340
13359
  };
13341
13360
  this.flush = () => FileLogService_default.flush();
13361
+ this.close = () => FileLogService_default.close();
13342
13362
  this.error = (error) => {
13343
13363
  let message;
13344
13364
  let stack;
@@ -13480,7 +13500,7 @@ var import_promises = __toESM(require("fs/promises"), 1);
13480
13500
 
13481
13501
  // ../../packages/constants/src/Constants.ts
13482
13502
  var CONSTANTS = {
13483
- cliVersion: "1.1.11",
13503
+ cliVersion: "1.1.12",
13484
13504
  backendVersion: 1,
13485
13505
  backendPort: 5088,
13486
13506
  workerVersion: 2,
@@ -16071,7 +16091,7 @@ var DOCUMENTATION_DIR = resolveAssetDir("documentation");
16071
16091
 
16072
16092
  // src/actions/init.ts
16073
16093
  var init = async () => {
16074
- console.log(import_chalk5.default.blue.bold("\u{1F4E6} Initializing your application..."));
16094
+ console.log(import_chalk5.default.blue.bold("\u{1F4E6} Initializing your Remora app (make sure you have set your REMORA_LICENCE_KEY in the environment variables)..."));
16075
16095
  try {
16076
16096
  const spinner = (0, import_ora3.default)("Creating configuration files...").start();
16077
16097
  const directories = [
@@ -16079,23 +16099,22 @@ var init = async () => {
16079
16099
  "remora/consumers",
16080
16100
  "remora/producers",
16081
16101
  "remora/schemas",
16082
- "remora/sources"
16102
+ "remora/sources",
16103
+ "default_remora_data"
16083
16104
  ];
16084
16105
  const defaultSource = import_fs_extra2.default.readFileSync(import_path13.default.join(DOCUMENTATION_DIR, "default_resources/source.json"), "utf-8");
16085
16106
  const defaultConsumer = import_fs_extra2.default.readFileSync(import_path13.default.join(DOCUMENTATION_DIR, "default_resources/consumer.json"), "utf-8");
16086
16107
  const defaultProducer = import_fs_extra2.default.readFileSync(import_path13.default.join(DOCUMENTATION_DIR, "default_resources/producer.json"), "utf-8");
16087
16108
  const defaultRemoraProject = import_fs_extra2.default.readFileSync(import_path13.default.join(DOCUMENTATION_DIR, "default_resources/project.json"), "utf-8");
16109
+ const defaultMockData = import_fs_extra2.default.readFileSync(import_path13.default.join(DOCUMENTATION_DIR, "default_resources/mock_data.csv"), "utf-8");
16088
16110
  const readme = import_fs_extra2.default.readFileSync(import_path13.default.join(DOCUMENTATION_DIR, "README.md"), "utf-8");
16089
16111
  const files = [
16090
- { path: "remora/sources/.gitkeep", content: "" },
16091
- { path: "remora/sources/default-source.json", content: defaultSource },
16092
- { path: "remora/consumers/.gitkeep", content: "" },
16093
- { path: "remora/consumers/default-consumer.json", content: defaultConsumer },
16094
- { path: "remora/producers/.gitkeep", content: "" },
16095
- { path: "remora/producers/default-producer.json", content: defaultProducer },
16096
- { path: "remora/schemas/.gitkeep", content: "" },
16112
+ { path: "remora/sources/s_default.json", content: defaultSource },
16113
+ { path: "remora/consumers/c_default.json", content: defaultConsumer },
16114
+ { path: "remora/producers/p_default.json", content: defaultProducer },
16097
16115
  { path: "remora/project.json", content: defaultRemoraProject },
16098
- { path: "remora/README.md", content: readme }
16116
+ { path: "remora/README.md", content: readme },
16117
+ { path: "default_remora_data/mock_data.csv", content: defaultMockData }
16099
16118
  ];
16100
16119
  for (let i = 0; i < directories.length; i++) {
16101
16120
  const dir = directories[i];
@@ -19326,19 +19345,17 @@ var ExecutorWriter_default = ExecutorWriter;
19326
19345
  var import_promises10 = require("stream/promises");
19327
19346
  var ExecutorOrchestratorClass = class {
19328
19347
  constructor() {
19329
- this.init = () => {
19330
- if (!this._executorPool) {
19331
- const options = {
19332
- workerThreadOpts: {
19333
- resourceLimits: {
19334
- maxOldGenerationSizeMb: Constants_default.defaults.MIN_RUNTIME_HEAP_MB
19335
- }
19348
+ this.createPool = () => {
19349
+ const options = {
19350
+ workerThreadOpts: {
19351
+ resourceLimits: {
19352
+ maxOldGenerationSizeMb: Constants_default.defaults.MIN_RUNTIME_HEAP_MB
19336
19353
  }
19337
- };
19338
- const workerPath = this._getWorkerPath();
19339
- Logger_default.log(`Initializing worker pool from ${workerPath} (heap limit: ${Constants_default.defaults.MIN_RUNTIME_HEAP_MB}MB)`);
19340
- this._executorPool = import_workerpool.default.pool(import_path19.default.join(workerPath, "ExecutorWorker.js"), options);
19341
- }
19354
+ }
19355
+ };
19356
+ const workerPath = this._getWorkerPath();
19357
+ Logger_default.log(`Initializing worker pool from ${workerPath} (heap limit: ${Constants_default.defaults.MIN_RUNTIME_HEAP_MB}MB)`);
19358
+ return import_workerpool.default.pool(import_path19.default.join(workerPath, "ExecutorWorker.js"), options);
19342
19359
  };
19343
19360
  this.launch = async (request) => {
19344
19361
  Affirm_default(request, "Invalid options");
@@ -19349,9 +19366,9 @@ var ExecutorOrchestratorClass = class {
19349
19366
  const _progress = new ExecutorProgress_default(logProgress);
19350
19367
  const { usageId } = UsageManager_default.startUsage(consumer, details);
19351
19368
  const scope = { id: usageId, folder: `${consumer.name}_${usageId}`, workersId: [], limitFileSize: consumer.MaximumFileSize };
19369
+ const pool = this.createPool();
19352
19370
  try {
19353
19371
  const start = performance.now();
19354
- this.init();
19355
19372
  const executorResults = [];
19356
19373
  Logger_default.log(`[${usageId}] Launching consumer "${consumer.name}" (invoked by: ${details.invokedBy}, user: ${details.user?.name ?? "unknown"}, producer(s): ${consumer.producers.length})`);
19357
19374
  let counter = performance.now();
@@ -19397,16 +19414,16 @@ var ExecutorOrchestratorClass = class {
19397
19414
  _progress.register((currentWorkerIndex + 1).toString(), prod.name, fileIndex, totalFiles);
19398
19415
  scope.workersId.push(workerId);
19399
19416
  Logger_default.log(`[${usageId}] Spawning worker ${workerId} for producer "${prod.name}" \u2014 chunk ${chunk.start}-${chunk.end} (${Math.round((chunk.end - chunk.start) / 1024)}KB)`);
19400
- workerThreads.push(this._executorPool.exec("executor", [workerData], {
19417
+ workerThreads.push(pool.exec("executor", [workerData], {
19401
19418
  on: (payload) => this.onWorkAdvanced(payload, currentWorkerIndex, _progress)
19402
19419
  }));
19403
19420
  }
19404
19421
  Logger_default.log(`[${usageId}] Waiting for ${workerThreads.length} worker(s) to complete`);
19405
19422
  executorResults.push(...await Promise.all(workerThreads));
19406
19423
  Logger_default.log(`[${usageId}] All ${workerThreads.length} worker(s) finished for producer "${prod.name}" file ${fileIndex + 1}/${totalFiles}`);
19407
- await this._executorPool.terminate();
19408
19424
  }
19409
19425
  }
19426
+ await pool.terminate();
19410
19427
  _progress.complete();
19411
19428
  if (executorResults.some((x) => !Algo_default.hasVal(x)))
19412
19429
  throw new Error(`${executorResults.filter((x) => !Algo_default.hasVal(x)).length} worker(s) failed to produce valid results`);
@@ -19465,6 +19482,7 @@ var ExecutorOrchestratorClass = class {
19465
19482
  } catch (error) {
19466
19483
  Logger_default.log(`[${usageId}] Consumer "${consumer.name}" failed: ${Helper_default.asError(error).message}`);
19467
19484
  Logger_default.error(Helper_default.asError(error));
19485
+ await pool.terminate();
19468
19486
  await ConsumerOnFinishManager_default.onConsumerError(consumer, usageId);
19469
19487
  Logger_default.log(`[${usageId}] Running cleanup after failure`);
19470
19488
  await this.performCleanupOperations(scope, tracker);
@@ -20070,6 +20088,8 @@ var mock = async (producerName, records) => {
20070
20088
 
20071
20089
  // src/index.ts
20072
20090
  import_dotenv.default.configDotenv();
20091
+ if (!process.env.NODE_ENV)
20092
+ process.env.NODE_ENV = "production";
20073
20093
  if (process.env.NODE_ENV !== "development" && process.env.REMORA_DEBUG_MODE === "true") {
20074
20094
  Logger_default.enableFileLogging("./remora/logs");
20075
20095
  console.log(`Enabled file logger.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forzalabs/remora",
3
- "version": "1.1.11",
3
+ "version": "1.1.12",
4
4
  "description": "A powerful CLI tool for seamless data translation.",
5
5
  "main": "index.js",
6
6
  "private": false,
@@ -13222,6 +13222,8 @@ var FileLogServiceClass = class {
13222
13222
  constructor() {
13223
13223
  this._enabled = false;
13224
13224
  this.enable = (folder = "logs", file = "remora.log") => {
13225
+ this._folder = folder;
13226
+ this._file = file;
13225
13227
  this._logger = import_winston.default.createLogger({
13226
13228
  level: "debug",
13227
13229
  format: import_winston.default.format.combine(
@@ -13254,6 +13256,23 @@ ${stack}` : base;
13254
13256
  });
13255
13257
  };
13256
13258
  this.flush = () => {
13259
+ if (!this._enabled || !this._logger) return Promise.resolve();
13260
+ return new Promise((resolve) => {
13261
+ let pending = this._logger.transports.length;
13262
+ if (pending === 0) return resolve();
13263
+ for (const transport of this._logger.transports) {
13264
+ if (typeof transport.on === "function") {
13265
+ transport.once("logged", () => {
13266
+ if (--pending === 0) resolve();
13267
+ });
13268
+ this._logger.info("");
13269
+ } else {
13270
+ if (--pending === 0) resolve();
13271
+ }
13272
+ }
13273
+ });
13274
+ };
13275
+ this.close = () => {
13257
13276
  if (!this._enabled || !this._logger) return Promise.resolve();
13258
13277
  return new Promise((resolve) => {
13259
13278
  this._logger.on("finish", resolve);
@@ -13333,6 +13352,7 @@ var Logger = class {
13333
13352
  FileLogService_default.write("INFO", String(message));
13334
13353
  };
13335
13354
  this.flush = () => FileLogService_default.flush();
13355
+ this.close = () => FileLogService_default.close();
13336
13356
  this.error = (error) => {
13337
13357
  let message;
13338
13358
  let stack;
@@ -13474,7 +13494,7 @@ var import_promises = __toESM(require("fs/promises"), 1);
13474
13494
 
13475
13495
  // ../../packages/constants/src/Constants.ts
13476
13496
  var CONSTANTS = {
13477
- cliVersion: "1.1.11",
13497
+ cliVersion: "1.1.12",
13478
13498
  backendVersion: 1,
13479
13499
  backendPort: 5088,
13480
13500
  workerVersion: 2,
@@ -19084,19 +19104,17 @@ var ExecutorWriter_default = ExecutorWriter;
19084
19104
  var import_promises10 = require("stream/promises");
19085
19105
  var ExecutorOrchestratorClass = class {
19086
19106
  constructor() {
19087
- this.init = () => {
19088
- if (!this._executorPool) {
19089
- const options = {
19090
- workerThreadOpts: {
19091
- resourceLimits: {
19092
- maxOldGenerationSizeMb: Constants_default.defaults.MIN_RUNTIME_HEAP_MB
19093
- }
19107
+ this.createPool = () => {
19108
+ const options = {
19109
+ workerThreadOpts: {
19110
+ resourceLimits: {
19111
+ maxOldGenerationSizeMb: Constants_default.defaults.MIN_RUNTIME_HEAP_MB
19094
19112
  }
19095
- };
19096
- const workerPath = this._getWorkerPath();
19097
- Logger_default.log(`Initializing worker pool from ${workerPath} (heap limit: ${Constants_default.defaults.MIN_RUNTIME_HEAP_MB}MB)`);
19098
- this._executorPool = import_workerpool.default.pool(import_path16.default.join(workerPath, "ExecutorWorker.js"), options);
19099
- }
19113
+ }
19114
+ };
19115
+ const workerPath = this._getWorkerPath();
19116
+ Logger_default.log(`Initializing worker pool from ${workerPath} (heap limit: ${Constants_default.defaults.MIN_RUNTIME_HEAP_MB}MB)`);
19117
+ return import_workerpool.default.pool(import_path16.default.join(workerPath, "ExecutorWorker.js"), options);
19100
19118
  };
19101
19119
  this.launch = async (request) => {
19102
19120
  Affirm_default(request, "Invalid options");
@@ -19107,9 +19125,9 @@ var ExecutorOrchestratorClass = class {
19107
19125
  const _progress = new ExecutorProgress_default(logProgress);
19108
19126
  const { usageId } = UsageManager_default.startUsage(consumer, details);
19109
19127
  const scope = { id: usageId, folder: `${consumer.name}_${usageId}`, workersId: [], limitFileSize: consumer.MaximumFileSize };
19128
+ const pool = this.createPool();
19110
19129
  try {
19111
19130
  const start = performance.now();
19112
- this.init();
19113
19131
  const executorResults = [];
19114
19132
  Logger_default.log(`[${usageId}] Launching consumer "${consumer.name}" (invoked by: ${details.invokedBy}, user: ${details.user?.name ?? "unknown"}, producer(s): ${consumer.producers.length})`);
19115
19133
  let counter = performance.now();
@@ -19155,16 +19173,16 @@ var ExecutorOrchestratorClass = class {
19155
19173
  _progress.register((currentWorkerIndex + 1).toString(), prod.name, fileIndex, totalFiles);
19156
19174
  scope.workersId.push(workerId);
19157
19175
  Logger_default.log(`[${usageId}] Spawning worker ${workerId} for producer "${prod.name}" \u2014 chunk ${chunk.start}-${chunk.end} (${Math.round((chunk.end - chunk.start) / 1024)}KB)`);
19158
- workerThreads.push(this._executorPool.exec("executor", [workerData], {
19176
+ workerThreads.push(pool.exec("executor", [workerData], {
19159
19177
  on: (payload) => this.onWorkAdvanced(payload, currentWorkerIndex, _progress)
19160
19178
  }));
19161
19179
  }
19162
19180
  Logger_default.log(`[${usageId}] Waiting for ${workerThreads.length} worker(s) to complete`);
19163
19181
  executorResults.push(...await Promise.all(workerThreads));
19164
19182
  Logger_default.log(`[${usageId}] All ${workerThreads.length} worker(s) finished for producer "${prod.name}" file ${fileIndex + 1}/${totalFiles}`);
19165
- await this._executorPool.terminate();
19166
19183
  }
19167
19184
  }
19185
+ await pool.terminate();
19168
19186
  _progress.complete();
19169
19187
  if (executorResults.some((x) => !Algo_default.hasVal(x)))
19170
19188
  throw new Error(`${executorResults.filter((x) => !Algo_default.hasVal(x)).length} worker(s) failed to produce valid results`);
@@ -19223,6 +19241,7 @@ var ExecutorOrchestratorClass = class {
19223
19241
  } catch (error) {
19224
19242
  Logger_default.log(`[${usageId}] Consumer "${consumer.name}" failed: ${Helper_default.asError(error).message}`);
19225
19243
  Logger_default.error(Helper_default.asError(error));
19244
+ await pool.terminate();
19226
19245
  await ConsumerOnFinishManager_default.onConsumerError(consumer, usageId);
19227
19246
  Logger_default.log(`[${usageId}] Running cleanup after failure`);
19228
19247
  await this.performCleanupOperations(scope, tracker);