@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.
@@ -10292,7 +10292,7 @@ var require_node2 = __commonJS({
10292
10292
  var require_tail_file = __commonJS({
10293
10293
  "../../packages/logger/node_modules/winston/lib/winston/tail-file.js"(exports2, module2) {
10294
10294
  "use strict";
10295
- var fs19 = require("fs");
10295
+ var fs18 = require("fs");
10296
10296
  var { StringDecoder } = require("string_decoder");
10297
10297
  var { Stream } = require_readable();
10298
10298
  function noop() {
@@ -10313,7 +10313,7 @@ var require_tail_file = __commonJS({
10313
10313
  stream.emit("end");
10314
10314
  stream.emit("close");
10315
10315
  };
10316
- fs19.open(options.file, "a+", "0644", (err2, fd) => {
10316
+ fs18.open(options.file, "a+", "0644", (err2, fd) => {
10317
10317
  if (err2) {
10318
10318
  if (!iter) {
10319
10319
  stream.emit("error", err2);
@@ -10325,10 +10325,10 @@ var require_tail_file = __commonJS({
10325
10325
  }
10326
10326
  (function read() {
10327
10327
  if (stream.destroyed) {
10328
- fs19.close(fd, noop);
10328
+ fs18.close(fd, noop);
10329
10329
  return;
10330
10330
  }
10331
- return fs19.read(fd, buffer, 0, buffer.length, pos, (error, bytes) => {
10331
+ return fs18.read(fd, buffer, 0, buffer.length, pos, (error, bytes) => {
10332
10332
  if (error) {
10333
10333
  if (!iter) {
10334
10334
  stream.emit("error", error);
@@ -10387,7 +10387,7 @@ var require_tail_file = __commonJS({
10387
10387
  var require_file = __commonJS({
10388
10388
  "../../packages/logger/node_modules/winston/lib/winston/transports/file.js"(exports2, module2) {
10389
10389
  "use strict";
10390
- var fs19 = require("fs");
10390
+ var fs18 = require("fs");
10391
10391
  var path18 = require("path");
10392
10392
  var asyncSeries = require_series();
10393
10393
  var zlib2 = require("zlib");
@@ -10592,7 +10592,7 @@ var require_file = __commonJS({
10592
10592
  let buff = "";
10593
10593
  let results = [];
10594
10594
  let row = 0;
10595
- const stream = fs19.createReadStream(file, {
10595
+ const stream = fs18.createReadStream(file, {
10596
10596
  encoding: "utf8"
10597
10597
  });
10598
10598
  stream.on("error", (err2) => {
@@ -10744,7 +10744,7 @@ var require_file = __commonJS({
10744
10744
  stat(callback) {
10745
10745
  const target = this._getFile();
10746
10746
  const fullpath = path18.join(this.dirname, target);
10747
- fs19.stat(fullpath, (err2, stat) => {
10747
+ fs18.stat(fullpath, (err2, stat) => {
10748
10748
  if (err2 && err2.code === "ENOENT") {
10749
10749
  debug("ENOENT\xA0ok", fullpath);
10750
10750
  this.filename = target;
@@ -10849,7 +10849,7 @@ var require_file = __commonJS({
10849
10849
  _createStream(source) {
10850
10850
  const fullpath = path18.join(this.dirname, this.filename);
10851
10851
  debug("create stream start", fullpath, this.options);
10852
- const dest = fs19.createWriteStream(fullpath, this.options).on("error", (err2) => debug(err2)).on("close", () => debug("close", dest.path, dest.bytesWritten)).on("open", () => {
10852
+ const dest = fs18.createWriteStream(fullpath, this.options).on("error", (err2) => debug(err2)).on("close", () => debug("close", dest.path, dest.bytesWritten)).on("open", () => {
10853
10853
  debug("file open ok", fullpath);
10854
10854
  this.emit("open", fullpath);
10855
10855
  source.pipe(dest);
@@ -10928,7 +10928,7 @@ var require_file = __commonJS({
10928
10928
  const isZipped = this.zippedArchive ? ".gz" : "";
10929
10929
  const filePath = `${basename}${isOldest}${ext}${isZipped}`;
10930
10930
  const target = path18.join(this.dirname, filePath);
10931
- fs19.unlink(target, callback);
10931
+ fs18.unlink(target, callback);
10932
10932
  }
10933
10933
  /**
10934
10934
  * Roll files forward based on integer, up to maxFiles. e.g. if base if
@@ -10951,17 +10951,17 @@ var require_file = __commonJS({
10951
10951
  tasks.push(function(i, cb) {
10952
10952
  let fileName = `${basename}${i - 1}${ext}${isZipped}`;
10953
10953
  const tmppath = path18.join(this.dirname, fileName);
10954
- fs19.exists(tmppath, (exists) => {
10954
+ fs18.exists(tmppath, (exists) => {
10955
10955
  if (!exists) {
10956
10956
  return cb(null);
10957
10957
  }
10958
10958
  fileName = `${basename}${i}${ext}${isZipped}`;
10959
- fs19.rename(tmppath, path18.join(this.dirname, fileName), cb);
10959
+ fs18.rename(tmppath, path18.join(this.dirname, fileName), cb);
10960
10960
  });
10961
10961
  }.bind(this, x));
10962
10962
  }
10963
10963
  asyncSeries(tasks, () => {
10964
- fs19.rename(
10964
+ fs18.rename(
10965
10965
  path18.join(this.dirname, `${basename}${ext}${isZipped}`),
10966
10966
  path18.join(this.dirname, `${basename}1${ext}${isZipped}`),
10967
10967
  callback
@@ -10977,22 +10977,22 @@ var require_file = __commonJS({
10977
10977
  * @private
10978
10978
  */
10979
10979
  _compressFile(src, dest, callback) {
10980
- fs19.access(src, fs19.F_OK, (err2) => {
10980
+ fs18.access(src, fs18.F_OK, (err2) => {
10981
10981
  if (err2) {
10982
10982
  return callback();
10983
10983
  }
10984
10984
  var gzip = zlib2.createGzip();
10985
- var inp = fs19.createReadStream(src);
10986
- var out = fs19.createWriteStream(dest);
10985
+ var inp = fs18.createReadStream(src);
10986
+ var out = fs18.createWriteStream(dest);
10987
10987
  out.on("finish", () => {
10988
- fs19.unlink(src, callback);
10988
+ fs18.unlink(src, callback);
10989
10989
  });
10990
10990
  inp.pipe(gzip).pipe(out);
10991
10991
  });
10992
10992
  }
10993
10993
  _createLogDirIfNotExist(dirPath) {
10994
- if (!fs19.existsSync(dirPath)) {
10995
- fs19.mkdirSync(dirPath, { recursive: true });
10994
+ if (!fs18.existsSync(dirPath)) {
10995
+ fs18.mkdirSync(dirPath, { recursive: true });
10996
10996
  }
10997
10997
  }
10998
10998
  };
@@ -18738,25 +18738,6 @@ var ProcessENVManagerClass = class {
18738
18738
  var ProcessENVManager = new ProcessENVManagerClass();
18739
18739
  var ProcessENVManager_default = ProcessENVManager;
18740
18740
 
18741
- // ../../packages/common/src/SecretManager.ts
18742
- var SecretManagerClass = class {
18743
- constructor() {
18744
- /**
18745
- * If the value is a secret (or .env setting), replace it with the value inside the .env configuration file
18746
- * Starts with "{" and ends with "}".
18747
- * e.g. {AWS_ID}
18748
- */
18749
- this.replaceSecret = (value) => {
18750
- if (!value || value.length <= 2 || !value.startsWith("{") || !value.endsWith("}"))
18751
- return value;
18752
- const parsedValue = value.slice(1, value.length - 1);
18753
- return ProcessENVManager_default.getEnvVariable(parsedValue);
18754
- };
18755
- }
18756
- };
18757
- var SecretManager = new SecretManagerClass();
18758
- var SecretManager_default = SecretManager;
18759
-
18760
18741
  // ../../packages/common/src/ExecutorScope.ts
18761
18742
  var import_path3 = __toESM(require("path"), 1);
18762
18743
  var import_fs3 = __toESM(require("fs"), 1);
@@ -18764,7 +18745,7 @@ var import_promises = __toESM(require("fs/promises"), 1);
18764
18745
 
18765
18746
  // ../../packages/constants/src/Constants.ts
18766
18747
  var CONSTANTS = {
18767
- cliVersion: "1.2.9",
18748
+ cliVersion: "1.2.11",
18768
18749
  backendVersion: 1,
18769
18750
  backendPort: 5088,
18770
18751
  workerVersion: 2,
@@ -18810,10 +18791,10 @@ var ExecutorScopeClass = class {
18810
18791
  constructor() {
18811
18792
  this.WORKERS_FOLDER = "workers";
18812
18793
  this.PRODUCERS_FOLDER = "producers";
18794
+ this.getBasePath = () => import_path3.default.join(Constants_default.defaults.REMORA_PATH, Constants_default.defaults.PRODUCER_TEMP_FOLDER);
18813
18795
  this.getWorkerPath = (scope, workerId) => {
18814
18796
  return import_path3.default.join(
18815
- Constants_default.defaults.REMORA_PATH,
18816
- Constants_default.defaults.PRODUCER_TEMP_FOLDER,
18797
+ this.getBasePath(),
18817
18798
  // A specific execution sits entirely in this folder, so at the end it's safe to delete it entirely
18818
18799
  scope.folder,
18819
18800
  this.WORKERS_FOLDER,
@@ -18822,8 +18803,7 @@ var ExecutorScopeClass = class {
18822
18803
  };
18823
18804
  this.getProducerPath = (scope, producer, sourceFileKey) => {
18824
18805
  return import_path3.default.join(
18825
- Constants_default.defaults.REMORA_PATH,
18826
- Constants_default.defaults.PRODUCER_TEMP_FOLDER,
18806
+ this.getBasePath(),
18827
18807
  // A specific execution sits entirely in this folder, so at the end it's safe to delete it entirely
18828
18808
  scope.folder,
18829
18809
  this.PRODUCERS_FOLDER,
@@ -18833,22 +18813,30 @@ var ExecutorScopeClass = class {
18833
18813
  };
18834
18814
  this.getMainPath = (scope) => {
18835
18815
  return import_path3.default.join(
18836
- Constants_default.defaults.REMORA_PATH,
18837
- Constants_default.defaults.PRODUCER_TEMP_FOLDER,
18816
+ this.getBasePath(),
18838
18817
  scope.folder,
18839
18818
  "main.dataset"
18840
18819
  );
18841
18820
  };
18842
18821
  this.clearScope = async (scope) => {
18843
18822
  const scopePath = import_path3.default.join(
18844
- Constants_default.defaults.REMORA_PATH,
18845
- Constants_default.defaults.PRODUCER_TEMP_FOLDER,
18823
+ this.getBasePath(),
18846
18824
  scope.folder
18847
18825
  );
18848
18826
  if (import_fs3.default.existsSync(scopePath)) {
18849
18827
  await import_promises.default.rm(scopePath, { recursive: true, force: true });
18850
18828
  }
18851
18829
  };
18830
+ this.deepClear = () => {
18831
+ const basePath = this.getBasePath();
18832
+ const openScopes = this.getOpenScopes();
18833
+ for (const scopeFolder of openScopes) {
18834
+ const scopePath = import_path3.default.join(basePath, scopeFolder);
18835
+ if (import_fs3.default.existsSync(scopePath)) {
18836
+ import_fs3.default.rmSync(scopePath, { recursive: true, force: true });
18837
+ }
18838
+ }
18839
+ };
18852
18840
  this.ensurePath = (fileUri) => {
18853
18841
  const dir = import_path3.default.dirname(fileUri);
18854
18842
  if (!import_fs3.default.existsSync(dir))
@@ -18856,11 +18844,105 @@ var ExecutorScopeClass = class {
18856
18844
  if (!import_fs3.default.existsSync(fileUri))
18857
18845
  import_fs3.default.writeFileSync(fileUri, "");
18858
18846
  };
18847
+ this.getOpenScopes = () => {
18848
+ const basePath = this.getBasePath();
18849
+ if (!import_fs3.default.existsSync(basePath))
18850
+ return [];
18851
+ return import_fs3.default.readdirSync(basePath, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((folder) => folder !== "logs" && folder !== "usage");
18852
+ };
18859
18853
  }
18860
18854
  };
18861
18855
  var ExecutorScope = new ExecutorScopeClass();
18862
18856
  var ExecutorScope_default = ExecutorScope;
18863
18857
 
18858
+ // ../../packages/common/src/ProcessShutdownManager.ts
18859
+ var ProcessShutdownManagerClass = class {
18860
+ constructor() {
18861
+ this._initialized = false;
18862
+ this._cleaned = false;
18863
+ this._runtimeName = "Remora process";
18864
+ this.init = (runtimeName) => {
18865
+ if (this._initialized)
18866
+ return;
18867
+ this._initialized = true;
18868
+ if (runtimeName)
18869
+ this._runtimeName = runtimeName;
18870
+ process.once("SIGINT", () => this.handleSignal("SIGINT", 130));
18871
+ process.once("SIGTERM", () => this.handleSignal("SIGTERM", 143));
18872
+ process.once("uncaughtException", (error) => this.handleUnexpectedShutdown("uncaughtException", error));
18873
+ process.once("unhandledRejection", (reason) => this.handleUnexpectedShutdown("unhandledRejection", reason));
18874
+ process.once("beforeExit", (code) => this.handleBeforeExit(code));
18875
+ process.once("exit", (code) => this.handleExit(code));
18876
+ };
18877
+ this.handleSignal = (signal, exitCode) => {
18878
+ this.setShutdownState("intentional", signal);
18879
+ Logger_default.warn(`Received ${signal}. Shutting down ${this._runtimeName}.`);
18880
+ process.exit(exitCode);
18881
+ };
18882
+ this.handleUnexpectedShutdown = (reason, error) => {
18883
+ this.setShutdownState("unintentional", reason);
18884
+ Logger_default.error(this.asError(reason, error));
18885
+ process.exit(1);
18886
+ };
18887
+ this.handleBeforeExit = (code) => {
18888
+ this.setShutdownState(code === 0 ? "intentional" : "unintentional", `beforeExit:${code}`);
18889
+ };
18890
+ this.handleExit = (code) => {
18891
+ if (!this._shutdownState)
18892
+ this.setShutdownState(code === 0 ? "intentional" : "unintentional", `exit:${code}`);
18893
+ this.cleanupOpenScopes(code);
18894
+ };
18895
+ this.cleanupOpenScopes = (code) => {
18896
+ if (this._cleaned)
18897
+ return;
18898
+ this._cleaned = true;
18899
+ const openScopes = ExecutorScope_default.getOpenScopes();
18900
+ const scopeCount = openScopes.length;
18901
+ const shutdownState = this._shutdownState;
18902
+ const shutdownDescription = `${shutdownState?.type ?? "intentional"} shutdown (${shutdownState?.reason ?? `exit:${code}`})`;
18903
+ if (scopeCount === 0) {
18904
+ Logger_default.info(`Detected ${shutdownDescription} for ${this._runtimeName}. No executor scopes to clean up.`);
18905
+ return;
18906
+ }
18907
+ Logger_default.warn(`Detected ${shutdownDescription} for ${this._runtimeName}. Cleaning up ${scopeCount} executor scope${scopeCount === 1 ? "" : "s"}.`);
18908
+ ExecutorScope_default.deepClear();
18909
+ };
18910
+ this.setShutdownState = (type, reason) => {
18911
+ if (this._shutdownState)
18912
+ return;
18913
+ this._shutdownState = { type, reason };
18914
+ };
18915
+ this.asError = (reason, error) => {
18916
+ if (error instanceof Error) {
18917
+ const contextualError = new Error(`Unexpected ${reason} during ${this._runtimeName} execution: ${error.message}`);
18918
+ contextualError.stack = error.stack;
18919
+ return contextualError;
18920
+ }
18921
+ return new Error(`Unexpected ${reason} during ${this._runtimeName} execution: ${String(error)}`);
18922
+ };
18923
+ }
18924
+ };
18925
+ var ProcessShutdownManager = new ProcessShutdownManagerClass();
18926
+
18927
+ // ../../packages/common/src/SecretManager.ts
18928
+ var SecretManagerClass = class {
18929
+ constructor() {
18930
+ /**
18931
+ * If the value is a secret (or .env setting), replace it with the value inside the .env configuration file
18932
+ * Starts with "{" and ends with "}".
18933
+ * e.g. {AWS_ID}
18934
+ */
18935
+ this.replaceSecret = (value) => {
18936
+ if (!value || value.length <= 2 || !value.startsWith("{") || !value.endsWith("}"))
18937
+ return value;
18938
+ const parsedValue = value.slice(1, value.length - 1);
18939
+ return ProcessENVManager_default.getEnvVariable(parsedValue);
18940
+ };
18941
+ }
18942
+ };
18943
+ var SecretManager = new SecretManagerClass();
18944
+ var SecretManager_default = SecretManager;
18945
+
18864
18946
  // ../../packages/common/src/Environment.ts
18865
18947
  var import_fs5 = __toESM(require("fs"), 1);
18866
18948
  var import_crypto = __toESM(require("crypto"), 1);
@@ -19102,7 +19184,16 @@ var ValidatorClass = class {
19102
19184
  }
19103
19185
  const allAvailableFields = [...availableFieldsByProducer.values()].flat();
19104
19186
  for (const field of consumer.fields) {
19105
- if (field.key === "*" || field.fixed || field.copyFrom) continue;
19187
+ const syntheticFieldModes = [field.fixed === true, !!field.copyFrom, field.lineCount === true].filter(Boolean);
19188
+ if (syntheticFieldModes.length > 1)
19189
+ errors.push(`Field "${field.alias ?? field.key}" in consumer "${consumer.name}" can only use one synthetic field mode among "fixed", "copyFrom", and "lineCount".`);
19190
+ if (field.lineCount && field.key === "*")
19191
+ errors.push(`Field "${field.alias ?? field.key}" in consumer "${consumer.name}" cannot use "lineCount" with the wildcard key "*".`);
19192
+ if (field.lineCount && field.transform)
19193
+ errors.push(`Field "${field.alias ?? field.key}" in consumer "${consumer.name}" cannot use "transform" together with "lineCount".`);
19194
+ if (field.lineCount && field.validate && field.validate.length > 0)
19195
+ errors.push(`Field "${field.alias ?? field.key}" in consumer "${consumer.name}" cannot use "validate" together with "lineCount".`);
19196
+ if (field.key === "*" || field.fixed || field.copyFrom || field.lineCount) continue;
19106
19197
  if (field.from) {
19107
19198
  const producerFields = availableFieldsByProducer.get(field.from);
19108
19199
  if (producerFields && !producerFields.includes(field.key))
@@ -19123,6 +19214,8 @@ var ValidatorClass = class {
19123
19214
  const found = precedingFields.find((f) => (f.alias ?? f.key) === field.copyFrom);
19124
19215
  if (!found)
19125
19216
  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}".`);
19217
+ else if (found.lineCount)
19218
+ 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.`);
19126
19219
  }
19127
19220
  if (consumer.filters && consumer.filters.length > 0) {
19128
19221
  if (consumer.filters.some((x) => x.sql && x.rule))
@@ -19143,10 +19236,14 @@ var ValidatorClass = class {
19143
19236
  const missingRules = rulesWithMatchingFields.filter((x) => !x.match);
19144
19237
  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.`);
19145
19238
  }
19239
+ const lineCountRuleMembers = rulesWithMatchingFields.filter((x) => x.match?.lineCount);
19240
+ if (lineCountRuleMembers.length > 0)
19241
+ errors.push(`Consumer "${consumer.name}" cannot use rule-based filters on field(s) with "lineCount": ${lineCountRuleMembers.map((x) => `"${x.rule.rule.member}"`).join(", ")}.`);
19146
19242
  }
19147
19243
  const validateTransformations = (fields) => {
19148
19244
  const errors2 = [];
19149
19245
  const trxsFields = fields.filter((x) => x.transform);
19246
+ const lineCountFieldNames = new Set(fields.filter((x) => x.lineCount).map((x) => x.alias ?? x.key));
19150
19247
  for (const field of trxsFields) {
19151
19248
  const trxToValidate = [];
19152
19249
  if (Array.isArray(field.transform))
@@ -19154,6 +19251,16 @@ var ValidatorClass = class {
19154
19251
  else
19155
19252
  trxToValidate.push(field.transform);
19156
19253
  for (const trans of trxToValidate) {
19254
+ if ("multiplyBy" in trans) {
19255
+ const invalidFields = trans.multiplyBy.fields.filter((x) => lineCountFieldNames.has(x));
19256
+ if (invalidFields.length > 0)
19257
+ errors2.push(`Transformation on field "${field.alias ?? field.key}" cannot reference lineCount field(s) in multiplyBy: ${invalidFields.map((x) => `"${x}"`).join(", ")}.`);
19258
+ }
19259
+ if ("addBy" in trans) {
19260
+ const invalidFields = trans.addBy.fields.filter((x) => lineCountFieldNames.has(x));
19261
+ if (invalidFields.length > 0)
19262
+ errors2.push(`Transformation on field "${field.alias ?? field.key}" cannot reference lineCount field(s) in addBy: ${invalidFields.map((x) => `"${x}"`).join(", ")}.`);
19263
+ }
19157
19264
  if ("combine_fields" in trans) {
19158
19265
  const { combine_fields } = trans;
19159
19266
  if (!combine_fields.fields || combine_fields.fields.length === 0)
@@ -19161,6 +19268,9 @@ var ValidatorClass = class {
19161
19268
  const missingFieldsInConsumer = combine_fields.fields.map((x) => ({ field: x, found: fields.find((k) => (k.alias ?? k.key) === x) })).filter((x) => !x.found);
19162
19269
  if (missingFieldsInConsumer.length > 0)
19163
19270
  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}";`);
19271
+ const invalidFields = combine_fields.fields.filter((x) => lineCountFieldNames.has(x));
19272
+ if (invalidFields.length > 0)
19273
+ errors2.push(`Transformation on field "${field.alias ?? field.key}" cannot reference lineCount field(s) in combine_fields: ${invalidFields.map((x) => `"${x}"`).join(", ")}.`);
19164
19274
  }
19165
19275
  }
19166
19276
  }
@@ -19183,7 +19293,7 @@ var ValidatorClass = class {
19183
19293
  errors.push(`The export destination "${output.exportDestination}" was not found in the sources.`);
19184
19294
  }
19185
19295
  }
19186
- const dimensionFields = consumer.fields.filter((x) => x.key !== "*" && !x.fixed && !x.copyFrom);
19296
+ const dimensionFields = consumer.fields.filter((x) => x.key !== "*" && !x.fixed && !x.copyFrom && !x.lineCount);
19187
19297
  const dimensionKeys = dimensionFields.map((x) => `${x.from ?? "_default_"}::${x.key}`);
19188
19298
  const duplicateDimensionKeys = dimensionKeys.filter((k, i) => dimensionKeys.indexOf(k) !== i);
19189
19299
  if (duplicateDimensionKeys.length > 0) {
@@ -19621,10 +19731,10 @@ var Environment = new EnvironmentClass();
19621
19731
  var Environment_default = Environment;
19622
19732
 
19623
19733
  // ../../packages/executors/src/ConsumerExecutor.ts
19624
- var import_path16 = __toESM(require("path"));
19625
- var import_fs11 = __toESM(require("fs"));
19734
+ var import_path15 = __toESM(require("path"));
19735
+ var import_fs10 = __toESM(require("fs"));
19626
19736
  var import_readline6 = __toESM(require("readline"));
19627
- var import_promises9 = __toESM(require("fs/promises"));
19737
+ var import_promises8 = __toESM(require("fs/promises"));
19628
19738
  var import_crypto4 = __toESM(require("crypto"));
19629
19739
 
19630
19740
  // ../../packages/engines/src/CryptoEngine.ts
@@ -20858,6 +20968,19 @@ var Helper = {
20858
20968
  };
20859
20969
  var Helper_default = Helper;
20860
20970
 
20971
+ // ../../packages/helper/src/Formatter.ts
20972
+ var Formatter = {
20973
+ bytes: (bytes, decimals = 2) => {
20974
+ if (!+bytes) return "0 Bytes";
20975
+ const k = 1024;
20976
+ const dm = decimals < 0 ? 0 : decimals;
20977
+ const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
20978
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
20979
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
20980
+ }
20981
+ };
20982
+ var Formatter_default = Formatter;
20983
+
20861
20984
  // ../../packages/helper/src/Settings.ts
20862
20985
  var SETTINGS = {
20863
20986
  db: {
@@ -22688,6 +22811,25 @@ var ConsumerManagerClass = class {
22688
22811
  const expandedFields = convertedFields.flatMap((x) => this.expandField(consumer, x, availableColumns));
22689
22812
  return expandedFields;
22690
22813
  };
22814
+ this.getLineCountFields = (consumer, visibleOnly = false) => {
22815
+ Affirm_default(consumer, "Invalid consumer");
22816
+ return this.getLineCountFieldsFromExpanded(this.getExpandedFields(consumer), visibleOnly);
22817
+ };
22818
+ this.getLineCountFieldsFromExpanded = (fields, visibleOnly = false) => {
22819
+ Affirm_default(fields, "Invalid consumer fields");
22820
+ return fields.filter((field) => this.isLineCountField(field.cField) && (!visibleOnly || !field.cField.hidden));
22821
+ };
22822
+ this.hasLineCountFields = (consumer, visibleOnly = false) => {
22823
+ return this.getLineCountFields(consumer, visibleOnly).length > 0;
22824
+ };
22825
+ this.isSyntheticField = (field) => {
22826
+ Affirm_default(field, "Invalid field");
22827
+ return field.fixed === true || !!field.copyFrom || this.isLineCountField(field);
22828
+ };
22829
+ this.isLineCountField = (field) => {
22830
+ Affirm_default(field, "Invalid field");
22831
+ return field.lineCount === true;
22832
+ };
22691
22833
  this.getAvailableColumns = (consumer) => {
22692
22834
  return ResourcesUtils_default.getAvailableColumns(consumer);
22693
22835
  };
@@ -22740,7 +22882,7 @@ var ConsumerManagerClass = class {
22740
22882
  column = columns.find((x) => x.owner === field.from && x.nameInProducer === field.key);
22741
22883
  } else if (consumer.producers.length === 1 && !field.from) {
22742
22884
  column = columns.find((x) => x.nameInProducer === field.key);
22743
- } else if (!field.fixed && !field.copyFrom) {
22885
+ } else if (!this.isSyntheticField(field)) {
22744
22886
  const matches = columns.filter((x) => x.nameInProducer === field.key);
22745
22887
  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(", ")})`);
22746
22888
  if (matches.length === 1) {
@@ -22751,14 +22893,14 @@ var ConsumerManagerClass = class {
22751
22893
  column = matches[0];
22752
22894
  }
22753
22895
  if (!column) {
22754
- if (field.fixed === true && Algo_default.hasVal(field.default) || field.copyFrom) {
22896
+ if (field.fixed === true && Algo_default.hasVal(field.default) || this.isSyntheticField(field)) {
22755
22897
  column = {
22756
22898
  aliasInProducer: field.key,
22757
22899
  nameInProducer: field.alias ?? field.key,
22758
22900
  consumerAlias: field.alias ?? field.key,
22759
22901
  consumerKey: field.key,
22760
22902
  owner: field.from,
22761
- dimension: { name: field.key, type: typeof field.default }
22903
+ dimension: { name: field.key, type: this.isLineCountField(field) ? "number" : typeof field.default }
22762
22904
  };
22763
22905
  }
22764
22906
  }
@@ -24197,69 +24339,8 @@ var UsageManager = new UsageManagerClass();
24197
24339
  var UsageManager_default = UsageManager;
24198
24340
 
24199
24341
  // ../../packages/executors/src/OutputExecutor.ts
24200
- var fs14 = __toESM(require("fs"));
24201
-
24202
- // ../../packages/executors/src/ExecutorScope.ts
24342
+ var fs13 = __toESM(require("fs"));
24203
24343
  var import_path14 = __toESM(require("path"));
24204
- var import_fs10 = __toESM(require("fs"));
24205
- var import_promises8 = __toESM(require("fs/promises"));
24206
- var ExecutorScopeClass2 = class {
24207
- constructor() {
24208
- this.WORKERS_FOLDER = "workers";
24209
- this.PRODUCERS_FOLDER = "producers";
24210
- this.getWorkerPath = (scope, workerId) => {
24211
- return import_path14.default.join(
24212
- Constants_default.defaults.REMORA_PATH,
24213
- Constants_default.defaults.PRODUCER_TEMP_FOLDER,
24214
- // A specific execution sits entirely in this folder, so at the end it's safe to delete it entirely
24215
- scope.folder,
24216
- this.WORKERS_FOLDER,
24217
- `${workerId}.dataset`
24218
- );
24219
- };
24220
- this.getProducerPath = (scope, producer, sourceFileKey) => {
24221
- return import_path14.default.join(
24222
- Constants_default.defaults.REMORA_PATH,
24223
- Constants_default.defaults.PRODUCER_TEMP_FOLDER,
24224
- // A specific execution sits entirely in this folder, so at the end it's safe to delete it entirely
24225
- scope.folder,
24226
- this.PRODUCERS_FOLDER,
24227
- producer.name,
24228
- `${sourceFileKey}.dataset`
24229
- );
24230
- };
24231
- this.getMainPath = (scope) => {
24232
- return import_path14.default.join(
24233
- Constants_default.defaults.REMORA_PATH,
24234
- Constants_default.defaults.PRODUCER_TEMP_FOLDER,
24235
- scope.folder,
24236
- "main.dataset"
24237
- );
24238
- };
24239
- this.clearScope = async (scope) => {
24240
- const scopePath = import_path14.default.join(
24241
- Constants_default.defaults.REMORA_PATH,
24242
- Constants_default.defaults.PRODUCER_TEMP_FOLDER,
24243
- scope.folder
24244
- );
24245
- if (import_fs10.default.existsSync(scopePath)) {
24246
- await import_promises8.default.rm(scopePath, { recursive: true, force: true });
24247
- }
24248
- };
24249
- this.ensurePath = (fileUri) => {
24250
- const dir = import_path14.default.dirname(fileUri);
24251
- if (!import_fs10.default.existsSync(dir))
24252
- import_fs10.default.mkdirSync(dir, { recursive: true });
24253
- if (!import_fs10.default.existsSync(fileUri))
24254
- import_fs10.default.writeFileSync(fileUri, "");
24255
- };
24256
- }
24257
- };
24258
- var ExecutorScope2 = new ExecutorScopeClass2();
24259
- var ExecutorScope_default2 = ExecutorScope2;
24260
-
24261
- // ../../packages/executors/src/OutputExecutor.ts
24262
- var import_path15 = __toESM(require("path"));
24263
24344
  var OutputExecutorClass = class {
24264
24345
  constructor() {
24265
24346
  this._getInternalRecordFormat = (consumer) => {
@@ -24303,13 +24384,13 @@ var OutputExecutorClass = class {
24303
24384
  for (const output of consumer.outputs) {
24304
24385
  const destination = Environment_default.getSource(output.exportDestination);
24305
24386
  const driver = await DriverFactory_default.instantiateDestination(destination);
24306
- const currentPath = import_path15.default.dirname(ExecutorScope_default2.getMainPath(scope));
24387
+ const currentPath = import_path14.default.dirname(ExecutorScope_default.getMainPath(scope));
24307
24388
  const destinationName = this._composeFileName(consumer, output, this._getExtension(output));
24308
24389
  Logger_default.log(`Exporting consumer "${consumer.name}" to "${output.exportDestination}" as ${output.format} (${destinationName})`);
24309
- const filenameArray = fs14.readdirSync(currentPath).filter((filename) => filename.includes(".dataset"));
24390
+ const filenameArray = fs13.readdirSync(currentPath).filter((filename) => filename.includes(".dataset"));
24310
24391
  for (const filename in filenameArray) {
24311
24392
  const destinationPath = this.getCompletedPath(destinationName, filename);
24312
- const startingPath = import_path15.default.join(currentPath, filenameArray[filename]);
24393
+ const startingPath = import_path14.default.join(currentPath, filenameArray[filename]);
24313
24394
  if (output.format === internalFormat) {
24314
24395
  results.push(await driver.move(startingPath, destinationPath));
24315
24396
  } else {
@@ -24369,47 +24450,54 @@ var OutputExecutor_default = OutputExecutor;
24369
24450
  var ConsumerExecutorClass = class {
24370
24451
  constructor() {
24371
24452
  this._getWorkPath = (consumer, executionId) => {
24372
- const execFolder = import_path16.default.join(consumer.name, executionId);
24373
- const workPath = import_path16.default.join("./remora", Constants_default.defaults.PRODUCER_TEMP_FOLDER, execFolder, ".dataset");
24453
+ const execFolder = import_path15.default.join(consumer.name, executionId);
24454
+ const workPath = import_path15.default.join("./remora", Constants_default.defaults.PRODUCER_TEMP_FOLDER, execFolder, ".dataset");
24374
24455
  return workPath;
24375
24456
  };
24376
24457
  this._clearWorkPath = async (workPath) => {
24377
24458
  try {
24378
- if (import_fs11.default.existsSync(workPath)) {
24379
- await import_promises9.default.unlink(workPath);
24459
+ if (import_fs10.default.existsSync(workPath)) {
24460
+ await import_promises8.default.unlink(workPath);
24380
24461
  }
24381
24462
  } catch (error) {
24382
24463
  }
24383
24464
  try {
24384
- const dir = import_path16.default.dirname(workPath);
24385
- if (import_fs11.default.existsSync(dir)) {
24386
- await import_promises9.default.rmdir(dir);
24465
+ const dir = import_path15.default.dirname(workPath);
24466
+ if (import_fs10.default.existsSync(dir)) {
24467
+ await import_promises8.default.rmdir(dir);
24387
24468
  }
24388
24469
  } catch (error) {
24389
24470
  }
24390
24471
  };
24391
24472
  this._ensurePath = (pathUri) => {
24392
- const dir = import_path16.default.dirname(pathUri);
24393
- if (!import_fs11.default.existsSync(dir))
24394
- import_fs11.default.mkdirSync(dir, { recursive: true });
24395
- if (!import_fs11.default.existsSync(pathUri))
24396
- import_fs11.default.writeFileSync(pathUri, "");
24473
+ const dir = import_path15.default.dirname(pathUri);
24474
+ if (!import_fs10.default.existsSync(dir))
24475
+ import_fs10.default.mkdirSync(dir, { recursive: true });
24476
+ if (!import_fs10.default.existsSync(pathUri))
24477
+ import_fs10.default.writeFileSync(pathUri, "");
24397
24478
  };
24398
24479
  this.processRecord = (options) => {
24399
24480
  const { consumer, fields, dimensions, producer, record, requestOptions, index: recordIndex } = options;
24400
24481
  for (const field of fields) {
24401
24482
  const { cField } = field;
24402
24483
  const fieldKey = cField.alias ?? cField.key;
24484
+ if (cField.fixed && Algo_default.hasVal(cField.default)) {
24485
+ record[fieldKey] = cField.default;
24486
+ continue;
24487
+ }
24488
+ if (ConsumerManager_default.isLineCountField(cField)) {
24489
+ record[fieldKey] = recordIndex + 1;
24490
+ continue;
24491
+ }
24492
+ if (cField.copyFrom) {
24493
+ record[fieldKey] = record[cField.copyFrom];
24494
+ continue;
24495
+ }
24403
24496
  let dimension;
24404
24497
  try {
24405
24498
  dimension = dimensions.find((x) => x.name === cField.key);
24406
24499
  if (!dimension) {
24407
- if (cField.fixed && Algo_default.hasVal(cField.default))
24408
- record[fieldKey] = cField.default;
24409
- else if (cField.copyFrom)
24410
- record[fieldKey] = record[cField.copyFrom];
24411
- else
24412
- 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(", ")})`);
24500
+ 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(", ")})`);
24413
24501
  }
24414
24502
  } catch (error) {
24415
24503
  const err2 = new Error(`Resolving dimension for field "${fieldKey}" of producer "${producer.name}" failed (index: ${recordIndex}): ${error.message}`, { cause: error });
@@ -24522,11 +24610,69 @@ var ConsumerExecutorClass = class {
24522
24610
  }
24523
24611
  return record;
24524
24612
  };
24613
+ this.processLineCount = async (consumer, datasetPath, executorResults) => {
24614
+ const internalRecordFormat = OutputExecutor_default._getInternalRecordFormat(consumer);
24615
+ const internalFields = ConsumerManager_default.getExpandedFields(consumer);
24616
+ const lineCountFields = ConsumerManager_default.getLineCountFieldsFromExpanded(internalFields, true);
24617
+ if (lineCountFields.length === 0)
24618
+ return 0;
24619
+ const tempWorkPath = datasetPath + "_tmp";
24620
+ const reader = import_fs10.default.createReadStream(datasetPath);
24621
+ const lineReader = import_readline6.default.createInterface({ input: reader, crlfDelay: Infinity });
24622
+ const writer = import_fs10.default.createWriteStream(tempWorkPath);
24623
+ const waitForDrain = () => new Promise((resolve) => writer.once("drain", resolve));
24624
+ let workerIndex = 0;
24625
+ let currentWorker = executorResults[workerIndex] ?? null;
24626
+ let remainingLinesInWorker = currentWorker?.outputCount ?? 0;
24627
+ let processedLineCount = 0;
24628
+ const advanceWorker = () => {
24629
+ while (currentWorker && remainingLinesInWorker <= 0) {
24630
+ workerIndex++;
24631
+ currentWorker = executorResults[workerIndex] ?? null;
24632
+ remainingLinesInWorker = currentWorker?.outputCount ?? 0;
24633
+ }
24634
+ };
24635
+ advanceWorker();
24636
+ for await (const line of lineReader) {
24637
+ advanceWorker();
24638
+ Affirm_default(currentWorker, `Unable to resolve worker metadata while processing lineCount for ${datasetPath}`);
24639
+ const rowOffset = currentWorker.rowOffset ?? 0;
24640
+ if (rowOffset > 0) {
24641
+ const record = this._parseLine(line, internalRecordFormat, internalFields);
24642
+ for (const field of lineCountFields) {
24643
+ const currentValue = Number(record[field.finalKey]);
24644
+ Affirm_default(!Number.isNaN(currentValue), `Invalid lineCount value found while processing ${datasetPath}`);
24645
+ record[field.finalKey] = currentValue + rowOffset;
24646
+ }
24647
+ if (!writer.write(OutputExecutor_default.outputRecord(record, consumer, internalFields) + "\n"))
24648
+ await waitForDrain();
24649
+ } else if (!writer.write(line + "\n")) {
24650
+ await waitForDrain();
24651
+ }
24652
+ remainingLinesInWorker--;
24653
+ processedLineCount++;
24654
+ }
24655
+ lineReader.close();
24656
+ await new Promise((resolve, reject) => {
24657
+ writer.on("close", resolve);
24658
+ writer.on("error", reject);
24659
+ writer.end();
24660
+ });
24661
+ if (!reader.destroyed) {
24662
+ await new Promise((resolve) => {
24663
+ reader.once("close", resolve);
24664
+ reader.destroy();
24665
+ });
24666
+ }
24667
+ await import_promises8.default.unlink(datasetPath);
24668
+ await import_promises8.default.rename(tempWorkPath, datasetPath);
24669
+ return processedLineCount;
24670
+ };
24525
24671
  this.processDistinct = async (datasetPath) => {
24526
- const reader = import_fs11.default.createReadStream(datasetPath);
24672
+ const reader = import_fs10.default.createReadStream(datasetPath);
24527
24673
  const lineReader = import_readline6.default.createInterface({ input: reader, crlfDelay: Infinity });
24528
24674
  const tempWorkPath = datasetPath + "_tmp";
24529
- const writer = import_fs11.default.createWriteStream(tempWorkPath);
24675
+ const writer = import_fs10.default.createWriteStream(tempWorkPath);
24530
24676
  const waitForDrain = () => new Promise((resolve) => writer.once("drain", resolve));
24531
24677
  let newLineCount = 0;
24532
24678
  const seen = /* @__PURE__ */ new Set();
@@ -24551,12 +24697,12 @@ var ConsumerExecutorClass = class {
24551
24697
  reader.destroy();
24552
24698
  });
24553
24699
  }
24554
- await import_promises9.default.unlink(datasetPath);
24555
- await import_promises9.default.rename(tempWorkPath, datasetPath);
24700
+ await import_promises8.default.unlink(datasetPath);
24701
+ await import_promises8.default.rename(tempWorkPath, datasetPath);
24556
24702
  return newLineCount;
24557
24703
  };
24558
24704
  this.processDistinctOn = async (consumer, datasetPath) => {
24559
- const reader = import_fs11.default.createReadStream(datasetPath);
24705
+ const reader = import_fs10.default.createReadStream(datasetPath);
24560
24706
  const lineReader = import_readline6.default.createInterface({ input: reader, crlfDelay: Infinity });
24561
24707
  const { distinctOn } = consumer.options;
24562
24708
  const { keys, resolution } = distinctOn;
@@ -24579,7 +24725,7 @@ var ConsumerExecutorClass = class {
24579
24725
  }
24580
24726
  lineReader.close();
24581
24727
  const tempWorkPath = datasetPath + "_tmp";
24582
- const writer = import_fs11.default.createWriteStream(tempWorkPath);
24728
+ const writer = import_fs10.default.createWriteStream(tempWorkPath);
24583
24729
  const waitForDrain = () => new Promise((resolve) => writer.once("drain", resolve));
24584
24730
  for (const { line } of winners.values()) {
24585
24731
  if (!writer.write(line + "\n"))
@@ -24596,8 +24742,8 @@ var ConsumerExecutorClass = class {
24596
24742
  reader.destroy();
24597
24743
  });
24598
24744
  }
24599
- await import_promises9.default.unlink(datasetPath);
24600
- await import_promises9.default.rename(tempWorkPath, datasetPath);
24745
+ await import_promises8.default.unlink(datasetPath);
24746
+ await import_promises8.default.rename(tempWorkPath, datasetPath);
24601
24747
  return winners.size;
24602
24748
  };
24603
24749
  this.processPivot = async (consumer, datasetPath) => {
@@ -24609,7 +24755,7 @@ var ConsumerExecutorClass = class {
24609
24755
  if (!pivotValues) {
24610
24756
  pivotValues = [];
24611
24757
  const discoverySet = /* @__PURE__ */ new Set();
24612
- const discoverReader = import_fs11.default.createReadStream(datasetPath);
24758
+ const discoverReader = import_fs10.default.createReadStream(datasetPath);
24613
24759
  const discoverLineReader = import_readline6.default.createInterface({ input: discoverReader, crlfDelay: Infinity });
24614
24760
  for await (const line of discoverLineReader) {
24615
24761
  const record = this._parseLine(line, internalRecordFormat, internalFields);
@@ -24628,7 +24774,7 @@ var ConsumerExecutorClass = class {
24628
24774
  }
24629
24775
  }
24630
24776
  const groups = /* @__PURE__ */ new Map();
24631
- const reader = import_fs11.default.createReadStream(datasetPath);
24777
+ const reader = import_fs10.default.createReadStream(datasetPath);
24632
24778
  const lineReader = import_readline6.default.createInterface({ input: reader, crlfDelay: Infinity });
24633
24779
  for await (const line of lineReader) {
24634
24780
  const record = this._parseLine(line, internalRecordFormat, internalFields);
@@ -24656,7 +24802,7 @@ var ConsumerExecutorClass = class {
24656
24802
  ...pivotValues.map((pv) => ({ cField: { key: columnPrefix + pv }, finalKey: columnPrefix + pv }))
24657
24803
  ];
24658
24804
  const tempWorkPath = datasetPath + "_tmp";
24659
- const writer = import_fs11.default.createWriteStream(tempWorkPath);
24805
+ const writer = import_fs10.default.createWriteStream(tempWorkPath);
24660
24806
  const waitForDrain = () => new Promise((resolve) => writer.once("drain", resolve));
24661
24807
  let outputCount = 0;
24662
24808
  for (const { rowRecord, cells } of groups.values()) {
@@ -24702,8 +24848,8 @@ var ConsumerExecutorClass = class {
24702
24848
  reader.destroy();
24703
24849
  });
24704
24850
  }
24705
- await import_promises9.default.unlink(datasetPath);
24706
- await import_promises9.default.rename(tempWorkPath, datasetPath);
24851
+ await import_promises8.default.unlink(datasetPath);
24852
+ await import_promises8.default.rename(tempWorkPath, datasetPath);
24707
24853
  return outputCount;
24708
24854
  };
24709
24855
  this._parseLine = (line, format2, fields) => {
@@ -24749,7 +24895,7 @@ var ConsumerExecutorClass = class {
24749
24895
  for (const fieldKey of uniqueFieldKeys) {
24750
24896
  fieldValueSets.set(fieldKey, /* @__PURE__ */ new Set());
24751
24897
  }
24752
- const reader = import_fs11.default.createReadStream(datasetPath);
24898
+ const reader = import_fs10.default.createReadStream(datasetPath);
24753
24899
  const lineReader = import_readline6.default.createInterface({ input: reader, crlfDelay: Infinity });
24754
24900
  for await (const line of lineReader) {
24755
24901
  rowCount++;
@@ -24797,11 +24943,11 @@ var ConsumerExecutor = new ConsumerExecutorClass();
24797
24943
  var ConsumerExecutor_default = ConsumerExecutor;
24798
24944
 
24799
24945
  // ../../packages/executors/src/Executor.ts
24800
- var import_fs12 = __toESM(require("fs"));
24946
+ var import_fs11 = __toESM(require("fs"));
24801
24947
  var import_readline7 = __toESM(require("readline"));
24802
24948
 
24803
24949
  // ../../packages/executors/src/ProducerExecutor.ts
24804
- var import_path17 = __toESM(require("path"));
24950
+ var import_path16 = __toESM(require("path"));
24805
24951
  var ProducerExecutorClass = class {
24806
24952
  constructor() {
24807
24953
  this.ready = async (producer, scope) => {
@@ -24827,7 +24973,7 @@ var ProducerExecutorClass = class {
24827
24973
  counter = performance.now();
24828
24974
  for (const dimension of dimensions) {
24829
24975
  if (dimension.prodDimension.sourceFilename === true)
24830
- record[dimension.name] = import_path17.default.basename(chunk.fileUri);
24976
+ record[dimension.name] = import_path16.default.basename(chunk.fileUri);
24831
24977
  const maskType = ProducerManager_default.getMask(dimension.prodDimension);
24832
24978
  if (Algo_default.hasVal(maskType))
24833
24979
  record[dimension.name] = CryptoEngine_default.hashValue(maskType, record[dimension.name]?.toString(), dimension.prodDimension.type);
@@ -24878,10 +25024,14 @@ var Executor = class {
24878
25024
  elapsedMS: -1,
24879
25025
  inputCount: -1,
24880
25026
  outputCount: -1,
24881
- resultUri: ExecutorScope_default2.getWorkerPath(scope, workerId),
25027
+ chunkIndex: chunk.index,
25028
+ fileUri: chunk.fileUri,
25029
+ rowOffset: chunk.rowOffset ?? -1,
25030
+ rowCount: -1,
25031
+ resultUri: ExecutorScope_default.getWorkerPath(scope, workerId),
24882
25032
  operations: {}
24883
25033
  };
24884
- ExecutorScope_default2.ensurePath(result.resultUri);
25034
+ ExecutorScope_default.ensurePath(result.resultUri);
24885
25035
  Logger_default.log(`[${workerId}] Starting execution for producer "${producer.name}" \u2192 consumer "${consumer.name}" (file: ${chunk.fileUri})${recordLimit ? ` (limit: ${recordLimit})` : ""}`);
24886
25036
  let totalOutputCount = 0, totalCycles = 1, perf = 0, lineIndex = 0;
24887
25037
  const readStream = this.openReadStream(chunk);
@@ -24987,17 +25137,18 @@ var Executor = class {
24987
25137
  result.cycles = totalCycles;
24988
25138
  result.inputCount = lineIndex;
24989
25139
  result.outputCount = totalOutputCount;
25140
+ result.rowCount = lineIndex;
24990
25141
  result.operations = this._performance.getOperations();
24991
25142
  Logger_default.log(`[${workerId}] Finished: ${lineIndex} input \u2192 ${totalOutputCount} output in ${Math.round(result.elapsedMS)}ms (${totalCycles} cycle(s))`);
24992
25143
  return result;
24993
25144
  };
24994
25145
  this.openReadStream = (chunk) => {
24995
25146
  const { end, fileUri, start } = chunk;
24996
- return import_fs12.default.createReadStream(fileUri, { start, end: end - 1 });
25147
+ return import_fs11.default.createReadStream(fileUri, { start, end: end - 1 });
24997
25148
  };
24998
25149
  this.openWriteStream = (scope, workerId) => {
24999
- const workerPath = ExecutorScope_default2.getWorkerPath(scope, workerId);
25000
- return import_fs12.default.createWriteStream(workerPath);
25150
+ const workerPath = ExecutorScope_default.getWorkerPath(scope, workerId);
25151
+ return import_fs11.default.createWriteStream(workerPath);
25001
25152
  };
25002
25153
  this.shouldProcessFirstLine = (producer) => {
25003
25154
  Affirm_default(producer, "Invalid producer");
@@ -25028,14 +25179,13 @@ var Executor = class {
25028
25179
  var Executor_default = Executor;
25029
25180
 
25030
25181
  // ../../packages/executors/src/ExecutorOrchestrator.ts
25031
- var import_os = __toESM(require("os"));
25032
- var import_fs13 = __toESM(require("fs"));
25033
- var import_promises10 = __toESM(require("fs/promises"));
25182
+ var import_fs12 = __toESM(require("fs"));
25183
+ var import_promises9 = __toESM(require("fs/promises"));
25034
25184
  var import_path18 = __toESM(require("path"));
25035
25185
  var import_workerpool = __toESM(require("workerpool"));
25036
25186
 
25037
25187
  // ../../packages/executors/src/ExecutorWriter.ts
25038
- var fs17 = __toESM(require("fs"));
25188
+ var fs16 = __toESM(require("fs"));
25039
25189
  var import_readline8 = __toESM(require("readline"));
25040
25190
  var ExecutorWriter = class {
25041
25191
  constructor() {
@@ -25052,11 +25202,11 @@ var ExecutorWriter = class {
25052
25202
  };
25053
25203
  this.splitBySize = async (scope, sourcePath) => {
25054
25204
  const maxOutputFileSize = scope.limitFileSize * this.FAKE_GB;
25055
- const readStream = fs17.createReadStream(sourcePath);
25205
+ const readStream = fs16.createReadStream(sourcePath);
25056
25206
  const reader = import_readline8.default.createInterface({ input: readStream, crlfDelay: Infinity });
25057
25207
  let writerIndex = 0;
25058
25208
  let destPath = this.getCompletedPath(sourcePath, writerIndex);
25059
- let writeStream = fs17.createWriteStream(destPath, { flags: "a" });
25209
+ let writeStream = fs16.createWriteStream(destPath, { flags: "a" });
25060
25210
  const waitForDrain = () => new Promise((resolve) => writeStream.once("drain", resolve));
25061
25211
  for await (const line of reader) {
25062
25212
  if (readStream.bytesRead > maxOutputFileSize * (writerIndex + 1)) {
@@ -25067,7 +25217,7 @@ var ExecutorWriter = class {
25067
25217
  });
25068
25218
  writerIndex++;
25069
25219
  destPath = this.getCompletedPath(sourcePath, writerIndex);
25070
- writeStream = fs17.createWriteStream(destPath, { flags: "a" });
25220
+ writeStream = fs16.createWriteStream(destPath, { flags: "a" });
25071
25221
  }
25072
25222
  if (!writeStream.write(line + "\n"))
25073
25223
  await waitForDrain();
@@ -25077,7 +25227,7 @@ var ExecutorWriter = class {
25077
25227
  writeStream.on("finish", resolve);
25078
25228
  writeStream.on("error", reject);
25079
25229
  });
25080
- await fs17.promises.unlink(sourcePath);
25230
+ await fs16.promises.unlink(sourcePath);
25081
25231
  };
25082
25232
  /**
25083
25233
  * Manage the Writestream for main.dataset
@@ -25134,7 +25284,7 @@ var ExecutorWriter = class {
25134
25284
  var ExecutorWriter_default = ExecutorWriter;
25135
25285
 
25136
25286
  // ../../packages/executors/src/ExecutorOrchestrator.ts
25137
- var import_promises11 = require("stream/promises");
25287
+ var import_promises10 = require("stream/promises");
25138
25288
 
25139
25289
  // ../../packages/executors/src/cli_progress/ExecutorProgress2.ts
25140
25290
  var ExecutorProgress2 = class {
@@ -25170,19 +25320,111 @@ var ExecutorProgress2 = class {
25170
25320
  };
25171
25321
  var ExecutorProgress2_default = ExecutorProgress2;
25172
25322
 
25323
+ // ../../packages/executors/src/OrchestratorHelper.ts
25324
+ var import_os = __toESM(require("os"));
25325
+ var import_path17 = __toESM(require("path"));
25326
+ var OrchestratorHelper = {
25327
+ getMemoryUsage: () => {
25328
+ const processMemory = process.memoryUsage();
25329
+ const freeSystemMemory = import_os.default.freemem();
25330
+ return {
25331
+ /**
25332
+ * resident set size (heap + code + stack)
25333
+ */
25334
+ rss: Formatter_default.bytes(processMemory.rss),
25335
+ heapUsed: Formatter_default.bytes(processMemory.heapUsed),
25336
+ heapTotal: Formatter_default.bytes(processMemory.heapTotal),
25337
+ heapPercent: Algo_default.round(processMemory.heapUsed / processMemory.heapTotal, 1),
25338
+ external: Formatter_default.bytes(processMemory.external),
25339
+ free: Formatter_default.bytes(freeSystemMemory)
25340
+ };
25341
+ },
25342
+ formatMemoryUsage: () => {
25343
+ return `Memory [total: ${OrchestratorHelper.getMemoryUsage().rss} - heap: ${OrchestratorHelper.getMemoryUsage().heapPercent}%]`;
25344
+ },
25345
+ computeFinalResult: (tracker, executorResults, executionId, resultUri) => {
25346
+ const result = {
25347
+ cycles: Algo_default.max(executorResults.map((x) => x.cycles)),
25348
+ elapsedMS: Algo_default.sum(executorResults.map((x) => x.elapsedMS)),
25349
+ inputCount: Algo_default.sum(executorResults.map((x) => x.inputCount)),
25350
+ outputCount: Algo_default.sum(executorResults.map((x) => x.outputCount)),
25351
+ workerCount: executorResults.length,
25352
+ executionId,
25353
+ resultUri,
25354
+ operations: {}
25355
+ };
25356
+ for (const res of executorResults) {
25357
+ for (const opKey of Object.keys(res.operations)) {
25358
+ const op = res.operations[opKey];
25359
+ let label = result.operations[opKey];
25360
+ if (!label) {
25361
+ result.operations[opKey] = { avg: -1, max: -1, min: -1, elapsedMS: [] };
25362
+ label = result.operations[opKey];
25363
+ }
25364
+ label.elapsedMS.push(op.elapsedMS);
25365
+ }
25366
+ for (const opKey of Object.keys(result.operations)) {
25367
+ const operation = result.operations[opKey];
25368
+ if (operation.elapsedMS.length > 0) {
25369
+ operation.min = Math.min(...operation.elapsedMS);
25370
+ operation.max = Math.max(...operation.elapsedMS);
25371
+ operation.avg = Algo_default.mean(operation.elapsedMS);
25372
+ }
25373
+ }
25374
+ }
25375
+ const trackerOperations = tracker.getOperations();
25376
+ for (const opKey of Object.keys(trackerOperations)) {
25377
+ const trackerOp = trackerOperations[opKey];
25378
+ const value = trackerOp.elapsedMS;
25379
+ if (!result.operations[opKey]) {
25380
+ result.operations[opKey] = { avg: value, max: value, min: value, elapsedMS: [] };
25381
+ }
25382
+ result.operations[opKey].elapsedMS.push(value);
25383
+ }
25384
+ return result;
25385
+ },
25386
+ /**
25387
+ * Returns the path to the worker thread file in the build (different between dev, cli and prod (docker)).
25388
+ * IMPORTANT!: when moving this (OrchestratorHelper.ts) file, or the workers output, you have to make sure to update these paths
25389
+ */
25390
+ getPhysicalWorkerPath: () => {
25391
+ const currentDir = __dirname;
25392
+ if (ProcessENVManager_default.getEnvVariable("NODE_ENV") === "dev" || ProcessENVManager_default.getEnvVariable("NODE_ENV") === "development")
25393
+ return import_path17.default.resolve("./.build/workers");
25394
+ const forcedPath = ProcessENVManager_default.getEnvVariable("REMORA_WORKERS_PATH");
25395
+ if (forcedPath && forcedPath.length > 0)
25396
+ return import_path17.default.join(__dirname, forcedPath);
25397
+ if (!currentDir.includes(".build")) {
25398
+ return import_path17.default.join(__dirname, "../workers");
25399
+ } else {
25400
+ return import_path17.default.resolve("./.build/workers");
25401
+ }
25402
+ },
25403
+ getParallelWorkerCount: () => {
25404
+ const cpuBoundWorkers = Math.max(1, Math.floor(import_os.default.cpus().length * 0.7));
25405
+ const totalMemoryMB = Math.floor(import_os.default.totalmem() / (1024 * 1024));
25406
+ const reservedMemoryMB = Constants_default.defaults.MIN_RUNTIME_HEAP_MB * 2;
25407
+ const availableMemoryForWorkersMB = Math.max(Constants_default.defaults.MIN_RUNTIME_HEAP_MB, totalMemoryMB - reservedMemoryMB);
25408
+ const memoryBoundWorkers = Math.max(1, Math.floor(availableMemoryForWorkersMB / Constants_default.defaults.MIN_RUNTIME_HEAP_MB));
25409
+ return Math.min(cpuBoundWorkers, Constants_default.defaults.MAX_THREAD_COUNT, memoryBoundWorkers);
25410
+ }
25411
+ };
25412
+ var OrchestratorHelper_default = OrchestratorHelper;
25413
+
25173
25414
  // ../../packages/executors/src/ExecutorOrchestrator.ts
25174
25415
  var ExecutorOrchestratorClass = class {
25175
25416
  constructor() {
25176
- this.createPool = () => {
25417
+ this.createPool = (maxWorkers) => {
25177
25418
  const options = {
25419
+ maxWorkers,
25178
25420
  workerThreadOpts: {
25179
25421
  resourceLimits: {
25180
25422
  maxOldGenerationSizeMb: Constants_default.defaults.MIN_RUNTIME_HEAP_MB
25181
25423
  }
25182
25424
  }
25183
25425
  };
25184
- const workerPath = this._getWorkerPath();
25185
- Logger_default.log(`Initializing worker pool from ${workerPath} (heap limit: ${Constants_default.defaults.MIN_RUNTIME_HEAP_MB}MB)`);
25426
+ const workerPath = OrchestratorHelper_default.getPhysicalWorkerPath();
25427
+ Logger_default.log(`Initializing worker pool from ${workerPath} (workers: ${maxWorkers}, heap limit: ${Constants_default.defaults.MIN_RUNTIME_HEAP_MB}MB) | ${OrchestratorHelper_default.formatMemoryUsage()}`);
25186
25428
  return import_workerpool.default.pool(import_path18.default.join(workerPath, "ExecutorWorker.js"), options);
25187
25429
  };
25188
25430
  this.launch = async (request) => {
@@ -25196,11 +25438,12 @@ var ExecutorOrchestratorClass = class {
25196
25438
  const _progress = new ExecutorProgress2_default(logProgress);
25197
25439
  const { usageId } = UsageManager_default.startUsage(consumer, details);
25198
25440
  const scope = { id: usageId, folder: `${consumer.name}_${usageId}`, workersId: [], limitFileSize: consumer.maximumFileSize };
25199
- const pool = this.createPool();
25441
+ const expandedFields = ConsumerManager_default.getExpandedFields(consumer);
25442
+ let activePool = null;
25200
25443
  try {
25201
25444
  const start = performance.now();
25202
25445
  const executorResults = [];
25203
- Logger_default.log(`[${usageId}] Launching consumer "${consumer.name}" (invoked by: ${details.invokedBy}, user: ${details.user?.name ?? "unknown"}, producer(s): ${consumer.producers.length})`);
25446
+ 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()}`);
25204
25447
  let counter = performance.now();
25205
25448
  _progress.update({ phase: "Preparing source data", progress: 0 });
25206
25449
  let sourceFilesByProducer = await this.readySourceFiles(consumer, scope);
@@ -25216,10 +25459,10 @@ var ExecutorOrchestratorClass = class {
25216
25459
  let globalWorkerIndex = 0;
25217
25460
  for (const pair of sourceFilesByProducer) {
25218
25461
  const { prod, cProd, response } = pair;
25219
- if (!import_fs13.default.existsSync(response.files[0].fullUri)) {
25220
- if (!cProd.isOptional)
25462
+ if (!import_fs12.default.existsSync(response.files[0].fullUri)) {
25463
+ if (!cProd.isOptional) {
25221
25464
  throw new Error(`Expected data file ${response.files[0].fullUri} of producer ${prod.name} in consumer ${consumer.name} is missing.`);
25222
- else if (cProd.isOptional === true) {
25465
+ } else if (cProd.isOptional === true) {
25223
25466
  Logger_default.log(`[${usageId}] Producer "${prod.name}": data file missing but marked optional, skipping`);
25224
25467
  continue;
25225
25468
  }
@@ -25232,35 +25475,42 @@ var ExecutorOrchestratorClass = class {
25232
25475
  for (const [fileIndex, file] of response.files.entries()) {
25233
25476
  const chunks = ExecutorOrchestrator.scopeWork(file.fullUri);
25234
25477
  const workerThreads = [];
25235
- Logger_default.log(`[${usageId}] Producer "${prod.name}" file ${fileIndex + 1}/${totalFiles}: split into ${chunks.length} chunk(s)`);
25236
- for (const chunk of chunks) {
25237
- const workerId = `${usageId}_${globalWorkerIndex}`;
25238
- globalWorkerIndex++;
25239
- const workerData = {
25240
- producer: prod,
25241
- chunk,
25242
- consumer,
25243
- prodDimensions,
25244
- workerId,
25245
- scope,
25246
- options,
25247
- loggerConfig: Logger_default.getConfig()
25248
- };
25249
- scope.workersId.push(workerId);
25250
- 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)`);
25251
- workerThreads.push(pool.exec("executor", [workerData], {
25252
- on: (payload) => this.onWorkAdvanced(payload, workerId, _progress, totalBytesToProcess, bytesProcessedByWorker)
25253
- }).catch((error) => {
25254
- Logger_default.error(error);
25255
- return null;
25256
- }));
25478
+ activePool = this.createPool(chunks.length);
25479
+ Logger_default.log(`[${usageId}] Producer "${prod.name}" file ${fileIndex + 1}/${totalFiles}: split into ${chunks.length} chunk(s) | ${OrchestratorHelper_default.formatMemoryUsage()}`);
25480
+ try {
25481
+ for (const chunk of chunks) {
25482
+ const workerId = `${usageId}_${globalWorkerIndex}`;
25483
+ globalWorkerIndex++;
25484
+ const workerData = {
25485
+ producer: prod,
25486
+ chunk,
25487
+ consumer,
25488
+ prodDimensions,
25489
+ workerId,
25490
+ scope,
25491
+ options,
25492
+ loggerConfig: Logger_default.getConfig()
25493
+ };
25494
+ scope.workersId.push(workerId);
25495
+ 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)`);
25496
+ workerThreads.push(activePool.exec("executor", [workerData], {
25497
+ on: (payload) => this.onWorkAdvanced(payload, workerId, _progress, totalBytesToProcess, bytesProcessedByWorker)
25498
+ }).catch((error) => {
25499
+ Logger_default.error(error);
25500
+ return null;
25501
+ }));
25502
+ }
25503
+ Logger_default.log(`[${usageId}] Waiting for ${workerThreads.length} worker(s) to complete | ${OrchestratorHelper_default.formatMemoryUsage()}`);
25504
+ const fileExecutorResults = await Promise.all(workerThreads);
25505
+ this.assignChunkRowMetadata(chunks, fileExecutorResults);
25506
+ executorResults.push(...fileExecutorResults);
25507
+ Logger_default.log(`[${usageId}] All ${workerThreads.length} worker(s) finished for producer "${prod.name}" file ${fileIndex + 1}/${totalFiles} | ${OrchestratorHelper_default.formatMemoryUsage()}`);
25508
+ } finally {
25509
+ await activePool.terminate();
25510
+ activePool = null;
25257
25511
  }
25258
- Logger_default.log(`[${usageId}] Waiting for ${workerThreads.length} worker(s) to complete`);
25259
- executorResults.push(...await Promise.all(workerThreads));
25260
- Logger_default.log(`[${usageId}] All ${workerThreads.length} worker(s) finished for producer "${prod.name}" file ${fileIndex + 1}/${totalFiles}`);
25261
25512
  }
25262
25513
  }
25263
- await pool.terminate();
25264
25514
  _progress.update({ phase: "Processing data", progress: 1 });
25265
25515
  if (executorResults.some((x) => !Algo_default.hasVal(x)))
25266
25516
  throw new Error(`${executorResults.filter((x) => !Algo_default.hasVal(x)).length} worker(s) failed to produce valid results`);
@@ -25275,7 +25525,7 @@ var ExecutorOrchestratorClass = class {
25275
25525
  if (consumer.options?.distinct === true) {
25276
25526
  Logger_default.log(`[${usageId}] Running unified distinct pass across merged workers`);
25277
25527
  counter = performance.now();
25278
- const unifiedOutputCount = await ConsumerExecutor_default.processDistinct(ExecutorScope_default2.getMainPath(scope));
25528
+ const unifiedOutputCount = await ConsumerExecutor_default.processDistinct(ExecutorScope_default.getMainPath(scope));
25279
25529
  tracker.measure("process-distinct:main", performance.now() - counter);
25280
25530
  postOperation.totalOutputCount = unifiedOutputCount;
25281
25531
  Logger_default.log(`[${usageId}] Distinct pass complete: ${unifiedOutputCount} unique rows in ${Math.round(performance.now() - counter)}ms`);
@@ -25283,24 +25533,31 @@ var ExecutorOrchestratorClass = class {
25283
25533
  if (consumer.options?.distinctOn) {
25284
25534
  Logger_default.log(`[${usageId}] Running unified distinctOn pass (${consumer.options.distinctOn})`);
25285
25535
  counter = performance.now();
25286
- const unifiedOutputCount = await ConsumerExecutor_default.processDistinctOn(consumer, ExecutorScope_default2.getMainPath(scope));
25536
+ const unifiedOutputCount = await ConsumerExecutor_default.processDistinctOn(consumer, ExecutorScope_default.getMainPath(scope));
25287
25537
  tracker.measure("process-distinct-on:main", performance.now() - counter);
25288
25538
  postOperation.totalOutputCount = unifiedOutputCount;
25289
25539
  Logger_default.log(`[${usageId}] DistinctOn pass complete: ${unifiedOutputCount} rows in ${Math.round(performance.now() - counter)}ms`);
25290
25540
  }
25291
- }
25292
- if (consumer.options?.pivot) {
25293
- Logger_default.log(`[${usageId}] Running pivot operation`);
25294
- counter = performance.now();
25295
- const unifiedOutputCount = await ConsumerExecutor_default.processPivot(consumer, ExecutorScope_default2.getMainPath(scope));
25296
- tracker.measure("process-pivot:main", performance.now() - counter);
25297
- postOperation.totalOutputCount = unifiedOutputCount;
25298
- Logger_default.log(`[${usageId}] Pivot complete: ${unifiedOutputCount} rows in ${Math.round(performance.now() - counter)}ms`);
25541
+ if (consumer.options?.pivot) {
25542
+ Logger_default.log(`[${usageId}] Running pivot operation`);
25543
+ counter = performance.now();
25544
+ const unifiedOutputCount = await ConsumerExecutor_default.processPivot(consumer, ExecutorScope_default.getMainPath(scope));
25545
+ tracker.measure("process-pivot:main", performance.now() - counter);
25546
+ postOperation.totalOutputCount = unifiedOutputCount;
25547
+ Logger_default.log(`[${usageId}] Pivot complete: ${unifiedOutputCount} rows in ${Math.round(performance.now() - counter)}ms`);
25548
+ }
25549
+ if (ConsumerManager_default.hasLineCountFields(consumer, true)) {
25550
+ Logger_default.log(`[${usageId}] Running unified lineCount pass across merged workers`);
25551
+ counter = performance.now();
25552
+ const processedLineCount = await ConsumerExecutor_default.processLineCount(consumer, ExecutorScope_default.getMainPath(scope), executorResults);
25553
+ tracker.measure("process-line-count:main", performance.now() - counter);
25554
+ Logger_default.log(`[${usageId}] LineCount pass complete: normalized ${processedLineCount} rows in ${Math.round(performance.now() - counter)}ms`);
25555
+ }
25299
25556
  }
25300
25557
  if (consumer.validate && consumer.validate.length > 0) {
25301
25558
  Logger_default.log(`[${usageId}] Running dataset-level validations`);
25302
25559
  counter = performance.now();
25303
- const validationResults = await ConsumerExecutor_default.processDatasetValidation(consumer, ExecutorScope_default2.getMainPath(scope));
25560
+ const validationResults = await ConsumerExecutor_default.processDatasetValidation(consumer, ExecutorScope_default.getMainPath(scope));
25304
25561
  tracker.measure("dataset-validation", performance.now() - counter);
25305
25562
  for (const result of validationResults) {
25306
25563
  if (result.onFail === "fail") {
@@ -25317,7 +25574,7 @@ var ExecutorOrchestratorClass = class {
25317
25574
  Logger_default.log(`[${usageId}] Splitting output by size limit (${scope.limitFileSize})`);
25318
25575
  counter = performance.now();
25319
25576
  const writer = new ExecutorWriter_default();
25320
- await writer.splitBySize(scope, ExecutorScope_default2.getMainPath(scope));
25577
+ await writer.splitBySize(scope, ExecutorScope_default.getMainPath(scope));
25321
25578
  tracker.measure("split-by-size", performance.now() - counter);
25322
25579
  Logger_default.log(`[${usageId}] Split complete in ${Math.round(performance.now() - counter)}ms`);
25323
25580
  }
@@ -25325,7 +25582,7 @@ var ExecutorOrchestratorClass = class {
25325
25582
  _progress.update({ phase: "Exporting result", progress: 0 });
25326
25583
  counter = performance.now();
25327
25584
  Logger_default.log(`[${usageId}] Exporting results to ${consumer.outputs.length} output(s)`);
25328
- const exportRes = await OutputExecutor_default.exportResult(consumer, ConsumerManager_default.getExpandedFields(consumer), scope);
25585
+ const exportRes = await OutputExecutor_default.exportResult(consumer, expandedFields, scope);
25329
25586
  tracker.measure("export-result", performance.now() - counter);
25330
25587
  Logger_default.log(`[${usageId}] Export complete in ${Math.round(performance.now() - counter)}ms (key: ${exportRes.key})`);
25331
25588
  _progress.update({ phase: "Exporting result", progress: 1 });
@@ -25336,9 +25593,9 @@ var ExecutorOrchestratorClass = class {
25336
25593
  tracker.measure("on-success-actions", performance.now() - counter);
25337
25594
  Logger_default.log(`[${usageId}] On-success actions complete in ${Math.round(performance.now() - counter)}ms`);
25338
25595
  }
25339
- Logger_default.log(`[${usageId}] Starting cleanup operations`);
25596
+ Logger_default.log(`[${usageId}] Starting cleanup operations | ${OrchestratorHelper_default.formatMemoryUsage()}`);
25340
25597
  await this.performCleanupOperations(scope, tracker);
25341
- const finalResult = this.computeFinalResult(tracker, executorResults, usageId, exportRes.key);
25598
+ const finalResult = OrchestratorHelper_default.computeFinalResult(tracker, executorResults, usageId, exportRes.key);
25342
25599
  finalResult.elapsedMS = performance.now() - start;
25343
25600
  if (Algo_default.hasVal(postOperation.totalOutputCount))
25344
25601
  finalResult.outputCount = postOperation.totalOutputCount;
@@ -25347,9 +25604,10 @@ var ExecutorOrchestratorClass = class {
25347
25604
  await Logger_default.flush();
25348
25605
  return finalResult;
25349
25606
  } catch (error) {
25350
- Logger_default.log(`[${usageId}] Consumer "${consumer.name}" failed: ${Helper_default.asError(error).message}`);
25607
+ Logger_default.log(`[${usageId}] Consumer "${consumer.name}" failed: ${Helper_default.asError(error).message} | ${OrchestratorHelper_default.formatMemoryUsage()}`);
25351
25608
  Logger_default.error(error);
25352
- await pool.terminate();
25609
+ if (activePool)
25610
+ await activePool.terminate();
25353
25611
  await ConsumerOnFinishManager_default.onConsumerError(consumer, usageId);
25354
25612
  Logger_default.log(`[${usageId}] Running cleanup after failure`);
25355
25613
  await this.performCleanupOperations(scope, tracker);
@@ -25364,18 +25622,17 @@ var ExecutorOrchestratorClass = class {
25364
25622
  * Returns a single chunk for small files where parallelism overhead isn't worth it.
25365
25623
  */
25366
25624
  this.scopeWork = (fileUri, numChunks) => {
25367
- const fileSize = import_fs13.default.statSync(fileUri).size;
25625
+ const fileSize = import_fs12.default.statSync(fileUri).size;
25368
25626
  if (fileSize === 0) return [];
25369
25627
  if (fileSize < Constants_default.defaults.MIN_FILE_SIZE_FOR_PARALLEL) {
25370
25628
  return [{ start: 0, end: fileSize, isFirstChunk: true, fileUri, index: 0 }];
25371
25629
  }
25372
- const availableCores = Math.max(1, Math.floor(import_os.default.cpus().length * 0.7));
25373
- const cpus = numChunks ?? Math.min(availableCores, Constants_default.defaults.MAX_THREAD_COUNT);
25630
+ const targetWorkers = numChunks ?? OrchestratorHelper_default.getParallelWorkerCount();
25374
25631
  const maxChunksBySize = Math.floor(fileSize / Constants_default.defaults.MIN_CHUNK_SIZE);
25375
- const effectiveChunks = Math.min(cpus, maxChunksBySize);
25632
+ const effectiveChunks = Math.min(targetWorkers, maxChunksBySize);
25376
25633
  if (effectiveChunks <= 1) return [{ start: 0, end: fileSize, isFirstChunk: true, fileUri, index: 0 }];
25377
25634
  const targetChunkSize = Math.floor(fileSize / effectiveChunks);
25378
- const fd = import_fs13.default.openSync(fileUri, "r");
25635
+ const fd = import_fs12.default.openSync(fileUri, "r");
25379
25636
  try {
25380
25637
  const offsets = [];
25381
25638
  let currentStart = 0;
@@ -25393,7 +25650,23 @@ var ExecutorOrchestratorClass = class {
25393
25650
  }
25394
25651
  return offsets;
25395
25652
  } finally {
25396
- import_fs13.default.closeSync(fd);
25653
+ import_fs12.default.closeSync(fd);
25654
+ }
25655
+ };
25656
+ this.assignChunkRowMetadata = (chunks, executorResults) => {
25657
+ const validResults = executorResults.filter((result) => Algo_default.hasVal(result));
25658
+ const resultsByChunkIndex = new Map(validResults.map((result) => [result.chunkIndex, result]));
25659
+ let nextRowOffset = 0;
25660
+ for (const chunk of chunks) {
25661
+ const result = resultsByChunkIndex.get(chunk.index);
25662
+ if (!result)
25663
+ continue;
25664
+ const rowCount = result.inputCount;
25665
+ chunk.rowOffset = nextRowOffset;
25666
+ chunk.rowCount = rowCount;
25667
+ result.rowOffset = nextRowOffset;
25668
+ result.rowCount = rowCount;
25669
+ nextRowOffset += rowCount;
25397
25670
  }
25398
25671
  };
25399
25672
  /**
@@ -25406,7 +25679,7 @@ var ExecutorOrchestratorClass = class {
25406
25679
  let currentPos = position;
25407
25680
  while (currentPos < fileSize) {
25408
25681
  const bytesToRead = Math.min(BUFFER_SIZE, fileSize - currentPos);
25409
- const bytesRead = import_fs13.default.readSync(fd, buffer, 0, bytesToRead, currentPos);
25682
+ const bytesRead = import_fs12.default.readSync(fd, buffer, 0, bytesToRead, currentPos);
25410
25683
  if (bytesRead === 0) break;
25411
25684
  for (let i = 0; i < bytesRead; i++) {
25412
25685
  if (buffer[i] === 10) {
@@ -25470,21 +25743,21 @@ var ExecutorOrchestratorClass = class {
25470
25743
  startRow: prod.settings.startRow,
25471
25744
  startColumn: prod.settings.startColumn
25472
25745
  });
25473
- await (0, import_promises11.pipeline)(
25746
+ await (0, import_promises10.pipeline)(
25474
25747
  xlsCsvStream,
25475
- import_fs13.default.createWriteStream(decodedPath)
25748
+ import_fs12.default.createWriteStream(decodedPath)
25476
25749
  );
25477
- const fileStats = await import_promises10.default.stat(decodedPath);
25750
+ const fileStats = await import_promises9.default.stat(decodedPath);
25478
25751
  decodedFiles.push({ fullUri: decodedPath, fileSize: fileStats.size });
25479
25752
  decodedCount++;
25480
25753
  continue;
25481
25754
  }
25482
25755
  if (inferredType === "XML") {
25483
- const fileContent = await import_promises10.default.readFile(file.fullUri, "utf-8");
25756
+ const fileContent = await import_promises9.default.readFile(file.fullUri, "utf-8");
25484
25757
  const jsonData = XMLParser_default.xmlToJson(fileContent);
25485
25758
  const records = normalizeXmlRows(jsonData);
25486
25759
  if (records.length === 0) {
25487
- await import_promises10.default.writeFile(decodedPath, "", "utf-8");
25760
+ await import_promises9.default.writeFile(decodedPath, "", "utf-8");
25488
25761
  } else {
25489
25762
  const columns = [];
25490
25763
  for (const record of records) {
@@ -25500,9 +25773,9 @@ var ExecutorOrchestratorClass = class {
25500
25773
  const row = columns.map((column) => csvSafeValue(record[column]));
25501
25774
  lines.push(CSVParser_default.stringifyRow(row));
25502
25775
  }
25503
- await import_promises10.default.writeFile(decodedPath, lines.join("\n"), "utf-8");
25776
+ await import_promises9.default.writeFile(decodedPath, lines.join("\n"), "utf-8");
25504
25777
  }
25505
- const fileStats = await import_promises10.default.stat(decodedPath);
25778
+ const fileStats = await import_promises9.default.stat(decodedPath);
25506
25779
  decodedFiles.push({ fullUri: decodedPath, fileSize: fileStats.size });
25507
25780
  decodedCount++;
25508
25781
  continue;
@@ -25521,86 +25794,32 @@ var ExecutorOrchestratorClass = class {
25521
25794
  }));
25522
25795
  return decodedResults;
25523
25796
  };
25524
- this._getWorkerPath = () => {
25525
- const currentDir = __dirname;
25526
- if (ProcessENVManager_default.getEnvVariable("NODE_ENV") === "dev" || ProcessENVManager_default.getEnvVariable("NODE_ENV") === "development")
25527
- return import_path18.default.resolve("./.build/workers");
25528
- const forcedPath = ProcessENVManager_default.getEnvVariable("REMORA_WORKERS_PATH");
25529
- if (forcedPath && forcedPath.length > 0)
25530
- return import_path18.default.join(__dirname, forcedPath);
25531
- if (!currentDir.includes(".build")) {
25532
- return import_path18.default.join(__dirname, "../workers");
25533
- } else {
25534
- return import_path18.default.resolve("./.build/workers");
25535
- }
25536
- };
25537
25797
  this.reconcileExecutorThreadsResults = async (scope, executorResults, tracker) => {
25538
- const mainPath = ExecutorScope_default2.getMainPath(scope);
25798
+ const mainPath = ExecutorScope_default.getMainPath(scope);
25539
25799
  ConsumerExecutor_default._ensurePath(mainPath);
25540
25800
  if (executorResults.length > 1) {
25541
25801
  Logger_default.log(`[${scope.id}] Merging ${executorResults.length} worker output files into ${mainPath}`);
25542
25802
  const perf = performance.now();
25543
25803
  for (const workerResult of executorResults) {
25544
- await (0, import_promises11.pipeline)(
25545
- import_fs13.default.createReadStream(workerResult.resultUri),
25546
- import_fs13.default.createWriteStream(mainPath, { flags: "a" })
25804
+ await (0, import_promises10.pipeline)(
25805
+ import_fs12.default.createReadStream(workerResult.resultUri),
25806
+ import_fs12.default.createWriteStream(mainPath, { flags: "a" })
25547
25807
  );
25548
- await import_promises10.default.unlink(workerResult.resultUri);
25808
+ await import_promises9.default.unlink(workerResult.resultUri);
25549
25809
  }
25550
25810
  tracker.measure("merge-workers", performance.now() - perf);
25551
25811
  Logger_default.log(`[${scope.id}] Merge complete in ${Math.round(performance.now() - perf)}ms`);
25552
25812
  } else if (executorResults.length === 1) {
25553
25813
  Logger_default.log(`[${scope.id}] Single worker \u2014 renaming output to ${mainPath}`);
25554
- await import_promises10.default.rename(executorResults[0].resultUri, mainPath);
25814
+ await import_promises9.default.rename(executorResults[0].resultUri, mainPath);
25555
25815
  }
25556
25816
  return mainPath;
25557
25817
  };
25558
25818
  this.performCleanupOperations = async (scope, tracker) => {
25559
25819
  const start = performance.now();
25560
- await ExecutorScope_default2.clearScope(scope);
25820
+ await ExecutorScope_default.clearScope(scope);
25561
25821
  tracker.measure("cleanup-operations", performance.now() - start);
25562
25822
  };
25563
- this.computeFinalResult = (tracker, executorResults, executionId, resultUri) => {
25564
- const result = {
25565
- cycles: Algo_default.max(executorResults.map((x) => x.cycles)),
25566
- elapsedMS: Algo_default.sum(executorResults.map((x) => x.elapsedMS)),
25567
- inputCount: Algo_default.sum(executorResults.map((x) => x.inputCount)),
25568
- outputCount: Algo_default.sum(executorResults.map((x) => x.outputCount)),
25569
- workerCount: executorResults.length,
25570
- executionId,
25571
- resultUri,
25572
- operations: {}
25573
- };
25574
- for (const res of executorResults) {
25575
- for (const opKey of Object.keys(res.operations)) {
25576
- const op = res.operations[opKey];
25577
- let label = result.operations[opKey];
25578
- if (!label) {
25579
- result.operations[opKey] = { avg: -1, max: -1, min: -1, elapsedMS: [] };
25580
- label = result.operations[opKey];
25581
- }
25582
- label.elapsedMS.push(op.elapsedMS);
25583
- }
25584
- for (const opKey of Object.keys(result.operations)) {
25585
- const operation = result.operations[opKey];
25586
- if (operation.elapsedMS.length > 0) {
25587
- operation.min = Math.min(...operation.elapsedMS);
25588
- operation.max = Math.max(...operation.elapsedMS);
25589
- operation.avg = Algo_default.mean(operation.elapsedMS);
25590
- }
25591
- }
25592
- }
25593
- const trackerOperations = tracker.getOperations();
25594
- for (const opKey of Object.keys(trackerOperations)) {
25595
- const trackerOp = trackerOperations[opKey];
25596
- const value = trackerOp.elapsedMS;
25597
- if (!result.operations[opKey]) {
25598
- result.operations[opKey] = { avg: value, max: value, min: value, elapsedMS: [] };
25599
- }
25600
- result.operations[opKey].elapsedMS.push(value);
25601
- }
25602
- return result;
25603
- };
25604
25823
  this.onWorkAdvanced = (packet, workerId, progress, totalBytesToProcess, bytesProcessedByWorker) => {
25605
25824
  const { processed } = packet;
25606
25825
  bytesProcessedByWorker[workerId] = processed;