@forzalabs/remora 1.2.9 → 1.2.11

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/index.js CHANGED
@@ -10293,7 +10293,7 @@ var require_node2 = __commonJS({
10293
10293
  var require_tail_file = __commonJS({
10294
10294
  "../../packages/logger/node_modules/winston/lib/winston/tail-file.js"(exports2, module2) {
10295
10295
  "use strict";
10296
- var fs25 = require("fs");
10296
+ var fs24 = require("fs");
10297
10297
  var { StringDecoder } = require("string_decoder");
10298
10298
  var { Stream } = require_readable();
10299
10299
  function noop() {
@@ -10314,7 +10314,7 @@ var require_tail_file = __commonJS({
10314
10314
  stream.emit("end");
10315
10315
  stream.emit("close");
10316
10316
  };
10317
- fs25.open(options.file, "a+", "0644", (err2, fd) => {
10317
+ fs24.open(options.file, "a+", "0644", (err2, fd) => {
10318
10318
  if (err2) {
10319
10319
  if (!iter) {
10320
10320
  stream.emit("error", err2);
@@ -10326,10 +10326,10 @@ var require_tail_file = __commonJS({
10326
10326
  }
10327
10327
  (function read() {
10328
10328
  if (stream.destroyed) {
10329
- fs25.close(fd, noop);
10329
+ fs24.close(fd, noop);
10330
10330
  return;
10331
10331
  }
10332
- return fs25.read(fd, buffer, 0, buffer.length, pos, (error, bytes) => {
10332
+ return fs24.read(fd, buffer, 0, buffer.length, pos, (error, bytes) => {
10333
10333
  if (error) {
10334
10334
  if (!iter) {
10335
10335
  stream.emit("error", error);
@@ -10388,7 +10388,7 @@ var require_tail_file = __commonJS({
10388
10388
  var require_file = __commonJS({
10389
10389
  "../../packages/logger/node_modules/winston/lib/winston/transports/file.js"(exports2, module2) {
10390
10390
  "use strict";
10391
- var fs25 = require("fs");
10391
+ var fs24 = require("fs");
10392
10392
  var path24 = require("path");
10393
10393
  var asyncSeries = require_series();
10394
10394
  var zlib2 = require("zlib");
@@ -10593,7 +10593,7 @@ var require_file = __commonJS({
10593
10593
  let buff = "";
10594
10594
  let results = [];
10595
10595
  let row = 0;
10596
- const stream = fs25.createReadStream(file, {
10596
+ const stream = fs24.createReadStream(file, {
10597
10597
  encoding: "utf8"
10598
10598
  });
10599
10599
  stream.on("error", (err2) => {
@@ -10745,7 +10745,7 @@ var require_file = __commonJS({
10745
10745
  stat(callback) {
10746
10746
  const target = this._getFile();
10747
10747
  const fullpath = path24.join(this.dirname, target);
10748
- fs25.stat(fullpath, (err2, stat) => {
10748
+ fs24.stat(fullpath, (err2, stat) => {
10749
10749
  if (err2 && err2.code === "ENOENT") {
10750
10750
  debug("ENOENT\xA0ok", fullpath);
10751
10751
  this.filename = target;
@@ -10850,7 +10850,7 @@ var require_file = __commonJS({
10850
10850
  _createStream(source) {
10851
10851
  const fullpath = path24.join(this.dirname, this.filename);
10852
10852
  debug("create stream start", fullpath, this.options);
10853
- const dest = fs25.createWriteStream(fullpath, this.options).on("error", (err2) => debug(err2)).on("close", () => debug("close", dest.path, dest.bytesWritten)).on("open", () => {
10853
+ const dest = fs24.createWriteStream(fullpath, this.options).on("error", (err2) => debug(err2)).on("close", () => debug("close", dest.path, dest.bytesWritten)).on("open", () => {
10854
10854
  debug("file open ok", fullpath);
10855
10855
  this.emit("open", fullpath);
10856
10856
  source.pipe(dest);
@@ -10929,7 +10929,7 @@ var require_file = __commonJS({
10929
10929
  const isZipped = this.zippedArchive ? ".gz" : "";
10930
10930
  const filePath = `${basename}${isOldest}${ext}${isZipped}`;
10931
10931
  const target = path24.join(this.dirname, filePath);
10932
- fs25.unlink(target, callback);
10932
+ fs24.unlink(target, callback);
10933
10933
  }
10934
10934
  /**
10935
10935
  * Roll files forward based on integer, up to maxFiles. e.g. if base if
@@ -10952,17 +10952,17 @@ var require_file = __commonJS({
10952
10952
  tasks.push(function(i, cb) {
10953
10953
  let fileName = `${basename}${i - 1}${ext}${isZipped}`;
10954
10954
  const tmppath = path24.join(this.dirname, fileName);
10955
- fs25.exists(tmppath, (exists) => {
10955
+ fs24.exists(tmppath, (exists) => {
10956
10956
  if (!exists) {
10957
10957
  return cb(null);
10958
10958
  }
10959
10959
  fileName = `${basename}${i}${ext}${isZipped}`;
10960
- fs25.rename(tmppath, path24.join(this.dirname, fileName), cb);
10960
+ fs24.rename(tmppath, path24.join(this.dirname, fileName), cb);
10961
10961
  });
10962
10962
  }.bind(this, x));
10963
10963
  }
10964
10964
  asyncSeries(tasks, () => {
10965
- fs25.rename(
10965
+ fs24.rename(
10966
10966
  path24.join(this.dirname, `${basename}${ext}${isZipped}`),
10967
10967
  path24.join(this.dirname, `${basename}1${ext}${isZipped}`),
10968
10968
  callback
@@ -10978,22 +10978,22 @@ var require_file = __commonJS({
10978
10978
  * @private
10979
10979
  */
10980
10980
  _compressFile(src, dest, callback) {
10981
- fs25.access(src, fs25.F_OK, (err2) => {
10981
+ fs24.access(src, fs24.F_OK, (err2) => {
10982
10982
  if (err2) {
10983
10983
  return callback();
10984
10984
  }
10985
10985
  var gzip = zlib2.createGzip();
10986
- var inp = fs25.createReadStream(src);
10987
- var out = fs25.createWriteStream(dest);
10986
+ var inp = fs24.createReadStream(src);
10987
+ var out = fs24.createWriteStream(dest);
10988
10988
  out.on("finish", () => {
10989
- fs25.unlink(src, callback);
10989
+ fs24.unlink(src, callback);
10990
10990
  });
10991
10991
  inp.pipe(gzip).pipe(out);
10992
10992
  });
10993
10993
  }
10994
10994
  _createLogDirIfNotExist(dirPath) {
10995
- if (!fs25.existsSync(dirPath)) {
10996
- fs25.mkdirSync(dirPath, { recursive: true });
10995
+ if (!fs24.existsSync(dirPath)) {
10996
+ fs24.mkdirSync(dirPath, { recursive: true });
10997
10997
  }
10998
10998
  }
10999
10999
  };
@@ -18744,25 +18744,6 @@ var ProcessENVManagerClass = class {
18744
18744
  var ProcessENVManager = new ProcessENVManagerClass();
18745
18745
  var ProcessENVManager_default = ProcessENVManager;
18746
18746
 
18747
- // ../../packages/common/src/SecretManager.ts
18748
- var SecretManagerClass = class {
18749
- constructor() {
18750
- /**
18751
- * If the value is a secret (or .env setting), replace it with the value inside the .env configuration file
18752
- * Starts with "{" and ends with "}".
18753
- * e.g. {AWS_ID}
18754
- */
18755
- this.replaceSecret = (value) => {
18756
- if (!value || value.length <= 2 || !value.startsWith("{") || !value.endsWith("}"))
18757
- return value;
18758
- const parsedValue = value.slice(1, value.length - 1);
18759
- return ProcessENVManager_default.getEnvVariable(parsedValue);
18760
- };
18761
- }
18762
- };
18763
- var SecretManager = new SecretManagerClass();
18764
- var SecretManager_default = SecretManager;
18765
-
18766
18747
  // ../../packages/common/src/ExecutorScope.ts
18767
18748
  var import_path3 = __toESM(require("path"), 1);
18768
18749
  var import_fs3 = __toESM(require("fs"), 1);
@@ -18770,7 +18751,7 @@ var import_promises = __toESM(require("fs/promises"), 1);
18770
18751
 
18771
18752
  // ../../packages/constants/src/Constants.ts
18772
18753
  var CONSTANTS = {
18773
- cliVersion: "1.2.9",
18754
+ cliVersion: "1.2.11",
18774
18755
  backendVersion: 1,
18775
18756
  backendPort: 5088,
18776
18757
  workerVersion: 2,
@@ -18816,10 +18797,10 @@ var ExecutorScopeClass = class {
18816
18797
  constructor() {
18817
18798
  this.WORKERS_FOLDER = "workers";
18818
18799
  this.PRODUCERS_FOLDER = "producers";
18800
+ this.getBasePath = () => import_path3.default.join(Constants_default.defaults.REMORA_PATH, Constants_default.defaults.PRODUCER_TEMP_FOLDER);
18819
18801
  this.getWorkerPath = (scope, workerId) => {
18820
18802
  return import_path3.default.join(
18821
- Constants_default.defaults.REMORA_PATH,
18822
- Constants_default.defaults.PRODUCER_TEMP_FOLDER,
18803
+ this.getBasePath(),
18823
18804
  // A specific execution sits entirely in this folder, so at the end it's safe to delete it entirely
18824
18805
  scope.folder,
18825
18806
  this.WORKERS_FOLDER,
@@ -18828,8 +18809,7 @@ var ExecutorScopeClass = class {
18828
18809
  };
18829
18810
  this.getProducerPath = (scope, producer, sourceFileKey) => {
18830
18811
  return import_path3.default.join(
18831
- Constants_default.defaults.REMORA_PATH,
18832
- Constants_default.defaults.PRODUCER_TEMP_FOLDER,
18812
+ this.getBasePath(),
18833
18813
  // A specific execution sits entirely in this folder, so at the end it's safe to delete it entirely
18834
18814
  scope.folder,
18835
18815
  this.PRODUCERS_FOLDER,
@@ -18839,22 +18819,30 @@ var ExecutorScopeClass = class {
18839
18819
  };
18840
18820
  this.getMainPath = (scope) => {
18841
18821
  return import_path3.default.join(
18842
- Constants_default.defaults.REMORA_PATH,
18843
- Constants_default.defaults.PRODUCER_TEMP_FOLDER,
18822
+ this.getBasePath(),
18844
18823
  scope.folder,
18845
18824
  "main.dataset"
18846
18825
  );
18847
18826
  };
18848
18827
  this.clearScope = async (scope) => {
18849
18828
  const scopePath = import_path3.default.join(
18850
- Constants_default.defaults.REMORA_PATH,
18851
- Constants_default.defaults.PRODUCER_TEMP_FOLDER,
18829
+ this.getBasePath(),
18852
18830
  scope.folder
18853
18831
  );
18854
18832
  if (import_fs3.default.existsSync(scopePath)) {
18855
18833
  await import_promises.default.rm(scopePath, { recursive: true, force: true });
18856
18834
  }
18857
18835
  };
18836
+ this.deepClear = () => {
18837
+ const basePath = this.getBasePath();
18838
+ const openScopes = this.getOpenScopes();
18839
+ for (const scopeFolder of openScopes) {
18840
+ const scopePath = import_path3.default.join(basePath, scopeFolder);
18841
+ if (import_fs3.default.existsSync(scopePath)) {
18842
+ import_fs3.default.rmSync(scopePath, { recursive: true, force: true });
18843
+ }
18844
+ }
18845
+ };
18858
18846
  this.ensurePath = (fileUri) => {
18859
18847
  const dir = import_path3.default.dirname(fileUri);
18860
18848
  if (!import_fs3.default.existsSync(dir))
@@ -18862,11 +18850,106 @@ var ExecutorScopeClass = class {
18862
18850
  if (!import_fs3.default.existsSync(fileUri))
18863
18851
  import_fs3.default.writeFileSync(fileUri, "");
18864
18852
  };
18853
+ this.getOpenScopes = () => {
18854
+ const basePath = this.getBasePath();
18855
+ if (!import_fs3.default.existsSync(basePath))
18856
+ return [];
18857
+ return import_fs3.default.readdirSync(basePath, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((folder) => folder !== "logs" && folder !== "usage");
18858
+ };
18865
18859
  }
18866
18860
  };
18867
18861
  var ExecutorScope = new ExecutorScopeClass();
18868
18862
  var ExecutorScope_default = ExecutorScope;
18869
18863
 
18864
+ // ../../packages/common/src/ProcessShutdownManager.ts
18865
+ var ProcessShutdownManagerClass = class {
18866
+ constructor() {
18867
+ this._initialized = false;
18868
+ this._cleaned = false;
18869
+ this._runtimeName = "Remora process";
18870
+ this.init = (runtimeName) => {
18871
+ if (this._initialized)
18872
+ return;
18873
+ this._initialized = true;
18874
+ if (runtimeName)
18875
+ this._runtimeName = runtimeName;
18876
+ process.once("SIGINT", () => this.handleSignal("SIGINT", 130));
18877
+ process.once("SIGTERM", () => this.handleSignal("SIGTERM", 143));
18878
+ process.once("uncaughtException", (error) => this.handleUnexpectedShutdown("uncaughtException", error));
18879
+ process.once("unhandledRejection", (reason) => this.handleUnexpectedShutdown("unhandledRejection", reason));
18880
+ process.once("beforeExit", (code) => this.handleBeforeExit(code));
18881
+ process.once("exit", (code) => this.handleExit(code));
18882
+ };
18883
+ this.handleSignal = (signal, exitCode) => {
18884
+ this.setShutdownState("intentional", signal);
18885
+ Logger_default.warn(`Received ${signal}. Shutting down ${this._runtimeName}.`);
18886
+ process.exit(exitCode);
18887
+ };
18888
+ this.handleUnexpectedShutdown = (reason, error) => {
18889
+ this.setShutdownState("unintentional", reason);
18890
+ Logger_default.error(this.asError(reason, error));
18891
+ process.exit(1);
18892
+ };
18893
+ this.handleBeforeExit = (code) => {
18894
+ this.setShutdownState(code === 0 ? "intentional" : "unintentional", `beforeExit:${code}`);
18895
+ };
18896
+ this.handleExit = (code) => {
18897
+ if (!this._shutdownState)
18898
+ this.setShutdownState(code === 0 ? "intentional" : "unintentional", `exit:${code}`);
18899
+ this.cleanupOpenScopes(code);
18900
+ };
18901
+ this.cleanupOpenScopes = (code) => {
18902
+ if (this._cleaned)
18903
+ return;
18904
+ this._cleaned = true;
18905
+ const openScopes = ExecutorScope_default.getOpenScopes();
18906
+ const scopeCount = openScopes.length;
18907
+ const shutdownState = this._shutdownState;
18908
+ const shutdownDescription = `${shutdownState?.type ?? "intentional"} shutdown (${shutdownState?.reason ?? `exit:${code}`})`;
18909
+ if (scopeCount === 0) {
18910
+ Logger_default.info(`Detected ${shutdownDescription} for ${this._runtimeName}. No executor scopes to clean up.`);
18911
+ return;
18912
+ }
18913
+ Logger_default.warn(`Detected ${shutdownDescription} for ${this._runtimeName}. Cleaning up ${scopeCount} executor scope${scopeCount === 1 ? "" : "s"}.`);
18914
+ ExecutorScope_default.deepClear();
18915
+ };
18916
+ this.setShutdownState = (type, reason) => {
18917
+ if (this._shutdownState)
18918
+ return;
18919
+ this._shutdownState = { type, reason };
18920
+ };
18921
+ this.asError = (reason, error) => {
18922
+ if (error instanceof Error) {
18923
+ const contextualError = new Error(`Unexpected ${reason} during ${this._runtimeName} execution: ${error.message}`);
18924
+ contextualError.stack = error.stack;
18925
+ return contextualError;
18926
+ }
18927
+ return new Error(`Unexpected ${reason} during ${this._runtimeName} execution: ${String(error)}`);
18928
+ };
18929
+ }
18930
+ };
18931
+ var ProcessShutdownManager = new ProcessShutdownManagerClass();
18932
+ var ProcessShutdownManager_default = ProcessShutdownManager;
18933
+
18934
+ // ../../packages/common/src/SecretManager.ts
18935
+ var SecretManagerClass = class {
18936
+ constructor() {
18937
+ /**
18938
+ * If the value is a secret (or .env setting), replace it with the value inside the .env configuration file
18939
+ * Starts with "{" and ends with "}".
18940
+ * e.g. {AWS_ID}
18941
+ */
18942
+ this.replaceSecret = (value) => {
18943
+ if (!value || value.length <= 2 || !value.startsWith("{") || !value.endsWith("}"))
18944
+ return value;
18945
+ const parsedValue = value.slice(1, value.length - 1);
18946
+ return ProcessENVManager_default.getEnvVariable(parsedValue);
18947
+ };
18948
+ }
18949
+ };
18950
+ var SecretManager = new SecretManagerClass();
18951
+ var SecretManager_default = SecretManager;
18952
+
18870
18953
  // ../../packages/common/src/Environment.ts
18871
18954
  var import_fs5 = __toESM(require("fs"), 1);
18872
18955
  var import_crypto = __toESM(require("crypto"), 1);
@@ -19108,7 +19191,16 @@ var ValidatorClass = class {
19108
19191
  }
19109
19192
  const allAvailableFields = [...availableFieldsByProducer.values()].flat();
19110
19193
  for (const field of consumer.fields) {
19111
- if (field.key === "*" || field.fixed || field.copyFrom) continue;
19194
+ const syntheticFieldModes = [field.fixed === true, !!field.copyFrom, field.lineCount === true].filter(Boolean);
19195
+ if (syntheticFieldModes.length > 1)
19196
+ errors.push(`Field "${field.alias ?? field.key}" in consumer "${consumer.name}" can only use one synthetic field mode among "fixed", "copyFrom", and "lineCount".`);
19197
+ if (field.lineCount && field.key === "*")
19198
+ errors.push(`Field "${field.alias ?? field.key}" in consumer "${consumer.name}" cannot use "lineCount" with the wildcard key "*".`);
19199
+ if (field.lineCount && field.transform)
19200
+ errors.push(`Field "${field.alias ?? field.key}" in consumer "${consumer.name}" cannot use "transform" together with "lineCount".`);
19201
+ if (field.lineCount && field.validate && field.validate.length > 0)
19202
+ errors.push(`Field "${field.alias ?? field.key}" in consumer "${consumer.name}" cannot use "validate" together with "lineCount".`);
19203
+ if (field.key === "*" || field.fixed || field.copyFrom || field.lineCount) continue;
19112
19204
  if (field.from) {
19113
19205
  const producerFields = availableFieldsByProducer.get(field.from);
19114
19206
  if (producerFields && !producerFields.includes(field.key))
@@ -19129,6 +19221,8 @@ var ValidatorClass = class {
19129
19221
  const found = precedingFields.find((f) => (f.alias ?? f.key) === field.copyFrom);
19130
19222
  if (!found)
19131
19223
  errors.push(`Field "${field.alias ?? field.key}" uses copyFrom "${field.copyFrom}" but no field with that name/alias exists before it in consumer "${consumer.name}".`);
19224
+ else if (found.lineCount)
19225
+ errors.push(`Field "${field.alias ?? field.key}" in consumer "${consumer.name}" cannot use copyFrom on a field with "lineCount" because line counts are normalized after worker merge.`);
19132
19226
  }
19133
19227
  if (consumer.filters && consumer.filters.length > 0) {
19134
19228
  if (consumer.filters.some((x) => x.sql && x.rule))
@@ -19149,10 +19243,14 @@ var ValidatorClass = class {
19149
19243
  const missingRules = rulesWithMatchingFields.filter((x) => !x.match);
19150
19244
  errors.push(`Filter(s) on member(s) "${missingRules.map((x) => x.rule.rule.member).join(", ")}" is invalid since the member specified is not present in the consumer. Check the member value or add the missing field to the consumer.`);
19151
19245
  }
19246
+ const lineCountRuleMembers = rulesWithMatchingFields.filter((x) => x.match?.lineCount);
19247
+ if (lineCountRuleMembers.length > 0)
19248
+ errors.push(`Consumer "${consumer.name}" cannot use rule-based filters on field(s) with "lineCount": ${lineCountRuleMembers.map((x) => `"${x.rule.rule.member}"`).join(", ")}.`);
19152
19249
  }
19153
19250
  const validateTransformations = (fields) => {
19154
19251
  const errors2 = [];
19155
19252
  const trxsFields = fields.filter((x) => x.transform);
19253
+ const lineCountFieldNames = new Set(fields.filter((x) => x.lineCount).map((x) => x.alias ?? x.key));
19156
19254
  for (const field of trxsFields) {
19157
19255
  const trxToValidate = [];
19158
19256
  if (Array.isArray(field.transform))
@@ -19160,6 +19258,16 @@ var ValidatorClass = class {
19160
19258
  else
19161
19259
  trxToValidate.push(field.transform);
19162
19260
  for (const trans of trxToValidate) {
19261
+ if ("multiplyBy" in trans) {
19262
+ const invalidFields = trans.multiplyBy.fields.filter((x) => lineCountFieldNames.has(x));
19263
+ if (invalidFields.length > 0)
19264
+ errors2.push(`Transformation on field "${field.alias ?? field.key}" cannot reference lineCount field(s) in multiplyBy: ${invalidFields.map((x) => `"${x}"`).join(", ")}.`);
19265
+ }
19266
+ if ("addBy" in trans) {
19267
+ const invalidFields = trans.addBy.fields.filter((x) => lineCountFieldNames.has(x));
19268
+ if (invalidFields.length > 0)
19269
+ errors2.push(`Transformation on field "${field.alias ?? field.key}" cannot reference lineCount field(s) in addBy: ${invalidFields.map((x) => `"${x}"`).join(", ")}.`);
19270
+ }
19163
19271
  if ("combine_fields" in trans) {
19164
19272
  const { combine_fields } = trans;
19165
19273
  if (!combine_fields.fields || combine_fields.fields.length === 0)
@@ -19167,6 +19275,9 @@ var ValidatorClass = class {
19167
19275
  const missingFieldsInConsumer = combine_fields.fields.map((x) => ({ field: x, found: fields.find((k) => (k.alias ?? k.key) === x) })).filter((x) => !x.found);
19168
19276
  if (missingFieldsInConsumer.length > 0)
19169
19277
  errors2.push(`The requested field(s) for a transformation is missing in the consumer -> missing field(s): "${missingFieldsInConsumer.map((x) => x.field).join(", ")}"; field transformation: "${field.alias ?? field.key}";`);
19278
+ const invalidFields = combine_fields.fields.filter((x) => lineCountFieldNames.has(x));
19279
+ if (invalidFields.length > 0)
19280
+ errors2.push(`Transformation on field "${field.alias ?? field.key}" cannot reference lineCount field(s) in combine_fields: ${invalidFields.map((x) => `"${x}"`).join(", ")}.`);
19170
19281
  }
19171
19282
  }
19172
19283
  }
@@ -19189,7 +19300,7 @@ var ValidatorClass = class {
19189
19300
  errors.push(`The export destination "${output.exportDestination}" was not found in the sources.`);
19190
19301
  }
19191
19302
  }
19192
- const dimensionFields = consumer.fields.filter((x) => x.key !== "*" && !x.fixed && !x.copyFrom);
19303
+ const dimensionFields = consumer.fields.filter((x) => x.key !== "*" && !x.fixed && !x.copyFrom && !x.lineCount);
19193
19304
  const dimensionKeys = dimensionFields.map((x) => `${x.from ?? "_default_"}::${x.key}`);
19194
19305
  const duplicateDimensionKeys = dimensionKeys.filter((k, i) => dimensionKeys.indexOf(k) !== i);
19195
19306
  if (duplicateDimensionKeys.length > 0) {
@@ -20514,6 +20625,19 @@ var Helper = {
20514
20625
  };
20515
20626
  var Helper_default = Helper;
20516
20627
 
20628
+ // ../../packages/helper/src/Formatter.ts
20629
+ var Formatter = {
20630
+ bytes: (bytes, decimals = 2) => {
20631
+ if (!+bytes) return "0 Bytes";
20632
+ const k = 1024;
20633
+ const dm = decimals < 0 ? 0 : decimals;
20634
+ const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
20635
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
20636
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
20637
+ }
20638
+ };
20639
+ var Formatter_default = Formatter;
20640
+
20517
20641
  // ../../packages/helper/src/Settings.ts
20518
20642
  var SETTINGS = {
20519
20643
  db: {
@@ -22172,10 +22296,10 @@ var LicenceManager = new LicenceManagerClass();
22172
22296
  var LicenceManager_default = LicenceManager;
22173
22297
 
22174
22298
  // ../../packages/executors/src/ConsumerExecutor.ts
22175
- var import_path19 = __toESM(require("path"));
22176
- var import_fs13 = __toESM(require("fs"));
22299
+ var import_path18 = __toESM(require("path"));
22300
+ var import_fs12 = __toESM(require("fs"));
22177
22301
  var import_readline6 = __toESM(require("readline"));
22178
- var import_promises9 = __toESM(require("fs/promises"));
22302
+ var import_promises8 = __toESM(require("fs/promises"));
22179
22303
  var import_crypto5 = __toESM(require("crypto"));
22180
22304
 
22181
22305
  // ../../packages/engines/src/CryptoEngine.ts
@@ -23363,6 +23487,25 @@ var ConsumerManagerClass = class {
23363
23487
  const expandedFields = convertedFields.flatMap((x) => this.expandField(consumer, x, availableColumns));
23364
23488
  return expandedFields;
23365
23489
  };
23490
+ this.getLineCountFields = (consumer, visibleOnly = false) => {
23491
+ Affirm_default(consumer, "Invalid consumer");
23492
+ return this.getLineCountFieldsFromExpanded(this.getExpandedFields(consumer), visibleOnly);
23493
+ };
23494
+ this.getLineCountFieldsFromExpanded = (fields, visibleOnly = false) => {
23495
+ Affirm_default(fields, "Invalid consumer fields");
23496
+ return fields.filter((field) => this.isLineCountField(field.cField) && (!visibleOnly || !field.cField.hidden));
23497
+ };
23498
+ this.hasLineCountFields = (consumer, visibleOnly = false) => {
23499
+ return this.getLineCountFields(consumer, visibleOnly).length > 0;
23500
+ };
23501
+ this.isSyntheticField = (field) => {
23502
+ Affirm_default(field, "Invalid field");
23503
+ return field.fixed === true || !!field.copyFrom || this.isLineCountField(field);
23504
+ };
23505
+ this.isLineCountField = (field) => {
23506
+ Affirm_default(field, "Invalid field");
23507
+ return field.lineCount === true;
23508
+ };
23366
23509
  this.getAvailableColumns = (consumer) => {
23367
23510
  return ResourcesUtils_default.getAvailableColumns(consumer);
23368
23511
  };
@@ -23415,7 +23558,7 @@ var ConsumerManagerClass = class {
23415
23558
  column = columns.find((x) => x.owner === field.from && x.nameInProducer === field.key);
23416
23559
  } else if (consumer.producers.length === 1 && !field.from) {
23417
23560
  column = columns.find((x) => x.nameInProducer === field.key);
23418
- } else if (!field.fixed && !field.copyFrom) {
23561
+ } else if (!this.isSyntheticField(field)) {
23419
23562
  const matches = columns.filter((x) => x.nameInProducer === field.key);
23420
23563
  Affirm_default(matches.length > 0, `Consumer "${consumer.name}" misconfiguration: the field "${field.key}" is not found in any of the included producers (${consumer.producers.map((x) => x.name).join(", ")})`);
23421
23564
  if (matches.length === 1) {
@@ -23426,14 +23569,14 @@ var ConsumerManagerClass = class {
23426
23569
  column = matches[0];
23427
23570
  }
23428
23571
  if (!column) {
23429
- if (field.fixed === true && Algo_default.hasVal(field.default) || field.copyFrom) {
23572
+ if (field.fixed === true && Algo_default.hasVal(field.default) || this.isSyntheticField(field)) {
23430
23573
  column = {
23431
23574
  aliasInProducer: field.key,
23432
23575
  nameInProducer: field.alias ?? field.key,
23433
23576
  consumerAlias: field.alias ?? field.key,
23434
23577
  consumerKey: field.key,
23435
23578
  owner: field.from,
23436
- dimension: { name: field.key, type: typeof field.default }
23579
+ dimension: { name: field.key, type: this.isLineCountField(field) ? "number" : typeof field.default }
23437
23580
  };
23438
23581
  }
23439
23582
  }
@@ -24603,69 +24746,8 @@ var UsageManager = new UsageManagerClass();
24603
24746
  var UsageManager_default = UsageManager;
24604
24747
 
24605
24748
  // ../../packages/executors/src/OutputExecutor.ts
24606
- var fs18 = __toESM(require("fs"));
24607
-
24608
- // ../../packages/executors/src/ExecutorScope.ts
24749
+ var fs17 = __toESM(require("fs"));
24609
24750
  var import_path17 = __toESM(require("path"));
24610
- var import_fs12 = __toESM(require("fs"));
24611
- var import_promises8 = __toESM(require("fs/promises"));
24612
- var ExecutorScopeClass2 = class {
24613
- constructor() {
24614
- this.WORKERS_FOLDER = "workers";
24615
- this.PRODUCERS_FOLDER = "producers";
24616
- this.getWorkerPath = (scope, workerId) => {
24617
- return import_path17.default.join(
24618
- Constants_default.defaults.REMORA_PATH,
24619
- Constants_default.defaults.PRODUCER_TEMP_FOLDER,
24620
- // A specific execution sits entirely in this folder, so at the end it's safe to delete it entirely
24621
- scope.folder,
24622
- this.WORKERS_FOLDER,
24623
- `${workerId}.dataset`
24624
- );
24625
- };
24626
- this.getProducerPath = (scope, producer, sourceFileKey) => {
24627
- return import_path17.default.join(
24628
- Constants_default.defaults.REMORA_PATH,
24629
- Constants_default.defaults.PRODUCER_TEMP_FOLDER,
24630
- // A specific execution sits entirely in this folder, so at the end it's safe to delete it entirely
24631
- scope.folder,
24632
- this.PRODUCERS_FOLDER,
24633
- producer.name,
24634
- `${sourceFileKey}.dataset`
24635
- );
24636
- };
24637
- this.getMainPath = (scope) => {
24638
- return import_path17.default.join(
24639
- Constants_default.defaults.REMORA_PATH,
24640
- Constants_default.defaults.PRODUCER_TEMP_FOLDER,
24641
- scope.folder,
24642
- "main.dataset"
24643
- );
24644
- };
24645
- this.clearScope = async (scope) => {
24646
- const scopePath = import_path17.default.join(
24647
- Constants_default.defaults.REMORA_PATH,
24648
- Constants_default.defaults.PRODUCER_TEMP_FOLDER,
24649
- scope.folder
24650
- );
24651
- if (import_fs12.default.existsSync(scopePath)) {
24652
- await import_promises8.default.rm(scopePath, { recursive: true, force: true });
24653
- }
24654
- };
24655
- this.ensurePath = (fileUri) => {
24656
- const dir = import_path17.default.dirname(fileUri);
24657
- if (!import_fs12.default.existsSync(dir))
24658
- import_fs12.default.mkdirSync(dir, { recursive: true });
24659
- if (!import_fs12.default.existsSync(fileUri))
24660
- import_fs12.default.writeFileSync(fileUri, "");
24661
- };
24662
- }
24663
- };
24664
- var ExecutorScope2 = new ExecutorScopeClass2();
24665
- var ExecutorScope_default2 = ExecutorScope2;
24666
-
24667
- // ../../packages/executors/src/OutputExecutor.ts
24668
- var import_path18 = __toESM(require("path"));
24669
24751
  var OutputExecutorClass = class {
24670
24752
  constructor() {
24671
24753
  this._getInternalRecordFormat = (consumer) => {
@@ -24709,13 +24791,13 @@ var OutputExecutorClass = class {
24709
24791
  for (const output of consumer.outputs) {
24710
24792
  const destination = Environment_default.getSource(output.exportDestination);
24711
24793
  const driver = await DriverFactory_default.instantiateDestination(destination);
24712
- const currentPath = import_path18.default.dirname(ExecutorScope_default2.getMainPath(scope));
24794
+ const currentPath = import_path17.default.dirname(ExecutorScope_default.getMainPath(scope));
24713
24795
  const destinationName = this._composeFileName(consumer, output, this._getExtension(output));
24714
24796
  Logger_default.log(`Exporting consumer "${consumer.name}" to "${output.exportDestination}" as ${output.format} (${destinationName})`);
24715
- const filenameArray = fs18.readdirSync(currentPath).filter((filename) => filename.includes(".dataset"));
24797
+ const filenameArray = fs17.readdirSync(currentPath).filter((filename) => filename.includes(".dataset"));
24716
24798
  for (const filename in filenameArray) {
24717
24799
  const destinationPath = this.getCompletedPath(destinationName, filename);
24718
- const startingPath = import_path18.default.join(currentPath, filenameArray[filename]);
24800
+ const startingPath = import_path17.default.join(currentPath, filenameArray[filename]);
24719
24801
  if (output.format === internalFormat) {
24720
24802
  results.push(await driver.move(startingPath, destinationPath));
24721
24803
  } else {
@@ -24775,47 +24857,54 @@ var OutputExecutor_default = OutputExecutor;
24775
24857
  var ConsumerExecutorClass = class {
24776
24858
  constructor() {
24777
24859
  this._getWorkPath = (consumer, executionId) => {
24778
- const execFolder = import_path19.default.join(consumer.name, executionId);
24779
- const workPath = import_path19.default.join("./remora", Constants_default.defaults.PRODUCER_TEMP_FOLDER, execFolder, ".dataset");
24860
+ const execFolder = import_path18.default.join(consumer.name, executionId);
24861
+ const workPath = import_path18.default.join("./remora", Constants_default.defaults.PRODUCER_TEMP_FOLDER, execFolder, ".dataset");
24780
24862
  return workPath;
24781
24863
  };
24782
24864
  this._clearWorkPath = async (workPath) => {
24783
24865
  try {
24784
- if (import_fs13.default.existsSync(workPath)) {
24785
- await import_promises9.default.unlink(workPath);
24866
+ if (import_fs12.default.existsSync(workPath)) {
24867
+ await import_promises8.default.unlink(workPath);
24786
24868
  }
24787
24869
  } catch (error) {
24788
24870
  }
24789
24871
  try {
24790
- const dir = import_path19.default.dirname(workPath);
24791
- if (import_fs13.default.existsSync(dir)) {
24792
- await import_promises9.default.rmdir(dir);
24872
+ const dir = import_path18.default.dirname(workPath);
24873
+ if (import_fs12.default.existsSync(dir)) {
24874
+ await import_promises8.default.rmdir(dir);
24793
24875
  }
24794
24876
  } catch (error) {
24795
24877
  }
24796
24878
  };
24797
24879
  this._ensurePath = (pathUri) => {
24798
- const dir = import_path19.default.dirname(pathUri);
24799
- if (!import_fs13.default.existsSync(dir))
24800
- import_fs13.default.mkdirSync(dir, { recursive: true });
24801
- if (!import_fs13.default.existsSync(pathUri))
24802
- import_fs13.default.writeFileSync(pathUri, "");
24880
+ const dir = import_path18.default.dirname(pathUri);
24881
+ if (!import_fs12.default.existsSync(dir))
24882
+ import_fs12.default.mkdirSync(dir, { recursive: true });
24883
+ if (!import_fs12.default.existsSync(pathUri))
24884
+ import_fs12.default.writeFileSync(pathUri, "");
24803
24885
  };
24804
24886
  this.processRecord = (options) => {
24805
24887
  const { consumer, fields, dimensions, producer, record, requestOptions, index: recordIndex } = options;
24806
24888
  for (const field of fields) {
24807
24889
  const { cField } = field;
24808
24890
  const fieldKey = cField.alias ?? cField.key;
24891
+ if (cField.fixed && Algo_default.hasVal(cField.default)) {
24892
+ record[fieldKey] = cField.default;
24893
+ continue;
24894
+ }
24895
+ if (ConsumerManager_default.isLineCountField(cField)) {
24896
+ record[fieldKey] = recordIndex + 1;
24897
+ continue;
24898
+ }
24899
+ if (cField.copyFrom) {
24900
+ record[fieldKey] = record[cField.copyFrom];
24901
+ continue;
24902
+ }
24809
24903
  let dimension;
24810
24904
  try {
24811
24905
  dimension = dimensions.find((x) => x.name === cField.key);
24812
24906
  if (!dimension) {
24813
- if (cField.fixed && Algo_default.hasVal(cField.default))
24814
- record[fieldKey] = cField.default;
24815
- else if (cField.copyFrom)
24816
- record[fieldKey] = record[cField.copyFrom];
24817
- else
24818
- throw new Error(`The requested field "${cField.key}" from the consumer is not present in the underlying producer "${producer.name}" (${dimensions.map((x) => x.name).join(", ")})`);
24907
+ throw new Error(`The requested field "${cField.key}" from the consumer is not present in the underlying producer "${producer.name}" (${dimensions.map((x) => x.name).join(", ")})`);
24819
24908
  }
24820
24909
  } catch (error) {
24821
24910
  const err2 = new Error(`Resolving dimension for field "${fieldKey}" of producer "${producer.name}" failed (index: ${recordIndex}): ${error.message}`, { cause: error });
@@ -24928,11 +25017,69 @@ var ConsumerExecutorClass = class {
24928
25017
  }
24929
25018
  return record;
24930
25019
  };
25020
+ this.processLineCount = async (consumer, datasetPath, executorResults) => {
25021
+ const internalRecordFormat = OutputExecutor_default._getInternalRecordFormat(consumer);
25022
+ const internalFields = ConsumerManager_default.getExpandedFields(consumer);
25023
+ const lineCountFields = ConsumerManager_default.getLineCountFieldsFromExpanded(internalFields, true);
25024
+ if (lineCountFields.length === 0)
25025
+ return 0;
25026
+ const tempWorkPath = datasetPath + "_tmp";
25027
+ const reader = import_fs12.default.createReadStream(datasetPath);
25028
+ const lineReader = import_readline6.default.createInterface({ input: reader, crlfDelay: Infinity });
25029
+ const writer = import_fs12.default.createWriteStream(tempWorkPath);
25030
+ const waitForDrain = () => new Promise((resolve) => writer.once("drain", resolve));
25031
+ let workerIndex = 0;
25032
+ let currentWorker = executorResults[workerIndex] ?? null;
25033
+ let remainingLinesInWorker = currentWorker?.outputCount ?? 0;
25034
+ let processedLineCount = 0;
25035
+ const advanceWorker = () => {
25036
+ while (currentWorker && remainingLinesInWorker <= 0) {
25037
+ workerIndex++;
25038
+ currentWorker = executorResults[workerIndex] ?? null;
25039
+ remainingLinesInWorker = currentWorker?.outputCount ?? 0;
25040
+ }
25041
+ };
25042
+ advanceWorker();
25043
+ for await (const line of lineReader) {
25044
+ advanceWorker();
25045
+ Affirm_default(currentWorker, `Unable to resolve worker metadata while processing lineCount for ${datasetPath}`);
25046
+ const rowOffset = currentWorker.rowOffset ?? 0;
25047
+ if (rowOffset > 0) {
25048
+ const record = this._parseLine(line, internalRecordFormat, internalFields);
25049
+ for (const field of lineCountFields) {
25050
+ const currentValue = Number(record[field.finalKey]);
25051
+ Affirm_default(!Number.isNaN(currentValue), `Invalid lineCount value found while processing ${datasetPath}`);
25052
+ record[field.finalKey] = currentValue + rowOffset;
25053
+ }
25054
+ if (!writer.write(OutputExecutor_default.outputRecord(record, consumer, internalFields) + "\n"))
25055
+ await waitForDrain();
25056
+ } else if (!writer.write(line + "\n")) {
25057
+ await waitForDrain();
25058
+ }
25059
+ remainingLinesInWorker--;
25060
+ processedLineCount++;
25061
+ }
25062
+ lineReader.close();
25063
+ await new Promise((resolve, reject) => {
25064
+ writer.on("close", resolve);
25065
+ writer.on("error", reject);
25066
+ writer.end();
25067
+ });
25068
+ if (!reader.destroyed) {
25069
+ await new Promise((resolve) => {
25070
+ reader.once("close", resolve);
25071
+ reader.destroy();
25072
+ });
25073
+ }
25074
+ await import_promises8.default.unlink(datasetPath);
25075
+ await import_promises8.default.rename(tempWorkPath, datasetPath);
25076
+ return processedLineCount;
25077
+ };
24931
25078
  this.processDistinct = async (datasetPath) => {
24932
- const reader = import_fs13.default.createReadStream(datasetPath);
25079
+ const reader = import_fs12.default.createReadStream(datasetPath);
24933
25080
  const lineReader = import_readline6.default.createInterface({ input: reader, crlfDelay: Infinity });
24934
25081
  const tempWorkPath = datasetPath + "_tmp";
24935
- const writer = import_fs13.default.createWriteStream(tempWorkPath);
25082
+ const writer = import_fs12.default.createWriteStream(tempWorkPath);
24936
25083
  const waitForDrain = () => new Promise((resolve) => writer.once("drain", resolve));
24937
25084
  let newLineCount = 0;
24938
25085
  const seen = /* @__PURE__ */ new Set();
@@ -24957,12 +25104,12 @@ var ConsumerExecutorClass = class {
24957
25104
  reader.destroy();
24958
25105
  });
24959
25106
  }
24960
- await import_promises9.default.unlink(datasetPath);
24961
- await import_promises9.default.rename(tempWorkPath, datasetPath);
25107
+ await import_promises8.default.unlink(datasetPath);
25108
+ await import_promises8.default.rename(tempWorkPath, datasetPath);
24962
25109
  return newLineCount;
24963
25110
  };
24964
25111
  this.processDistinctOn = async (consumer, datasetPath) => {
24965
- const reader = import_fs13.default.createReadStream(datasetPath);
25112
+ const reader = import_fs12.default.createReadStream(datasetPath);
24966
25113
  const lineReader = import_readline6.default.createInterface({ input: reader, crlfDelay: Infinity });
24967
25114
  const { distinctOn } = consumer.options;
24968
25115
  const { keys, resolution } = distinctOn;
@@ -24985,7 +25132,7 @@ var ConsumerExecutorClass = class {
24985
25132
  }
24986
25133
  lineReader.close();
24987
25134
  const tempWorkPath = datasetPath + "_tmp";
24988
- const writer = import_fs13.default.createWriteStream(tempWorkPath);
25135
+ const writer = import_fs12.default.createWriteStream(tempWorkPath);
24989
25136
  const waitForDrain = () => new Promise((resolve) => writer.once("drain", resolve));
24990
25137
  for (const { line } of winners.values()) {
24991
25138
  if (!writer.write(line + "\n"))
@@ -25002,8 +25149,8 @@ var ConsumerExecutorClass = class {
25002
25149
  reader.destroy();
25003
25150
  });
25004
25151
  }
25005
- await import_promises9.default.unlink(datasetPath);
25006
- await import_promises9.default.rename(tempWorkPath, datasetPath);
25152
+ await import_promises8.default.unlink(datasetPath);
25153
+ await import_promises8.default.rename(tempWorkPath, datasetPath);
25007
25154
  return winners.size;
25008
25155
  };
25009
25156
  this.processPivot = async (consumer, datasetPath) => {
@@ -25015,7 +25162,7 @@ var ConsumerExecutorClass = class {
25015
25162
  if (!pivotValues) {
25016
25163
  pivotValues = [];
25017
25164
  const discoverySet = /* @__PURE__ */ new Set();
25018
- const discoverReader = import_fs13.default.createReadStream(datasetPath);
25165
+ const discoverReader = import_fs12.default.createReadStream(datasetPath);
25019
25166
  const discoverLineReader = import_readline6.default.createInterface({ input: discoverReader, crlfDelay: Infinity });
25020
25167
  for await (const line of discoverLineReader) {
25021
25168
  const record = this._parseLine(line, internalRecordFormat, internalFields);
@@ -25034,7 +25181,7 @@ var ConsumerExecutorClass = class {
25034
25181
  }
25035
25182
  }
25036
25183
  const groups = /* @__PURE__ */ new Map();
25037
- const reader = import_fs13.default.createReadStream(datasetPath);
25184
+ const reader = import_fs12.default.createReadStream(datasetPath);
25038
25185
  const lineReader = import_readline6.default.createInterface({ input: reader, crlfDelay: Infinity });
25039
25186
  for await (const line of lineReader) {
25040
25187
  const record = this._parseLine(line, internalRecordFormat, internalFields);
@@ -25062,7 +25209,7 @@ var ConsumerExecutorClass = class {
25062
25209
  ...pivotValues.map((pv) => ({ cField: { key: columnPrefix + pv }, finalKey: columnPrefix + pv }))
25063
25210
  ];
25064
25211
  const tempWorkPath = datasetPath + "_tmp";
25065
- const writer = import_fs13.default.createWriteStream(tempWorkPath);
25212
+ const writer = import_fs12.default.createWriteStream(tempWorkPath);
25066
25213
  const waitForDrain = () => new Promise((resolve) => writer.once("drain", resolve));
25067
25214
  let outputCount = 0;
25068
25215
  for (const { rowRecord, cells } of groups.values()) {
@@ -25108,8 +25255,8 @@ var ConsumerExecutorClass = class {
25108
25255
  reader.destroy();
25109
25256
  });
25110
25257
  }
25111
- await import_promises9.default.unlink(datasetPath);
25112
- await import_promises9.default.rename(tempWorkPath, datasetPath);
25258
+ await import_promises8.default.unlink(datasetPath);
25259
+ await import_promises8.default.rename(tempWorkPath, datasetPath);
25113
25260
  return outputCount;
25114
25261
  };
25115
25262
  this._parseLine = (line, format2, fields) => {
@@ -25155,7 +25302,7 @@ var ConsumerExecutorClass = class {
25155
25302
  for (const fieldKey of uniqueFieldKeys) {
25156
25303
  fieldValueSets.set(fieldKey, /* @__PURE__ */ new Set());
25157
25304
  }
25158
- const reader = import_fs13.default.createReadStream(datasetPath);
25305
+ const reader = import_fs12.default.createReadStream(datasetPath);
25159
25306
  const lineReader = import_readline6.default.createInterface({ input: reader, crlfDelay: Infinity });
25160
25307
  for await (const line of lineReader) {
25161
25308
  rowCount++;
@@ -25203,7 +25350,7 @@ var ConsumerExecutor = new ConsumerExecutorClass();
25203
25350
  var ConsumerExecutor_default = ConsumerExecutor;
25204
25351
 
25205
25352
  // ../../packages/executors/src/ProducerExecutor.ts
25206
- var import_path20 = __toESM(require("path"));
25353
+ var import_path19 = __toESM(require("path"));
25207
25354
  var ProducerExecutorClass = class {
25208
25355
  constructor() {
25209
25356
  this.ready = async (producer, scope) => {
@@ -25229,7 +25376,7 @@ var ProducerExecutorClass = class {
25229
25376
  counter = performance.now();
25230
25377
  for (const dimension of dimensions) {
25231
25378
  if (dimension.prodDimension.sourceFilename === true)
25232
- record[dimension.name] = import_path20.default.basename(chunk.fileUri);
25379
+ record[dimension.name] = import_path19.default.basename(chunk.fileUri);
25233
25380
  const maskType = ProducerManager_default.getMask(dimension.prodDimension);
25234
25381
  if (Algo_default.hasVal(maskType))
25235
25382
  record[dimension.name] = CryptoEngine_default.hashValue(maskType, record[dimension.name]?.toString(), dimension.prodDimension.type);
@@ -25260,14 +25407,13 @@ var ExecutorPerformance = class {
25260
25407
  var ExecutorPerformance_default = ExecutorPerformance;
25261
25408
 
25262
25409
  // ../../packages/executors/src/ExecutorOrchestrator.ts
25263
- var import_os = __toESM(require("os"));
25264
- var import_fs14 = __toESM(require("fs"));
25265
- var import_promises10 = __toESM(require("fs/promises"));
25410
+ var import_fs13 = __toESM(require("fs"));
25411
+ var import_promises9 = __toESM(require("fs/promises"));
25266
25412
  var import_path21 = __toESM(require("path"));
25267
25413
  var import_workerpool = __toESM(require("workerpool"));
25268
25414
 
25269
25415
  // ../../packages/executors/src/ExecutorWriter.ts
25270
- var fs20 = __toESM(require("fs"));
25416
+ var fs19 = __toESM(require("fs"));
25271
25417
  var import_readline7 = __toESM(require("readline"));
25272
25418
  var ExecutorWriter = class {
25273
25419
  constructor() {
@@ -25284,11 +25430,11 @@ var ExecutorWriter = class {
25284
25430
  };
25285
25431
  this.splitBySize = async (scope, sourcePath) => {
25286
25432
  const maxOutputFileSize = scope.limitFileSize * this.FAKE_GB;
25287
- const readStream = fs20.createReadStream(sourcePath);
25433
+ const readStream = fs19.createReadStream(sourcePath);
25288
25434
  const reader = import_readline7.default.createInterface({ input: readStream, crlfDelay: Infinity });
25289
25435
  let writerIndex = 0;
25290
25436
  let destPath = this.getCompletedPath(sourcePath, writerIndex);
25291
- let writeStream = fs20.createWriteStream(destPath, { flags: "a" });
25437
+ let writeStream = fs19.createWriteStream(destPath, { flags: "a" });
25292
25438
  const waitForDrain = () => new Promise((resolve) => writeStream.once("drain", resolve));
25293
25439
  for await (const line of reader) {
25294
25440
  if (readStream.bytesRead > maxOutputFileSize * (writerIndex + 1)) {
@@ -25299,7 +25445,7 @@ var ExecutorWriter = class {
25299
25445
  });
25300
25446
  writerIndex++;
25301
25447
  destPath = this.getCompletedPath(sourcePath, writerIndex);
25302
- writeStream = fs20.createWriteStream(destPath, { flags: "a" });
25448
+ writeStream = fs19.createWriteStream(destPath, { flags: "a" });
25303
25449
  }
25304
25450
  if (!writeStream.write(line + "\n"))
25305
25451
  await waitForDrain();
@@ -25309,7 +25455,7 @@ var ExecutorWriter = class {
25309
25455
  writeStream.on("finish", resolve);
25310
25456
  writeStream.on("error", reject);
25311
25457
  });
25312
- await fs20.promises.unlink(sourcePath);
25458
+ await fs19.promises.unlink(sourcePath);
25313
25459
  };
25314
25460
  /**
25315
25461
  * Manage the Writestream for main.dataset
@@ -25366,7 +25512,7 @@ var ExecutorWriter = class {
25366
25512
  var ExecutorWriter_default = ExecutorWriter;
25367
25513
 
25368
25514
  // ../../packages/executors/src/ExecutorOrchestrator.ts
25369
- var import_promises11 = require("stream/promises");
25515
+ var import_promises10 = require("stream/promises");
25370
25516
 
25371
25517
  // ../../packages/executors/src/cli_progress/ExecutorProgress2.ts
25372
25518
  var ExecutorProgress2 = class {
@@ -25402,19 +25548,111 @@ var ExecutorProgress2 = class {
25402
25548
  };
25403
25549
  var ExecutorProgress2_default = ExecutorProgress2;
25404
25550
 
25551
+ // ../../packages/executors/src/OrchestratorHelper.ts
25552
+ var import_os = __toESM(require("os"));
25553
+ var import_path20 = __toESM(require("path"));
25554
+ var OrchestratorHelper = {
25555
+ getMemoryUsage: () => {
25556
+ const processMemory = process.memoryUsage();
25557
+ const freeSystemMemory = import_os.default.freemem();
25558
+ return {
25559
+ /**
25560
+ * resident set size (heap + code + stack)
25561
+ */
25562
+ rss: Formatter_default.bytes(processMemory.rss),
25563
+ heapUsed: Formatter_default.bytes(processMemory.heapUsed),
25564
+ heapTotal: Formatter_default.bytes(processMemory.heapTotal),
25565
+ heapPercent: Algo_default.round(processMemory.heapUsed / processMemory.heapTotal, 1),
25566
+ external: Formatter_default.bytes(processMemory.external),
25567
+ free: Formatter_default.bytes(freeSystemMemory)
25568
+ };
25569
+ },
25570
+ formatMemoryUsage: () => {
25571
+ return `Memory [total: ${OrchestratorHelper.getMemoryUsage().rss} - heap: ${OrchestratorHelper.getMemoryUsage().heapPercent}%]`;
25572
+ },
25573
+ computeFinalResult: (tracker, executorResults, executionId, resultUri) => {
25574
+ const result = {
25575
+ cycles: Algo_default.max(executorResults.map((x) => x.cycles)),
25576
+ elapsedMS: Algo_default.sum(executorResults.map((x) => x.elapsedMS)),
25577
+ inputCount: Algo_default.sum(executorResults.map((x) => x.inputCount)),
25578
+ outputCount: Algo_default.sum(executorResults.map((x) => x.outputCount)),
25579
+ workerCount: executorResults.length,
25580
+ executionId,
25581
+ resultUri,
25582
+ operations: {}
25583
+ };
25584
+ for (const res of executorResults) {
25585
+ for (const opKey of Object.keys(res.operations)) {
25586
+ const op = res.operations[opKey];
25587
+ let label = result.operations[opKey];
25588
+ if (!label) {
25589
+ result.operations[opKey] = { avg: -1, max: -1, min: -1, elapsedMS: [] };
25590
+ label = result.operations[opKey];
25591
+ }
25592
+ label.elapsedMS.push(op.elapsedMS);
25593
+ }
25594
+ for (const opKey of Object.keys(result.operations)) {
25595
+ const operation = result.operations[opKey];
25596
+ if (operation.elapsedMS.length > 0) {
25597
+ operation.min = Math.min(...operation.elapsedMS);
25598
+ operation.max = Math.max(...operation.elapsedMS);
25599
+ operation.avg = Algo_default.mean(operation.elapsedMS);
25600
+ }
25601
+ }
25602
+ }
25603
+ const trackerOperations = tracker.getOperations();
25604
+ for (const opKey of Object.keys(trackerOperations)) {
25605
+ const trackerOp = trackerOperations[opKey];
25606
+ const value = trackerOp.elapsedMS;
25607
+ if (!result.operations[opKey]) {
25608
+ result.operations[opKey] = { avg: value, max: value, min: value, elapsedMS: [] };
25609
+ }
25610
+ result.operations[opKey].elapsedMS.push(value);
25611
+ }
25612
+ return result;
25613
+ },
25614
+ /**
25615
+ * Returns the path to the worker thread file in the build (different between dev, cli and prod (docker)).
25616
+ * IMPORTANT!: when moving this (OrchestratorHelper.ts) file, or the workers output, you have to make sure to update these paths
25617
+ */
25618
+ getPhysicalWorkerPath: () => {
25619
+ const currentDir = __dirname;
25620
+ if (ProcessENVManager_default.getEnvVariable("NODE_ENV") === "dev" || ProcessENVManager_default.getEnvVariable("NODE_ENV") === "development")
25621
+ return import_path20.default.resolve("./.build/workers");
25622
+ const forcedPath = ProcessENVManager_default.getEnvVariable("REMORA_WORKERS_PATH");
25623
+ if (forcedPath && forcedPath.length > 0)
25624
+ return import_path20.default.join(__dirname, forcedPath);
25625
+ if (!currentDir.includes(".build")) {
25626
+ return import_path20.default.join(__dirname, "../workers");
25627
+ } else {
25628
+ return import_path20.default.resolve("./.build/workers");
25629
+ }
25630
+ },
25631
+ getParallelWorkerCount: () => {
25632
+ const cpuBoundWorkers = Math.max(1, Math.floor(import_os.default.cpus().length * 0.7));
25633
+ const totalMemoryMB = Math.floor(import_os.default.totalmem() / (1024 * 1024));
25634
+ const reservedMemoryMB = Constants_default.defaults.MIN_RUNTIME_HEAP_MB * 2;
25635
+ const availableMemoryForWorkersMB = Math.max(Constants_default.defaults.MIN_RUNTIME_HEAP_MB, totalMemoryMB - reservedMemoryMB);
25636
+ const memoryBoundWorkers = Math.max(1, Math.floor(availableMemoryForWorkersMB / Constants_default.defaults.MIN_RUNTIME_HEAP_MB));
25637
+ return Math.min(cpuBoundWorkers, Constants_default.defaults.MAX_THREAD_COUNT, memoryBoundWorkers);
25638
+ }
25639
+ };
25640
+ var OrchestratorHelper_default = OrchestratorHelper;
25641
+
25405
25642
  // ../../packages/executors/src/ExecutorOrchestrator.ts
25406
25643
  var ExecutorOrchestratorClass = class {
25407
25644
  constructor() {
25408
- this.createPool = () => {
25645
+ this.createPool = (maxWorkers) => {
25409
25646
  const options = {
25647
+ maxWorkers,
25410
25648
  workerThreadOpts: {
25411
25649
  resourceLimits: {
25412
25650
  maxOldGenerationSizeMb: Constants_default.defaults.MIN_RUNTIME_HEAP_MB
25413
25651
  }
25414
25652
  }
25415
25653
  };
25416
- const workerPath = this._getWorkerPath();
25417
- Logger_default.log(`Initializing worker pool from ${workerPath} (heap limit: ${Constants_default.defaults.MIN_RUNTIME_HEAP_MB}MB)`);
25654
+ const workerPath = OrchestratorHelper_default.getPhysicalWorkerPath();
25655
+ Logger_default.log(`Initializing worker pool from ${workerPath} (workers: ${maxWorkers}, heap limit: ${Constants_default.defaults.MIN_RUNTIME_HEAP_MB}MB) | ${OrchestratorHelper_default.formatMemoryUsage()}`);
25418
25656
  return import_workerpool.default.pool(import_path21.default.join(workerPath, "ExecutorWorker.js"), options);
25419
25657
  };
25420
25658
  this.launch = async (request) => {
@@ -25428,11 +25666,12 @@ var ExecutorOrchestratorClass = class {
25428
25666
  const _progress = new ExecutorProgress2_default(logProgress);
25429
25667
  const { usageId } = UsageManager_default.startUsage(consumer, details);
25430
25668
  const scope = { id: usageId, folder: `${consumer.name}_${usageId}`, workersId: [], limitFileSize: consumer.maximumFileSize };
25431
- const pool = this.createPool();
25669
+ const expandedFields = ConsumerManager_default.getExpandedFields(consumer);
25670
+ let activePool = null;
25432
25671
  try {
25433
25672
  const start = performance.now();
25434
25673
  const executorResults = [];
25435
- Logger_default.log(`[${usageId}] Launching consumer "${consumer.name}" (invoked by: ${details.invokedBy}, user: ${details.user?.name ?? "unknown"}, producer(s): ${consumer.producers.length})`);
25674
+ Logger_default.log(`[${usageId}] Launching consumer "${consumer.name}" (invoked by: ${details.invokedBy}, user: ${details.user?.name ?? "unknown"}, producer(s): ${consumer.producers.length}) | ${OrchestratorHelper_default.formatMemoryUsage()}`);
25436
25675
  let counter = performance.now();
25437
25676
  _progress.update({ phase: "Preparing source data", progress: 0 });
25438
25677
  let sourceFilesByProducer = await this.readySourceFiles(consumer, scope);
@@ -25448,10 +25687,10 @@ var ExecutorOrchestratorClass = class {
25448
25687
  let globalWorkerIndex = 0;
25449
25688
  for (const pair of sourceFilesByProducer) {
25450
25689
  const { prod, cProd, response } = pair;
25451
- if (!import_fs14.default.existsSync(response.files[0].fullUri)) {
25452
- if (!cProd.isOptional)
25690
+ if (!import_fs13.default.existsSync(response.files[0].fullUri)) {
25691
+ if (!cProd.isOptional) {
25453
25692
  throw new Error(`Expected data file ${response.files[0].fullUri} of producer ${prod.name} in consumer ${consumer.name} is missing.`);
25454
- else if (cProd.isOptional === true) {
25693
+ } else if (cProd.isOptional === true) {
25455
25694
  Logger_default.log(`[${usageId}] Producer "${prod.name}": data file missing but marked optional, skipping`);
25456
25695
  continue;
25457
25696
  }
@@ -25464,35 +25703,42 @@ var ExecutorOrchestratorClass = class {
25464
25703
  for (const [fileIndex, file] of response.files.entries()) {
25465
25704
  const chunks = ExecutorOrchestrator.scopeWork(file.fullUri);
25466
25705
  const workerThreads = [];
25467
- Logger_default.log(`[${usageId}] Producer "${prod.name}" file ${fileIndex + 1}/${totalFiles}: split into ${chunks.length} chunk(s)`);
25468
- for (const chunk of chunks) {
25469
- const workerId = `${usageId}_${globalWorkerIndex}`;
25470
- globalWorkerIndex++;
25471
- const workerData = {
25472
- producer: prod,
25473
- chunk,
25474
- consumer,
25475
- prodDimensions,
25476
- workerId,
25477
- scope,
25478
- options,
25479
- loggerConfig: Logger_default.getConfig()
25480
- };
25481
- scope.workersId.push(workerId);
25482
- 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)`);
25483
- workerThreads.push(pool.exec("executor", [workerData], {
25484
- on: (payload) => this.onWorkAdvanced(payload, workerId, _progress, totalBytesToProcess, bytesProcessedByWorker)
25485
- }).catch((error) => {
25486
- Logger_default.error(error);
25487
- return null;
25488
- }));
25706
+ activePool = this.createPool(chunks.length);
25707
+ Logger_default.log(`[${usageId}] Producer "${prod.name}" file ${fileIndex + 1}/${totalFiles}: split into ${chunks.length} chunk(s) | ${OrchestratorHelper_default.formatMemoryUsage()}`);
25708
+ try {
25709
+ for (const chunk of chunks) {
25710
+ const workerId = `${usageId}_${globalWorkerIndex}`;
25711
+ globalWorkerIndex++;
25712
+ const workerData = {
25713
+ producer: prod,
25714
+ chunk,
25715
+ consumer,
25716
+ prodDimensions,
25717
+ workerId,
25718
+ scope,
25719
+ options,
25720
+ loggerConfig: Logger_default.getConfig()
25721
+ };
25722
+ scope.workersId.push(workerId);
25723
+ 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)`);
25724
+ workerThreads.push(activePool.exec("executor", [workerData], {
25725
+ on: (payload) => this.onWorkAdvanced(payload, workerId, _progress, totalBytesToProcess, bytesProcessedByWorker)
25726
+ }).catch((error) => {
25727
+ Logger_default.error(error);
25728
+ return null;
25729
+ }));
25730
+ }
25731
+ Logger_default.log(`[${usageId}] Waiting for ${workerThreads.length} worker(s) to complete | ${OrchestratorHelper_default.formatMemoryUsage()}`);
25732
+ const fileExecutorResults = await Promise.all(workerThreads);
25733
+ this.assignChunkRowMetadata(chunks, fileExecutorResults);
25734
+ executorResults.push(...fileExecutorResults);
25735
+ Logger_default.log(`[${usageId}] All ${workerThreads.length} worker(s) finished for producer "${prod.name}" file ${fileIndex + 1}/${totalFiles} | ${OrchestratorHelper_default.formatMemoryUsage()}`);
25736
+ } finally {
25737
+ await activePool.terminate();
25738
+ activePool = null;
25489
25739
  }
25490
- Logger_default.log(`[${usageId}] Waiting for ${workerThreads.length} worker(s) to complete`);
25491
- executorResults.push(...await Promise.all(workerThreads));
25492
- Logger_default.log(`[${usageId}] All ${workerThreads.length} worker(s) finished for producer "${prod.name}" file ${fileIndex + 1}/${totalFiles}`);
25493
25740
  }
25494
25741
  }
25495
- await pool.terminate();
25496
25742
  _progress.update({ phase: "Processing data", progress: 1 });
25497
25743
  if (executorResults.some((x) => !Algo_default.hasVal(x)))
25498
25744
  throw new Error(`${executorResults.filter((x) => !Algo_default.hasVal(x)).length} worker(s) failed to produce valid results`);
@@ -25507,7 +25753,7 @@ var ExecutorOrchestratorClass = class {
25507
25753
  if (consumer.options?.distinct === true) {
25508
25754
  Logger_default.log(`[${usageId}] Running unified distinct pass across merged workers`);
25509
25755
  counter = performance.now();
25510
- const unifiedOutputCount = await ConsumerExecutor_default.processDistinct(ExecutorScope_default2.getMainPath(scope));
25756
+ const unifiedOutputCount = await ConsumerExecutor_default.processDistinct(ExecutorScope_default.getMainPath(scope));
25511
25757
  tracker.measure("process-distinct:main", performance.now() - counter);
25512
25758
  postOperation.totalOutputCount = unifiedOutputCount;
25513
25759
  Logger_default.log(`[${usageId}] Distinct pass complete: ${unifiedOutputCount} unique rows in ${Math.round(performance.now() - counter)}ms`);
@@ -25515,24 +25761,31 @@ var ExecutorOrchestratorClass = class {
25515
25761
  if (consumer.options?.distinctOn) {
25516
25762
  Logger_default.log(`[${usageId}] Running unified distinctOn pass (${consumer.options.distinctOn})`);
25517
25763
  counter = performance.now();
25518
- const unifiedOutputCount = await ConsumerExecutor_default.processDistinctOn(consumer, ExecutorScope_default2.getMainPath(scope));
25764
+ const unifiedOutputCount = await ConsumerExecutor_default.processDistinctOn(consumer, ExecutorScope_default.getMainPath(scope));
25519
25765
  tracker.measure("process-distinct-on:main", performance.now() - counter);
25520
25766
  postOperation.totalOutputCount = unifiedOutputCount;
25521
25767
  Logger_default.log(`[${usageId}] DistinctOn pass complete: ${unifiedOutputCount} rows in ${Math.round(performance.now() - counter)}ms`);
25522
25768
  }
25523
- }
25524
- if (consumer.options?.pivot) {
25525
- Logger_default.log(`[${usageId}] Running pivot operation`);
25526
- counter = performance.now();
25527
- const unifiedOutputCount = await ConsumerExecutor_default.processPivot(consumer, ExecutorScope_default2.getMainPath(scope));
25528
- tracker.measure("process-pivot:main", performance.now() - counter);
25529
- postOperation.totalOutputCount = unifiedOutputCount;
25530
- Logger_default.log(`[${usageId}] Pivot complete: ${unifiedOutputCount} rows in ${Math.round(performance.now() - counter)}ms`);
25769
+ if (consumer.options?.pivot) {
25770
+ Logger_default.log(`[${usageId}] Running pivot operation`);
25771
+ counter = performance.now();
25772
+ const unifiedOutputCount = await ConsumerExecutor_default.processPivot(consumer, ExecutorScope_default.getMainPath(scope));
25773
+ tracker.measure("process-pivot:main", performance.now() - counter);
25774
+ postOperation.totalOutputCount = unifiedOutputCount;
25775
+ Logger_default.log(`[${usageId}] Pivot complete: ${unifiedOutputCount} rows in ${Math.round(performance.now() - counter)}ms`);
25776
+ }
25777
+ if (ConsumerManager_default.hasLineCountFields(consumer, true)) {
25778
+ Logger_default.log(`[${usageId}] Running unified lineCount pass across merged workers`);
25779
+ counter = performance.now();
25780
+ const processedLineCount = await ConsumerExecutor_default.processLineCount(consumer, ExecutorScope_default.getMainPath(scope), executorResults);
25781
+ tracker.measure("process-line-count:main", performance.now() - counter);
25782
+ Logger_default.log(`[${usageId}] LineCount pass complete: normalized ${processedLineCount} rows in ${Math.round(performance.now() - counter)}ms`);
25783
+ }
25531
25784
  }
25532
25785
  if (consumer.validate && consumer.validate.length > 0) {
25533
25786
  Logger_default.log(`[${usageId}] Running dataset-level validations`);
25534
25787
  counter = performance.now();
25535
- const validationResults = await ConsumerExecutor_default.processDatasetValidation(consumer, ExecutorScope_default2.getMainPath(scope));
25788
+ const validationResults = await ConsumerExecutor_default.processDatasetValidation(consumer, ExecutorScope_default.getMainPath(scope));
25536
25789
  tracker.measure("dataset-validation", performance.now() - counter);
25537
25790
  for (const result of validationResults) {
25538
25791
  if (result.onFail === "fail") {
@@ -25549,7 +25802,7 @@ var ExecutorOrchestratorClass = class {
25549
25802
  Logger_default.log(`[${usageId}] Splitting output by size limit (${scope.limitFileSize})`);
25550
25803
  counter = performance.now();
25551
25804
  const writer = new ExecutorWriter_default();
25552
- await writer.splitBySize(scope, ExecutorScope_default2.getMainPath(scope));
25805
+ await writer.splitBySize(scope, ExecutorScope_default.getMainPath(scope));
25553
25806
  tracker.measure("split-by-size", performance.now() - counter);
25554
25807
  Logger_default.log(`[${usageId}] Split complete in ${Math.round(performance.now() - counter)}ms`);
25555
25808
  }
@@ -25557,7 +25810,7 @@ var ExecutorOrchestratorClass = class {
25557
25810
  _progress.update({ phase: "Exporting result", progress: 0 });
25558
25811
  counter = performance.now();
25559
25812
  Logger_default.log(`[${usageId}] Exporting results to ${consumer.outputs.length} output(s)`);
25560
- const exportRes = await OutputExecutor_default.exportResult(consumer, ConsumerManager_default.getExpandedFields(consumer), scope);
25813
+ const exportRes = await OutputExecutor_default.exportResult(consumer, expandedFields, scope);
25561
25814
  tracker.measure("export-result", performance.now() - counter);
25562
25815
  Logger_default.log(`[${usageId}] Export complete in ${Math.round(performance.now() - counter)}ms (key: ${exportRes.key})`);
25563
25816
  _progress.update({ phase: "Exporting result", progress: 1 });
@@ -25568,9 +25821,9 @@ var ExecutorOrchestratorClass = class {
25568
25821
  tracker.measure("on-success-actions", performance.now() - counter);
25569
25822
  Logger_default.log(`[${usageId}] On-success actions complete in ${Math.round(performance.now() - counter)}ms`);
25570
25823
  }
25571
- Logger_default.log(`[${usageId}] Starting cleanup operations`);
25824
+ Logger_default.log(`[${usageId}] Starting cleanup operations | ${OrchestratorHelper_default.formatMemoryUsage()}`);
25572
25825
  await this.performCleanupOperations(scope, tracker);
25573
- const finalResult = this.computeFinalResult(tracker, executorResults, usageId, exportRes.key);
25826
+ const finalResult = OrchestratorHelper_default.computeFinalResult(tracker, executorResults, usageId, exportRes.key);
25574
25827
  finalResult.elapsedMS = performance.now() - start;
25575
25828
  if (Algo_default.hasVal(postOperation.totalOutputCount))
25576
25829
  finalResult.outputCount = postOperation.totalOutputCount;
@@ -25579,9 +25832,10 @@ var ExecutorOrchestratorClass = class {
25579
25832
  await Logger_default.flush();
25580
25833
  return finalResult;
25581
25834
  } catch (error) {
25582
- Logger_default.log(`[${usageId}] Consumer "${consumer.name}" failed: ${Helper_default.asError(error).message}`);
25835
+ Logger_default.log(`[${usageId}] Consumer "${consumer.name}" failed: ${Helper_default.asError(error).message} | ${OrchestratorHelper_default.formatMemoryUsage()}`);
25583
25836
  Logger_default.error(error);
25584
- await pool.terminate();
25837
+ if (activePool)
25838
+ await activePool.terminate();
25585
25839
  await ConsumerOnFinishManager_default.onConsumerError(consumer, usageId);
25586
25840
  Logger_default.log(`[${usageId}] Running cleanup after failure`);
25587
25841
  await this.performCleanupOperations(scope, tracker);
@@ -25596,18 +25850,17 @@ var ExecutorOrchestratorClass = class {
25596
25850
  * Returns a single chunk for small files where parallelism overhead isn't worth it.
25597
25851
  */
25598
25852
  this.scopeWork = (fileUri, numChunks) => {
25599
- const fileSize = import_fs14.default.statSync(fileUri).size;
25853
+ const fileSize = import_fs13.default.statSync(fileUri).size;
25600
25854
  if (fileSize === 0) return [];
25601
25855
  if (fileSize < Constants_default.defaults.MIN_FILE_SIZE_FOR_PARALLEL) {
25602
25856
  return [{ start: 0, end: fileSize, isFirstChunk: true, fileUri, index: 0 }];
25603
25857
  }
25604
- const availableCores = Math.max(1, Math.floor(import_os.default.cpus().length * 0.7));
25605
- const cpus = numChunks ?? Math.min(availableCores, Constants_default.defaults.MAX_THREAD_COUNT);
25858
+ const targetWorkers = numChunks ?? OrchestratorHelper_default.getParallelWorkerCount();
25606
25859
  const maxChunksBySize = Math.floor(fileSize / Constants_default.defaults.MIN_CHUNK_SIZE);
25607
- const effectiveChunks = Math.min(cpus, maxChunksBySize);
25860
+ const effectiveChunks = Math.min(targetWorkers, maxChunksBySize);
25608
25861
  if (effectiveChunks <= 1) return [{ start: 0, end: fileSize, isFirstChunk: true, fileUri, index: 0 }];
25609
25862
  const targetChunkSize = Math.floor(fileSize / effectiveChunks);
25610
- const fd = import_fs14.default.openSync(fileUri, "r");
25863
+ const fd = import_fs13.default.openSync(fileUri, "r");
25611
25864
  try {
25612
25865
  const offsets = [];
25613
25866
  let currentStart = 0;
@@ -25625,7 +25878,23 @@ var ExecutorOrchestratorClass = class {
25625
25878
  }
25626
25879
  return offsets;
25627
25880
  } finally {
25628
- import_fs14.default.closeSync(fd);
25881
+ import_fs13.default.closeSync(fd);
25882
+ }
25883
+ };
25884
+ this.assignChunkRowMetadata = (chunks, executorResults) => {
25885
+ const validResults = executorResults.filter((result) => Algo_default.hasVal(result));
25886
+ const resultsByChunkIndex = new Map(validResults.map((result) => [result.chunkIndex, result]));
25887
+ let nextRowOffset = 0;
25888
+ for (const chunk of chunks) {
25889
+ const result = resultsByChunkIndex.get(chunk.index);
25890
+ if (!result)
25891
+ continue;
25892
+ const rowCount = result.inputCount;
25893
+ chunk.rowOffset = nextRowOffset;
25894
+ chunk.rowCount = rowCount;
25895
+ result.rowOffset = nextRowOffset;
25896
+ result.rowCount = rowCount;
25897
+ nextRowOffset += rowCount;
25629
25898
  }
25630
25899
  };
25631
25900
  /**
@@ -25638,7 +25907,7 @@ var ExecutorOrchestratorClass = class {
25638
25907
  let currentPos = position;
25639
25908
  while (currentPos < fileSize) {
25640
25909
  const bytesToRead = Math.min(BUFFER_SIZE, fileSize - currentPos);
25641
- const bytesRead = import_fs14.default.readSync(fd, buffer, 0, bytesToRead, currentPos);
25910
+ const bytesRead = import_fs13.default.readSync(fd, buffer, 0, bytesToRead, currentPos);
25642
25911
  if (bytesRead === 0) break;
25643
25912
  for (let i = 0; i < bytesRead; i++) {
25644
25913
  if (buffer[i] === 10) {
@@ -25702,21 +25971,21 @@ var ExecutorOrchestratorClass = class {
25702
25971
  startRow: prod.settings.startRow,
25703
25972
  startColumn: prod.settings.startColumn
25704
25973
  });
25705
- await (0, import_promises11.pipeline)(
25974
+ await (0, import_promises10.pipeline)(
25706
25975
  xlsCsvStream,
25707
- import_fs14.default.createWriteStream(decodedPath)
25976
+ import_fs13.default.createWriteStream(decodedPath)
25708
25977
  );
25709
- const fileStats = await import_promises10.default.stat(decodedPath);
25978
+ const fileStats = await import_promises9.default.stat(decodedPath);
25710
25979
  decodedFiles.push({ fullUri: decodedPath, fileSize: fileStats.size });
25711
25980
  decodedCount++;
25712
25981
  continue;
25713
25982
  }
25714
25983
  if (inferredType === "XML") {
25715
- const fileContent = await import_promises10.default.readFile(file.fullUri, "utf-8");
25984
+ const fileContent = await import_promises9.default.readFile(file.fullUri, "utf-8");
25716
25985
  const jsonData = XMLParser_default.xmlToJson(fileContent);
25717
25986
  const records = normalizeXmlRows(jsonData);
25718
25987
  if (records.length === 0) {
25719
- await import_promises10.default.writeFile(decodedPath, "", "utf-8");
25988
+ await import_promises9.default.writeFile(decodedPath, "", "utf-8");
25720
25989
  } else {
25721
25990
  const columns = [];
25722
25991
  for (const record of records) {
@@ -25732,9 +26001,9 @@ var ExecutorOrchestratorClass = class {
25732
26001
  const row = columns.map((column) => csvSafeValue(record[column]));
25733
26002
  lines.push(CSVParser_default.stringifyRow(row));
25734
26003
  }
25735
- await import_promises10.default.writeFile(decodedPath, lines.join("\n"), "utf-8");
26004
+ await import_promises9.default.writeFile(decodedPath, lines.join("\n"), "utf-8");
25736
26005
  }
25737
- const fileStats = await import_promises10.default.stat(decodedPath);
26006
+ const fileStats = await import_promises9.default.stat(decodedPath);
25738
26007
  decodedFiles.push({ fullUri: decodedPath, fileSize: fileStats.size });
25739
26008
  decodedCount++;
25740
26009
  continue;
@@ -25753,86 +26022,32 @@ var ExecutorOrchestratorClass = class {
25753
26022
  }));
25754
26023
  return decodedResults;
25755
26024
  };
25756
- this._getWorkerPath = () => {
25757
- const currentDir = __dirname;
25758
- if (ProcessENVManager_default.getEnvVariable("NODE_ENV") === "dev" || ProcessENVManager_default.getEnvVariable("NODE_ENV") === "development")
25759
- return import_path21.default.resolve("./.build/workers");
25760
- const forcedPath = ProcessENVManager_default.getEnvVariable("REMORA_WORKERS_PATH");
25761
- if (forcedPath && forcedPath.length > 0)
25762
- return import_path21.default.join(__dirname, forcedPath);
25763
- if (!currentDir.includes(".build")) {
25764
- return import_path21.default.join(__dirname, "../workers");
25765
- } else {
25766
- return import_path21.default.resolve("./.build/workers");
25767
- }
25768
- };
25769
26025
  this.reconcileExecutorThreadsResults = async (scope, executorResults, tracker) => {
25770
- const mainPath = ExecutorScope_default2.getMainPath(scope);
26026
+ const mainPath = ExecutorScope_default.getMainPath(scope);
25771
26027
  ConsumerExecutor_default._ensurePath(mainPath);
25772
26028
  if (executorResults.length > 1) {
25773
26029
  Logger_default.log(`[${scope.id}] Merging ${executorResults.length} worker output files into ${mainPath}`);
25774
26030
  const perf = performance.now();
25775
26031
  for (const workerResult of executorResults) {
25776
- await (0, import_promises11.pipeline)(
25777
- import_fs14.default.createReadStream(workerResult.resultUri),
25778
- import_fs14.default.createWriteStream(mainPath, { flags: "a" })
26032
+ await (0, import_promises10.pipeline)(
26033
+ import_fs13.default.createReadStream(workerResult.resultUri),
26034
+ import_fs13.default.createWriteStream(mainPath, { flags: "a" })
25779
26035
  );
25780
- await import_promises10.default.unlink(workerResult.resultUri);
26036
+ await import_promises9.default.unlink(workerResult.resultUri);
25781
26037
  }
25782
26038
  tracker.measure("merge-workers", performance.now() - perf);
25783
26039
  Logger_default.log(`[${scope.id}] Merge complete in ${Math.round(performance.now() - perf)}ms`);
25784
26040
  } else if (executorResults.length === 1) {
25785
26041
  Logger_default.log(`[${scope.id}] Single worker \u2014 renaming output to ${mainPath}`);
25786
- await import_promises10.default.rename(executorResults[0].resultUri, mainPath);
26042
+ await import_promises9.default.rename(executorResults[0].resultUri, mainPath);
25787
26043
  }
25788
26044
  return mainPath;
25789
26045
  };
25790
26046
  this.performCleanupOperations = async (scope, tracker) => {
25791
26047
  const start = performance.now();
25792
- await ExecutorScope_default2.clearScope(scope);
26048
+ await ExecutorScope_default.clearScope(scope);
25793
26049
  tracker.measure("cleanup-operations", performance.now() - start);
25794
26050
  };
25795
- this.computeFinalResult = (tracker, executorResults, executionId, resultUri) => {
25796
- const result = {
25797
- cycles: Algo_default.max(executorResults.map((x) => x.cycles)),
25798
- elapsedMS: Algo_default.sum(executorResults.map((x) => x.elapsedMS)),
25799
- inputCount: Algo_default.sum(executorResults.map((x) => x.inputCount)),
25800
- outputCount: Algo_default.sum(executorResults.map((x) => x.outputCount)),
25801
- workerCount: executorResults.length,
25802
- executionId,
25803
- resultUri,
25804
- operations: {}
25805
- };
25806
- for (const res of executorResults) {
25807
- for (const opKey of Object.keys(res.operations)) {
25808
- const op = res.operations[opKey];
25809
- let label = result.operations[opKey];
25810
- if (!label) {
25811
- result.operations[opKey] = { avg: -1, max: -1, min: -1, elapsedMS: [] };
25812
- label = result.operations[opKey];
25813
- }
25814
- label.elapsedMS.push(op.elapsedMS);
25815
- }
25816
- for (const opKey of Object.keys(result.operations)) {
25817
- const operation = result.operations[opKey];
25818
- if (operation.elapsedMS.length > 0) {
25819
- operation.min = Math.min(...operation.elapsedMS);
25820
- operation.max = Math.max(...operation.elapsedMS);
25821
- operation.avg = Algo_default.mean(operation.elapsedMS);
25822
- }
25823
- }
25824
- }
25825
- const trackerOperations = tracker.getOperations();
25826
- for (const opKey of Object.keys(trackerOperations)) {
25827
- const trackerOp = trackerOperations[opKey];
25828
- const value = trackerOp.elapsedMS;
25829
- if (!result.operations[opKey]) {
25830
- result.operations[opKey] = { avg: value, max: value, min: value, elapsedMS: [] };
25831
- }
25832
- result.operations[opKey].elapsedMS.push(value);
25833
- }
25834
- return result;
25835
- };
25836
26051
  this.onWorkAdvanced = (packet, workerId, progress, totalBytesToProcess, bytesProcessedByWorker) => {
25837
26052
  const { processed } = packet;
25838
26053
  bytesProcessedByWorker[workerId] = processed;
@@ -25972,21 +26187,21 @@ var discover = async (producerName) => {
25972
26187
 
25973
26188
  // src/actions/create_producer.ts
25974
26189
  var import_chalk8 = __toESM(require("chalk"));
25975
- var import_fs15 = __toESM(require("fs"));
26190
+ var import_fs14 = __toESM(require("fs"));
25976
26191
  var import_path22 = __toESM(require("path"));
25977
26192
  var create_producer = async (name) => {
25978
26193
  try {
25979
- if (!import_fs15.default.existsSync("./remora/producers")) {
26194
+ if (!import_fs14.default.existsSync("./remora/producers")) {
25980
26195
  throw new Error(import_chalk8.default.red("Missing directory: ") + import_chalk8.default.yellow("./remora/producers"));
25981
26196
  }
25982
- const defaultProducerTemplate = import_fs15.default.readFileSync(
26197
+ const defaultProducerTemplate = import_fs14.default.readFileSync(
25983
26198
  import_path22.default.join(DOCUMENTATION_DIR, "default_resources/producer.json"),
25984
26199
  "utf-8"
25985
26200
  );
25986
26201
  const defaultProducer = JSON.parse(defaultProducerTemplate);
25987
26202
  defaultProducer.name = name;
25988
26203
  const producerPath = import_path22.default.join("remora/producers", `${name}.json`);
25989
- import_fs15.default.writeFileSync(producerPath, JSON.stringify(defaultProducer, null, 4));
26204
+ import_fs14.default.writeFileSync(producerPath, JSON.stringify(defaultProducer, null, 4));
25990
26205
  console.log(import_chalk8.default.green(`\u2705 Created producer config at ${producerPath}`));
25991
26206
  console.log(import_chalk8.default.blue("Remember to:"));
25992
26207
  console.log(import_chalk8.default.blue("1. Set the correct source name"));
@@ -26001,14 +26216,14 @@ var create_producer = async (name) => {
26001
26216
 
26002
26217
  // src/actions/create_consumer.ts
26003
26218
  var import_chalk9 = __toESM(require("chalk"));
26004
- var import_fs16 = __toESM(require("fs"));
26219
+ var import_fs15 = __toESM(require("fs"));
26005
26220
  var import_path23 = __toESM(require("path"));
26006
26221
  var create_consumer = async (name, producerName) => {
26007
26222
  try {
26008
- if (!import_fs16.default.existsSync("./remora/consumers")) {
26223
+ if (!import_fs15.default.existsSync("./remora/consumers")) {
26009
26224
  throw new Error(import_chalk9.default.red("Missing directory: ") + import_chalk9.default.yellow("./remora/consumers"));
26010
26225
  }
26011
- const defaultConsumerTemplate = import_fs16.default.readFileSync(
26226
+ const defaultConsumerTemplate = import_fs15.default.readFileSync(
26012
26227
  import_path23.default.join(DOCUMENTATION_DIR, "default_resources/consumer.json"),
26013
26228
  "utf-8"
26014
26229
  );
@@ -26016,10 +26231,10 @@ var create_consumer = async (name, producerName) => {
26016
26231
  defaultConsumer.name = name;
26017
26232
  if (producerName) {
26018
26233
  const producerPath = import_path23.default.join("remora/producers", `${producerName}.json`);
26019
- if (!import_fs16.default.existsSync(producerPath)) {
26234
+ if (!import_fs15.default.existsSync(producerPath)) {
26020
26235
  throw new Error(import_chalk9.default.red("Producer not found: ") + import_chalk9.default.yellow(producerPath));
26021
26236
  }
26022
- const producerConfig = JSON.parse(import_fs16.default.readFileSync(producerPath, "utf-8"));
26237
+ const producerConfig = JSON.parse(import_fs15.default.readFileSync(producerPath, "utf-8"));
26023
26238
  defaultConsumer.producers = [{ name: producerName }];
26024
26239
  defaultConsumer.fields = producerConfig.dimensions.map((dim) => ({
26025
26240
  key: dim.name,
@@ -26037,7 +26252,7 @@ var create_consumer = async (name, producerName) => {
26037
26252
  defaultConsumer.metadata = void 0;
26038
26253
  }
26039
26254
  const consumerPath = import_path23.default.join("remora/consumers", `${name}.json`);
26040
- import_fs16.default.writeFileSync(consumerPath, JSON.stringify(defaultConsumer, null, 4));
26255
+ import_fs15.default.writeFileSync(consumerPath, JSON.stringify(defaultConsumer, null, 4));
26041
26256
  console.log(import_chalk9.default.green(`\u2705 Created consumer config at ${consumerPath}`));
26042
26257
  if (!producerName) {
26043
26258
  console.log(import_chalk9.default.blue("Remember to:"));
@@ -26060,7 +26275,7 @@ var create_consumer = async (name, producerName) => {
26060
26275
  // src/actions/automap.ts
26061
26276
  var import_chalk10 = __toESM(require("chalk"));
26062
26277
  var import_ora5 = __toESM(require("ora"));
26063
- var import_fs17 = __toESM(require("fs"));
26278
+ var import_fs16 = __toESM(require("fs"));
26064
26279
  var import_path24 = __toESM(require("path"));
26065
26280
  var automap = async (producerName, schemaNames) => {
26066
26281
  try {
@@ -26087,12 +26302,12 @@ var automap = async (producerName, schemaNames) => {
26087
26302
  const mapResult = await AutoMapperEngine_default.map(sampleStrings, schemas, producer.settings.fileKey, [source]);
26088
26303
  for (const producer2 of mapResult.producers) {
26089
26304
  const producerPath = import_path24.default.join("remora/producers", `${producer2.name}.json`);
26090
- import_fs17.default.writeFileSync(producerPath, JSON.stringify(producer2, null, 4));
26305
+ import_fs16.default.writeFileSync(producerPath, JSON.stringify(producer2, null, 4));
26091
26306
  console.log(import_chalk10.default.blue(`Created producer: ${producer2.name}`));
26092
26307
  }
26093
26308
  for (const consumer of mapResult.consumers) {
26094
26309
  const consumerPath = import_path24.default.join("remora/consumers", `${consumer.name}.json`);
26095
- import_fs17.default.writeFileSync(consumerPath, JSON.stringify(consumer, null, 4));
26310
+ import_fs16.default.writeFileSync(consumerPath, JSON.stringify(consumer, null, 4));
26096
26311
  console.log(import_chalk10.default.blue(`Created consumer: ${consumer.name}`));
26097
26312
  }
26098
26313
  spinner.succeed("Producer has been successfully mapped");
@@ -26290,6 +26505,7 @@ if (!process.env.REMORA_RUNTIME_CONTEXT) {
26290
26505
  process.env.REMORA_RUNTIME_CONTEXT = "cli";
26291
26506
  Logger_default.warn('Missing property for REMORA_RUNTIME_CONTEXT during the Remora CLI startup. Defaulting to "cli" for this run. Set it manually in your environment.');
26292
26507
  }
26508
+ ProcessShutdownManager_default.init("Remora CLI");
26293
26509
  var program = new import_commander.Command();
26294
26510
  var remoraLicenceKey = ProcessENVManager_default.getEnvVariable("REMORA_LICENCE_KEY");
26295
26511
  var check = LicenceManager_default.validate(remoraLicenceKey);