@forzalabs/remora 2.0.1 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7807,15 +7807,15 @@ var require_index_cjs = __commonJS({
7807
7807
  };
7808
7808
  }
7809
7809
  function wrapConversion(toModel, graph) {
7810
- const path20 = [graph[toModel].parent, toModel];
7810
+ const path21 = [graph[toModel].parent, toModel];
7811
7811
  let fn = convert$1[graph[toModel].parent][toModel];
7812
7812
  let cur = graph[toModel].parent;
7813
7813
  while (graph[cur].parent) {
7814
- path20.unshift(graph[cur].parent);
7814
+ path21.unshift(graph[cur].parent);
7815
7815
  fn = link(convert$1[graph[cur].parent][cur], fn);
7816
7816
  cur = graph[cur].parent;
7817
7817
  }
7818
- fn.conversion = path20;
7818
+ fn.conversion = path21;
7819
7819
  return fn;
7820
7820
  }
7821
7821
  function route(fromModel) {
@@ -8445,7 +8445,7 @@ var require_node2 = __commonJS({
8445
8445
  var require_tail_file = __commonJS({
8446
8446
  "../../node_modules/winston/lib/winston/tail-file.js"(exports2, module2) {
8447
8447
  "use strict";
8448
- var fs23 = require("fs");
8448
+ var fs24 = require("fs");
8449
8449
  var { StringDecoder } = require("string_decoder");
8450
8450
  var { Stream } = require_readable();
8451
8451
  function noop() {
@@ -8466,7 +8466,7 @@ var require_tail_file = __commonJS({
8466
8466
  stream.emit("end");
8467
8467
  stream.emit("close");
8468
8468
  };
8469
- fs23.open(options.file, "a+", "0644", (err2, fd) => {
8469
+ fs24.open(options.file, "a+", "0644", (err2, fd) => {
8470
8470
  if (err2) {
8471
8471
  if (!iter) {
8472
8472
  stream.emit("error", err2);
@@ -8478,10 +8478,10 @@ var require_tail_file = __commonJS({
8478
8478
  }
8479
8479
  (function read() {
8480
8480
  if (stream.destroyed) {
8481
- fs23.close(fd, noop);
8481
+ fs24.close(fd, noop);
8482
8482
  return;
8483
8483
  }
8484
- return fs23.read(fd, buffer, 0, buffer.length, pos, (error, bytes) => {
8484
+ return fs24.read(fd, buffer, 0, buffer.length, pos, (error, bytes) => {
8485
8485
  if (error) {
8486
8486
  if (!iter) {
8487
8487
  stream.emit("error", error);
@@ -8540,8 +8540,8 @@ var require_tail_file = __commonJS({
8540
8540
  var require_file = __commonJS({
8541
8541
  "../../node_modules/winston/lib/winston/transports/file.js"(exports2, module2) {
8542
8542
  "use strict";
8543
- var fs23 = require("fs");
8544
- var path20 = require("path");
8543
+ var fs24 = require("fs");
8544
+ var path21 = require("path");
8545
8545
  var asyncSeries = require_series();
8546
8546
  var zlib3 = require("zlib");
8547
8547
  var { MESSAGE } = require_triple_beam();
@@ -8571,14 +8571,14 @@ var require_file = __commonJS({
8571
8571
  this._onError = this._onError.bind(this);
8572
8572
  if (options.filename || options.dirname) {
8573
8573
  throwIf("filename or dirname", "stream");
8574
- this._basename = this.filename = options.filename ? path20.basename(options.filename) : "winston.log";
8575
- this.dirname = options.dirname || path20.dirname(options.filename);
8574
+ this._basename = this.filename = options.filename ? path21.basename(options.filename) : "winston.log";
8575
+ this.dirname = options.dirname || path21.dirname(options.filename);
8576
8576
  this.options = options.options || { flags: "a" };
8577
8577
  } else if (options.stream) {
8578
8578
  console.warn("options.stream will be removed in winston@4. Use winston.transports.Stream");
8579
8579
  throwIf("stream", "filename", "maxsize");
8580
8580
  this._dest = this._stream.pipe(this._setupStream(options.stream));
8581
- this.dirname = path20.dirname(this._dest.path);
8581
+ this.dirname = path21.dirname(this._dest.path);
8582
8582
  } else {
8583
8583
  throw new Error("Cannot log to file without filename or stream.");
8584
8584
  }
@@ -8741,11 +8741,11 @@ var require_file = __commonJS({
8741
8741
  options = {};
8742
8742
  }
8743
8743
  options = normalizeQuery(options);
8744
- const file = path20.join(this.dirname, this.filename);
8744
+ const file = path21.join(this.dirname, this.filename);
8745
8745
  let buff = "";
8746
8746
  let results = [];
8747
8747
  let row = 0;
8748
- const stream = fs23.createReadStream(file, {
8748
+ const stream = fs24.createReadStream(file, {
8749
8749
  encoding: "utf8"
8750
8750
  });
8751
8751
  stream.on("error", (err2) => {
@@ -8846,7 +8846,7 @@ var require_file = __commonJS({
8846
8846
  * TODO: Refactor me.
8847
8847
  */
8848
8848
  stream(options = {}) {
8849
- const file = path20.join(this.dirname, this.filename);
8849
+ const file = path21.join(this.dirname, this.filename);
8850
8850
  const stream = new Stream();
8851
8851
  const tail = {
8852
8852
  file,
@@ -8896,8 +8896,8 @@ var require_file = __commonJS({
8896
8896
  */
8897
8897
  stat(callback) {
8898
8898
  const target = this._getFile();
8899
- const fullpath = path20.join(this.dirname, target);
8900
- fs23.stat(fullpath, (err2, stat) => {
8899
+ const fullpath = path21.join(this.dirname, target);
8900
+ fs24.stat(fullpath, (err2, stat) => {
8901
8901
  if (err2 && err2.code === "ENOENT") {
8902
8902
  debug("ENOENT\xA0ok", fullpath);
8903
8903
  this.filename = target;
@@ -9000,9 +9000,9 @@ var require_file = __commonJS({
9000
9000
  * @returns {WritableStream} Stream that writes to disk for the active file.
9001
9001
  */
9002
9002
  _createStream(source) {
9003
- const fullpath = path20.join(this.dirname, this.filename);
9003
+ const fullpath = path21.join(this.dirname, this.filename);
9004
9004
  debug("create stream start", fullpath, this.options);
9005
- const dest = fs23.createWriteStream(fullpath, this.options).on("error", (err2) => debug(err2)).on("close", () => debug("close", dest.path, dest.bytesWritten)).on("open", () => {
9005
+ const dest = fs24.createWriteStream(fullpath, this.options).on("error", (err2) => debug(err2)).on("close", () => debug("close", dest.path, dest.bytesWritten)).on("open", () => {
9006
9006
  debug("file open ok", fullpath);
9007
9007
  this.emit("open", fullpath);
9008
9008
  source.pipe(dest);
@@ -9025,16 +9025,16 @@ var require_file = __commonJS({
9025
9025
  */
9026
9026
  _incFile(callback) {
9027
9027
  debug("_incFile", this.filename);
9028
- const ext = path20.extname(this._basename);
9029
- const basename = path20.basename(this._basename, ext);
9028
+ const ext = path21.extname(this._basename);
9029
+ const basename = path21.basename(this._basename, ext);
9030
9030
  const tasks = [];
9031
9031
  if (this.zippedArchive) {
9032
9032
  tasks.push(
9033
9033
  function(cb) {
9034
9034
  const num = this._created > 0 && !this.tailable ? this._created : "";
9035
9035
  this._compressFile(
9036
- path20.join(this.dirname, `${basename}${num}${ext}`),
9037
- path20.join(this.dirname, `${basename}${num}${ext}.gz`),
9036
+ path21.join(this.dirname, `${basename}${num}${ext}`),
9037
+ path21.join(this.dirname, `${basename}${num}${ext}.gz`),
9038
9038
  cb
9039
9039
  );
9040
9040
  }.bind(this)
@@ -9059,8 +9059,8 @@ var require_file = __commonJS({
9059
9059
  * @private
9060
9060
  */
9061
9061
  _getFile() {
9062
- const ext = path20.extname(this._basename);
9063
- const basename = path20.basename(this._basename, ext);
9062
+ const ext = path21.extname(this._basename);
9063
+ const basename = path21.basename(this._basename, ext);
9064
9064
  const isRotation = this.rotationFormat ? this.rotationFormat() : this._created;
9065
9065
  return !this.tailable && this._created ? `${basename}${isRotation}${ext}` : `${basename}${ext}`;
9066
9066
  }
@@ -9080,8 +9080,8 @@ var require_file = __commonJS({
9080
9080
  const isOldest = oldest !== 0 ? oldest : "";
9081
9081
  const isZipped = this.zippedArchive ? ".gz" : "";
9082
9082
  const filePath = `${basename}${isOldest}${ext}${isZipped}`;
9083
- const target = path20.join(this.dirname, filePath);
9084
- fs23.unlink(target, callback);
9083
+ const target = path21.join(this.dirname, filePath);
9084
+ fs24.unlink(target, callback);
9085
9085
  }
9086
9086
  /**
9087
9087
  * Roll files forward based on integer, up to maxFiles. e.g. if base if
@@ -9103,20 +9103,20 @@ var require_file = __commonJS({
9103
9103
  for (let x2 = this.maxFiles - 1; x2 > 1; x2--) {
9104
9104
  tasks.push(function(i6, cb) {
9105
9105
  let fileName = `${basename}${i6 - 1}${ext}${isZipped}`;
9106
- const tmppath = path20.join(this.dirname, fileName);
9107
- fs23.exists(tmppath, (exists) => {
9106
+ const tmppath = path21.join(this.dirname, fileName);
9107
+ fs24.exists(tmppath, (exists) => {
9108
9108
  if (!exists) {
9109
9109
  return cb(null);
9110
9110
  }
9111
9111
  fileName = `${basename}${i6}${ext}${isZipped}`;
9112
- fs23.rename(tmppath, path20.join(this.dirname, fileName), cb);
9112
+ fs24.rename(tmppath, path21.join(this.dirname, fileName), cb);
9113
9113
  });
9114
9114
  }.bind(this, x2));
9115
9115
  }
9116
9116
  asyncSeries(tasks, () => {
9117
- fs23.rename(
9118
- path20.join(this.dirname, `${basename}${ext}${isZipped}`),
9119
- path20.join(this.dirname, `${basename}1${ext}${isZipped}`),
9117
+ fs24.rename(
9118
+ path21.join(this.dirname, `${basename}${ext}${isZipped}`),
9119
+ path21.join(this.dirname, `${basename}1${ext}${isZipped}`),
9120
9120
  callback
9121
9121
  );
9122
9122
  });
@@ -9130,22 +9130,22 @@ var require_file = __commonJS({
9130
9130
  * @private
9131
9131
  */
9132
9132
  _compressFile(src, dest, callback) {
9133
- fs23.access(src, fs23.F_OK, (err2) => {
9133
+ fs24.access(src, fs24.F_OK, (err2) => {
9134
9134
  if (err2) {
9135
9135
  return callback();
9136
9136
  }
9137
9137
  var gzip = zlib3.createGzip();
9138
- var inp = fs23.createReadStream(src);
9139
- var out = fs23.createWriteStream(dest);
9138
+ var inp = fs24.createReadStream(src);
9139
+ var out = fs24.createWriteStream(dest);
9140
9140
  out.on("finish", () => {
9141
- fs23.unlink(src, callback);
9141
+ fs24.unlink(src, callback);
9142
9142
  });
9143
9143
  inp.pipe(gzip).pipe(out);
9144
9144
  });
9145
9145
  }
9146
9146
  _createLogDirIfNotExist(dirPath) {
9147
- if (!fs23.existsSync(dirPath)) {
9148
- fs23.mkdirSync(dirPath, { recursive: true });
9147
+ if (!fs24.existsSync(dirPath)) {
9148
+ fs24.mkdirSync(dirPath, { recursive: true });
9149
9149
  }
9150
9150
  }
9151
9151
  };
@@ -9229,9 +9229,9 @@ var require_http = __commonJS({
9229
9229
  };
9230
9230
  const auth = options.params.auth || null;
9231
9231
  delete options.params.auth;
9232
- const path20 = options.params.path || null;
9232
+ const path21 = options.params.path || null;
9233
9233
  delete options.params.path;
9234
- this._request(options, auth, path20, (err2, res, body) => {
9234
+ this._request(options, auth, path21, (err2, res, body) => {
9235
9235
  if (res && res.statusCode !== 200) {
9236
9236
  err2 = new Error(`Invalid HTTP Status Code: ${res.statusCode}`);
9237
9237
  }
@@ -9259,12 +9259,12 @@ var require_http = __commonJS({
9259
9259
  method: "stream",
9260
9260
  params: options
9261
9261
  };
9262
- const path20 = options.params.path || null;
9262
+ const path21 = options.params.path || null;
9263
9263
  delete options.params.path;
9264
9264
  const auth = options.params.auth || null;
9265
9265
  delete options.params.auth;
9266
9266
  let buff = "";
9267
- const req = this._request(options, auth, path20);
9267
+ const req = this._request(options, auth, path21);
9268
9268
  stream.destroy = () => req.destroy();
9269
9269
  req.on("data", (data) => {
9270
9270
  data = (buff + data).split(/\n+/);
@@ -9290,14 +9290,14 @@ var require_http = __commonJS({
9290
9290
  * @param {string} path - request path
9291
9291
  * @param {function} callback - Continuation to respond to when complete.
9292
9292
  */
9293
- _request(options, auth, path20, callback) {
9293
+ _request(options, auth, path21, callback) {
9294
9294
  options = options || {};
9295
9295
  auth = auth || this.auth;
9296
- path20 = path20 || this.path || "";
9296
+ path21 = path21 || this.path || "";
9297
9297
  if (this.batch) {
9298
- this._doBatch(options, callback, auth, path20);
9298
+ this._doBatch(options, callback, auth, path21);
9299
9299
  } else {
9300
- this._doRequest(options, callback, auth, path20);
9300
+ this._doRequest(options, callback, auth, path21);
9301
9301
  }
9302
9302
  }
9303
9303
  /**
@@ -9307,18 +9307,18 @@ var require_http = __commonJS({
9307
9307
  * @param {Object?} auth - authentication options
9308
9308
  * @param {string} path - request path
9309
9309
  */
9310
- _doBatch(options, callback, auth, path20) {
9310
+ _doBatch(options, callback, auth, path21) {
9311
9311
  this.batchOptions.push(options);
9312
9312
  if (this.batchOptions.length === 1) {
9313
9313
  const me3 = this;
9314
9314
  this.batchCallback = callback;
9315
9315
  this.batchTimeoutID = setTimeout(function() {
9316
9316
  me3.batchTimeoutID = -1;
9317
- me3._doBatchRequest(me3.batchCallback, auth, path20);
9317
+ me3._doBatchRequest(me3.batchCallback, auth, path21);
9318
9318
  }, this.batchInterval);
9319
9319
  }
9320
9320
  if (this.batchOptions.length === this.batchCount) {
9321
- this._doBatchRequest(this.batchCallback, auth, path20);
9321
+ this._doBatchRequest(this.batchCallback, auth, path21);
9322
9322
  }
9323
9323
  }
9324
9324
  /**
@@ -9327,14 +9327,14 @@ var require_http = __commonJS({
9327
9327
  * @param {Object?} auth - authentication options
9328
9328
  * @param {string} path - request path
9329
9329
  */
9330
- _doBatchRequest(callback, auth, path20) {
9330
+ _doBatchRequest(callback, auth, path21) {
9331
9331
  if (this.batchTimeoutID > 0) {
9332
9332
  clearTimeout(this.batchTimeoutID);
9333
9333
  this.batchTimeoutID = -1;
9334
9334
  }
9335
9335
  const batchOptionsCopy = this.batchOptions.slice();
9336
9336
  this.batchOptions = [];
9337
- this._doRequest(batchOptionsCopy, callback, auth, path20);
9337
+ this._doRequest(batchOptionsCopy, callback, auth, path21);
9338
9338
  }
9339
9339
  /**
9340
9340
  * Make a request to a winstond server or any http server which can
@@ -9344,7 +9344,7 @@ var require_http = __commonJS({
9344
9344
  * @param {Object?} auth - authentication options
9345
9345
  * @param {string} path - request path
9346
9346
  */
9347
- _doRequest(options, callback, auth, path20) {
9347
+ _doRequest(options, callback, auth, path21) {
9348
9348
  const headers = Object.assign({}, this.headers);
9349
9349
  if (auth && auth.bearer) {
9350
9350
  headers.Authorization = `Bearer ${auth.bearer}`;
@@ -9354,7 +9354,7 @@ var require_http = __commonJS({
9354
9354
  method: "POST",
9355
9355
  host: this.host,
9356
9356
  port: this.port,
9357
- path: `/${path20.replace(/^\//, "")}`,
9357
+ path: `/${path21.replace(/^\//, "")}`,
9358
9358
  headers,
9359
9359
  auth: auth && auth.username && auth.password ? `${auth.username}:${auth.password}` : "",
9360
9360
  agent: this.agent
@@ -11575,7 +11575,7 @@ var CONSTANTS, Constants_default;
11575
11575
  var init_Constants = __esm({
11576
11576
  "../../packages/constants/src/Constants.ts"() {
11577
11577
  CONSTANTS = {
11578
- cliVersion: "2.0.1",
11578
+ cliVersion: "2.0.3",
11579
11579
  backendVersion: 1,
11580
11580
  backendPort: 5088,
11581
11581
  workerVersion: 2,
@@ -11632,7 +11632,12 @@ var init_Constants = __esm({
11632
11632
  * whole run, so the entire work list is submitted at once and needs an upper bound. A queued
11633
11633
  * task holds references, not copies, so this bounds the work list rather than memory.
11634
11634
  */
11635
- MAX_WORKER_QUEUE_SIZE: 1e5
11635
+ MAX_WORKER_QUEUE_SIZE: 1e5,
11636
+ /**
11637
+ * Rows a sample may ask for out of the pipeline. A sample is a look at the data, never an
11638
+ * export of it — `remora sample -s` and the preview's Data tab are both held to this.
11639
+ */
11640
+ MAX_SAMPLE_ROWS: 100
11636
11641
  }
11637
11642
  };
11638
11643
  Constants_default = CONSTANTS;
@@ -12780,7 +12785,7 @@ var init_Validator = __esm({
12780
12785
  */
12781
12786
  this.detectOnFinishRecursion = (consumer) => {
12782
12787
  const errors = [];
12783
- const path20 = [];
12788
+ const path21 = [];
12784
12789
  const targetsOf = (cons) => {
12785
12790
  const names = [];
12786
12791
  for (const output of cons.outputs ?? []) {
@@ -12792,17 +12797,17 @@ var init_Validator = __esm({
12792
12797
  return names;
12793
12798
  };
12794
12799
  const walk = (name) => {
12795
- if (path20.some((x2) => x2.toLowerCase() === name.toLowerCase())) {
12796
- errors.push(`Consumer "${consumer.name}" has a cyclic run-consumer (onSuccess/onError) chain: ${[...path20, name].join(" -> ")}`);
12800
+ if (path21.some((x2) => x2.toLowerCase() === name.toLowerCase())) {
12801
+ errors.push(`Consumer "${consumer.name}" has a cyclic run-consumer (onSuccess/onError) chain: ${[...path21, name].join(" -> ")}`);
12797
12802
  return true;
12798
12803
  }
12799
12804
  const cons = Environment_default.getConsumer(name);
12800
12805
  if (!cons) return false;
12801
- path20.push(name);
12806
+ path21.push(name);
12802
12807
  for (const target of targetsOf(cons)) {
12803
12808
  if (walk(target)) return true;
12804
12809
  }
12805
- path20.pop();
12810
+ path21.pop();
12806
12811
  return false;
12807
12812
  };
12808
12813
  walk(consumer.name);
@@ -14852,14 +14857,14 @@ var init_readFile = __esm({
14852
14857
  import_promises3 = require("fs/promises");
14853
14858
  filePromises = {};
14854
14859
  fileIntercept = {};
14855
- readFile2 = (path20, options) => {
14856
- if (fileIntercept[path20] !== void 0) {
14857
- return fileIntercept[path20];
14860
+ readFile2 = (path21, options) => {
14861
+ if (fileIntercept[path21] !== void 0) {
14862
+ return fileIntercept[path21];
14858
14863
  }
14859
- if (!filePromises[path20] || options?.ignoreCache) {
14860
- filePromises[path20] = (0, import_promises3.readFile)(path20, "utf8");
14864
+ if (!filePromises[path21] || options?.ignoreCache) {
14865
+ filePromises[path21] = (0, import_promises3.readFile)(path21, "utf8");
14861
14866
  }
14862
- return filePromises[path20];
14867
+ return filePromises[path21];
14863
14868
  };
14864
14869
  }
14865
14870
  });
@@ -14971,8 +14976,8 @@ var init_externalDataInterceptor = __esm({
14971
14976
  getFileRecord() {
14972
14977
  return fileIntercept;
14973
14978
  },
14974
- interceptFile(path20, contents) {
14975
- fileIntercept[path20] = Promise.resolve(contents);
14979
+ interceptFile(path21, contents) {
14980
+ fileIntercept[path21] = Promise.resolve(contents);
14976
14981
  },
14977
14982
  getTokenRecord() {
14978
14983
  return tokenIntercept;
@@ -18052,8 +18057,8 @@ var init_createConfigValueProvider = __esm({
18052
18057
  return endpoint.url.href;
18053
18058
  }
18054
18059
  if ("hostname" in endpoint) {
18055
- const { protocol, hostname, port, path: path20 } = endpoint;
18056
- return `${protocol}//${hostname}${port ? ":" + port : ""}${path20}`;
18060
+ const { protocol, hostname, port, path: path21 } = endpoint;
18061
+ return `${protocol}//${hostname}${port ? ":" + port : ""}${path21}`;
18057
18062
  }
18058
18063
  }
18059
18064
  return endpoint;
@@ -18428,18 +18433,18 @@ var getAttrPathList;
18428
18433
  var init_getAttrPathList = __esm({
18429
18434
  "../../node_modules/@smithy/core/dist-es/submodules/endpoints/util-endpoints/lib/getAttrPathList.js"() {
18430
18435
  init_types2();
18431
- getAttrPathList = (path20) => {
18432
- const parts = path20.split(".");
18436
+ getAttrPathList = (path21) => {
18437
+ const parts = path21.split(".");
18433
18438
  const pathList = [];
18434
18439
  for (const part of parts) {
18435
18440
  const squareBracketIndex = part.indexOf("[");
18436
18441
  if (squareBracketIndex !== -1) {
18437
18442
  if (part.indexOf("]") !== part.length - 1) {
18438
- throw new EndpointError(`Path: '${path20}' does not end with ']'`);
18443
+ throw new EndpointError(`Path: '${path21}' does not end with ']'`);
18439
18444
  }
18440
18445
  const arrayIndex = part.slice(squareBracketIndex + 1, -1);
18441
18446
  if (Number.isNaN(parseInt(arrayIndex))) {
18442
- throw new EndpointError(`Invalid array index: '${arrayIndex}' in path: '${path20}'`);
18447
+ throw new EndpointError(`Invalid array index: '${arrayIndex}' in path: '${path21}'`);
18443
18448
  }
18444
18449
  if (squareBracketIndex !== 0) {
18445
18450
  pathList.push(part.slice(0, squareBracketIndex));
@@ -18460,9 +18465,9 @@ var init_getAttr = __esm({
18460
18465
  "../../node_modules/@smithy/core/dist-es/submodules/endpoints/util-endpoints/lib/getAttr.js"() {
18461
18466
  init_types2();
18462
18467
  init_getAttrPathList();
18463
- getAttr = (value, path20) => getAttrPathList(path20).reduce((acc, index) => {
18468
+ getAttr = (value, path21) => getAttrPathList(path21).reduce((acc, index) => {
18464
18469
  if (typeof acc !== "object") {
18465
- throw new EndpointError(`Index '${index}' in '${path20}' not found in '${JSON.stringify(value)}'`);
18470
+ throw new EndpointError(`Index '${index}' in '${path21}' not found in '${JSON.stringify(value)}'`);
18466
18471
  } else if (Array.isArray(acc)) {
18467
18472
  const i6 = parseInt(index);
18468
18473
  return acc[i6 < 0 ? acc.length + i6 : i6];
@@ -18523,8 +18528,8 @@ var init_parseURL = __esm({
18523
18528
  return value;
18524
18529
  }
18525
18530
  if (typeof value === "object" && "hostname" in value) {
18526
- const { hostname: hostname2, port, protocol: protocol2 = "", path: path20 = "", query = {} } = value;
18527
- const url = new URL(`${protocol2}//${hostname2}${port ? `:${port}` : ""}${path20}`);
18531
+ const { hostname: hostname2, port, protocol: protocol2 = "", path: path21 = "", query = {} } = value;
18532
+ const url = new URL(`${protocol2}//${hostname2}${port ? `:${port}` : ""}${path21}`);
18528
18533
  url.search = Object.entries(query).map(([k7, v3]) => `${k7}=${v3}`).join("&");
18529
18534
  return url;
18530
18535
  }
@@ -20868,11 +20873,11 @@ var init_HttpBindingProtocol = __esm({
20868
20873
  const opTraits = translateTraits(operationSchema.traits);
20869
20874
  if (opTraits.http) {
20870
20875
  request.method = opTraits.http[0];
20871
- const [path20, search] = opTraits.http[1].split("?");
20876
+ const [path21, search] = opTraits.http[1].split("?");
20872
20877
  if (request.path == "/") {
20873
- request.path = path20;
20878
+ request.path = path21;
20874
20879
  } else {
20875
- request.path += path20;
20880
+ request.path += path21;
20876
20881
  }
20877
20882
  const traitSearchParams = new URLSearchParams(search ?? "");
20878
20883
  for (const [key, value] of traitSearchParams) {
@@ -24585,12 +24590,12 @@ or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler conf
24585
24590
  const password = request.password ?? "";
24586
24591
  auth = `${username}:${password}`;
24587
24592
  }
24588
- let path20 = request.path;
24593
+ let path21 = request.path;
24589
24594
  if (queryString) {
24590
- path20 += `?${queryString}`;
24595
+ path21 += `?${queryString}`;
24591
24596
  }
24592
24597
  if (request.fragment) {
24593
- path20 += `#${request.fragment}`;
24598
+ path21 += `#${request.fragment}`;
24594
24599
  }
24595
24600
  let hostname = request.hostname ?? "";
24596
24601
  if (hostname[0] === "[" && hostname.endsWith("]")) {
@@ -24602,7 +24607,7 @@ or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler conf
24602
24607
  headers: request.headers,
24603
24608
  host: hostname,
24604
24609
  method: request.method,
24605
- path: path20,
24610
+ path: path21,
24606
24611
  port: request.port,
24607
24612
  agent,
24608
24613
  auth
@@ -25511,10 +25516,10 @@ ${longDate}
25511
25516
  ${credentialScope}
25512
25517
  ${toHex(hashedRequest)}`;
25513
25518
  }
25514
- getCanonicalPath({ path: path20 }) {
25519
+ getCanonicalPath({ path: path21 }) {
25515
25520
  if (this.uriEscapePath) {
25516
25521
  const normalizedPathSegments = [];
25517
- for (const pathSegment of path20.split("/")) {
25522
+ for (const pathSegment of path21.split("/")) {
25518
25523
  if (pathSegment?.length === 0)
25519
25524
  continue;
25520
25525
  if (pathSegment === ".")
@@ -25525,11 +25530,11 @@ ${toHex(hashedRequest)}`;
25525
25530
  normalizedPathSegments.push(pathSegment);
25526
25531
  }
25527
25532
  }
25528
- const normalizedPath = `${path20?.startsWith("/") ? "/" : ""}${normalizedPathSegments.join("/")}${normalizedPathSegments.length > 0 && path20?.endsWith("/") ? "/" : ""}`;
25533
+ const normalizedPath = `${path21?.startsWith("/") ? "/" : ""}${normalizedPathSegments.join("/")}${normalizedPathSegments.length > 0 && path21?.endsWith("/") ? "/" : ""}`;
25529
25534
  const doubleEncoded = escapeUri(normalizedPath);
25530
25535
  return doubleEncoded.replace(/%2F/g, "/");
25531
25536
  }
25532
- return path20;
25537
+ return path21;
25533
25538
  }
25534
25539
  validateResolvedCredentials(credentials) {
25535
25540
  if (typeof credentials !== "object" || typeof credentials.accessKeyId !== "string" || typeof credentials.secretAccessKey !== "string") {
@@ -42802,8 +42807,8 @@ var init_HttpClient = __esm({
42802
42807
  throw new Error(err2.message ?? "An error occurred on GET.");
42803
42808
  }
42804
42809
  };
42805
- this.getWorkerUrl = (path20) => {
42806
- const cleanPath = path20.startsWith("/") ? path20.substring(1) : path20;
42810
+ this.getWorkerUrl = (path21) => {
42811
+ const cleanPath = path21.startsWith("/") ? path21.substring(1) : path21;
42807
42812
  return `${this.workerHost}/${cleanPath}`;
42808
42813
  };
42809
42814
  this.workerHost = ProcessENVManager_default.getEnvVariable("REMORA_WORKER_HOST") || "http://worker:5069";
@@ -42949,32 +42954,32 @@ var init_DriverHelper = __esm({
42949
42954
  },
42950
42955
  quickReadFile: async (filePath, lineCount) => {
42951
42956
  const fileStream = (0, import_fs9.createReadStream)(filePath);
42952
- const rl7 = (0, import_readline.createInterface)({ input: fileStream, crlfDelay: Infinity });
42957
+ const rl8 = (0, import_readline.createInterface)({ input: fileStream, crlfDelay: Infinity });
42953
42958
  const lines = [];
42954
42959
  let counter = 0;
42955
- for await (const line of rl7) {
42960
+ for await (const line of rl8) {
42956
42961
  lines.push(line);
42957
42962
  counter++;
42958
42963
  if (counter >= lineCount) {
42959
42964
  break;
42960
42965
  }
42961
42966
  }
42962
- rl7.close();
42967
+ rl8.close();
42963
42968
  fileStream.close();
42964
42969
  return lines;
42965
42970
  },
42966
42971
  quickReadStream: async (stream, lineCount) => {
42967
- const rl7 = (0, import_readline.createInterface)({ input: stream, crlfDelay: Infinity });
42972
+ const rl8 = (0, import_readline.createInterface)({ input: stream, crlfDelay: Infinity });
42968
42973
  const lines = [];
42969
42974
  let counter = 0;
42970
- for await (const line of rl7) {
42975
+ for await (const line of rl8) {
42971
42976
  lines.push(line);
42972
42977
  counter++;
42973
42978
  if (counter >= lineCount) {
42974
42979
  break;
42975
42980
  }
42976
42981
  }
42977
- rl7.close();
42982
+ rl8.close();
42978
42983
  return lines;
42979
42984
  }
42980
42985
  };
@@ -43748,7 +43753,7 @@ var init_S3DestinationDriver = __esm({
43748
43753
  });
43749
43754
 
43750
43755
  // ../../packages/drivers/src/DriverFactory.ts
43751
- var DriverFactoryClass, DriverFactory, DriverFactory_default;
43756
+ var STAGEABLE_ENGINES, DriverFactoryClass, DriverFactory, DriverFactory_default;
43752
43757
  var init_DriverFactory = __esm({
43753
43758
  "../../packages/drivers/src/DriverFactory.ts"() {
43754
43759
  init_RedshiftDriver();
@@ -43758,8 +43763,11 @@ var init_DriverFactory = __esm({
43758
43763
  init_LocalDestinationDriver();
43759
43764
  init_S3SourceDriver();
43760
43765
  init_S3DestinationDriver();
43766
+ STAGEABLE_ENGINES = ["local", "aws-s3", "delta-share"];
43761
43767
  DriverFactoryClass = class {
43762
43768
  constructor() {
43769
+ /** Whether a run can stage a producer from this engine — see `STAGEABLE_ENGINES`. */
43770
+ this.canStage = (engine) => STAGEABLE_ENGINES.includes(engine);
43763
43771
  this.instantiateSource = async (source) => {
43764
43772
  switch (source.engine) {
43765
43773
  case "aws-redshift": {
@@ -43839,6 +43847,7 @@ var init_ProducerManager = __esm({
43839
43847
  init_src4();
43840
43848
  init_src3();
43841
43849
  init_src5();
43850
+ init_CryptoEngine();
43842
43851
  ProducerManagerClass = class {
43843
43852
  constructor() {
43844
43853
  this.getColumns = (producer) => {
@@ -43893,6 +43902,46 @@ var init_ProducerManager = __esm({
43893
43902
  return true;
43894
43903
  return FixedWidthParser_default.matchesSelector(line, resolved.selector, resolved.range);
43895
43904
  };
43905
+ /**
43906
+ * The post-parse work a producer implies for every record it produces: which keys carry the
43907
+ * source file name, and which carry a mask.
43908
+ *
43909
+ * Resolved once per chunk, never per line — `getMask` resolves a secret and validates the value,
43910
+ * and neither depends on the record. Empty for almost every producer, which is the case worth
43911
+ * keeping fast.
43912
+ */
43913
+ this.compileFills = (dimensions) => {
43914
+ Affirm_default(dimensions, "Invalid producer dimensions");
43915
+ const fills = [];
43916
+ for (const dimension of dimensions) {
43917
+ const { prodDimension } = dimension;
43918
+ const mask = this.getMask(prodDimension);
43919
+ const sourceFilename = prodDimension.sourceFilename === true;
43920
+ if (!sourceFilename && !Algo_default.hasVal(mask))
43921
+ continue;
43922
+ fills.push({ key: dimension.name, mask, sourceFilename, type: prodDimension.type });
43923
+ }
43924
+ return fills;
43925
+ };
43926
+ /**
43927
+ * Applies the compiled fills to one parsed record, in place.
43928
+ *
43929
+ * The one place a producer dimension's mask is ever applied, on purpose: a run reaches it through
43930
+ * `ProducerExecutor.processLine` and a sample through `ProducerEngine`, and a masked dimension
43931
+ * that came back in the clear from one of the two would be a `hash` the project declared and the
43932
+ * screen ignored — a disclosure, not a display bug.
43933
+ */
43934
+ this.applyFills = (record, fills, sourceFilename) => {
43935
+ if (!fills?.length)
43936
+ return record;
43937
+ for (const fill2 of fills) {
43938
+ if (fill2.sourceFilename)
43939
+ record[fill2.key] = sourceFilename;
43940
+ if (Algo_default.hasVal(fill2.mask))
43941
+ record[fill2.key] = CryptoEngine_default.hashValue(fill2.mask, record[fill2.key]?.toString(), fill2.type);
43942
+ }
43943
+ return record;
43944
+ };
43896
43945
  }
43897
43946
  };
43898
43947
  ProducerManager = new ProducerManagerClass();
@@ -44307,7 +44356,8 @@ var init_ProducerEngine = __esm({
44307
44356
  }
44308
44357
  };
44309
44358
  }
44310
- const fileData = await this.readFile(effectiveProducer, { readmode: "lines", lines: { from: 0, to: sampleSize } }, effectiveSource);
44359
+ const headerLines = ParseHelper_default.hasHeaderLine(effectiveProducer.settings.fileType, effectiveProducer.settings.hasHeaderRow) ? 1 : 0;
44360
+ const fileData = await this.readFile(effectiveProducer, { readmode: "lines", lines: { from: 0, to: sampleSize + headerLines } }, effectiveSource);
44311
44361
  rawData = fileData.data;
44312
44362
  } finally {
44313
44363
  if (scope)
@@ -44326,13 +44376,15 @@ var init_ProducerEngine = __esm({
44326
44376
  if (!rawData || rawData.length === 0) return [];
44327
44377
  const firstItem = rawData[0];
44328
44378
  const isLineData = typeof firstItem === "string";
44379
+ const fills = discover ? [] : ProducerManager_default.compileFills(this._fillDimensions(producer));
44380
+ const sourceFilename = import_path13.default.basename(producer.settings.fileKey ?? producer.settings.sqlTable ?? "");
44329
44381
  if (isLineData) {
44330
44382
  const lines = rawData;
44331
44383
  const dimensions = this.deriveDimensions(lines[0], producer, discover);
44332
44384
  const { fileType, hasHeaderRow } = producer.settings;
44333
44385
  const dataLines = ParseHelper_default.hasHeaderLine(fileType, hasHeaderRow) ? lines.slice(1) : lines;
44334
44386
  const recordSelector = ProducerManager_default.resolveRecordSelector(producer);
44335
- return dataLines.filter((line) => line.trim().length > 0).filter((line) => ProducerManager_default.shouldReadLine(line, recordSelector)).flatMap((line) => LineParser_default.parseAll(line, producer, dimensions, noopTracker));
44387
+ return dataLines.filter((line) => line.trim().length > 0).filter((line) => ProducerManager_default.shouldReadLine(line, recordSelector)).flatMap((line) => LineParser_default.parseAll(line, producer, dimensions, noopTracker)).map((record) => ProducerManager_default.applyFills(record, fills, sourceFilename));
44336
44388
  }
44337
44389
  if (discover) {
44338
44390
  return rawData.map((item) => ({ ...item }));
@@ -44343,9 +44395,15 @@ var init_ProducerEngine = __esm({
44343
44395
  const key = dim.alias ?? dim.name;
44344
44396
  record[dim.name] = TypeCaster_default.cast(item[key], dim.type, dim.format);
44345
44397
  }
44346
- return record;
44398
+ return ProducerManager_default.applyFills(record, fills, sourceFilename);
44347
44399
  });
44348
44400
  };
44401
+ /**
44402
+ * The producer's declared dimensions in the shape `compileFills` reads them in. Declaration
44403
+ * order, because a fill is addressed by key and never by position — unlike the executor
44404
+ * dimensions of a run, whose index is reconciled against the file's own header.
44405
+ */
44406
+ this._fillDimensions = (producer) => producer.dimensions.map((prodDimension, index) => ({ index, name: prodDimension.name, prodDimension }));
44349
44407
  /**
44350
44408
  * Builds the executor dimensions of a producer. There are two shapes of source: a FIXED file has
44351
44409
  * no header, so its layout comes from the positions declared on the dimensions; every other file
@@ -46813,9 +46871,10 @@ var init_MockDataFactory = __esm({
46813
46871
  if (type === "number") {
46814
46872
  const first = bounds?.min ?? 1;
46815
46873
  const last = first + poolSize - 1;
46874
+ const ceiling = bounds?.max;
46816
46875
  Affirm_default(
46817
- bounds?.max === void 0 || bounds.max >= last,
46818
- `Key "${norm}" has a synth range of ${first}-${bounds.max}, which holds fewer than the ${poolSize} distinct values it has to generate; widen it or generate fewer records`
46876
+ ceiling === void 0 || ceiling >= last,
46877
+ `Key "${norm}" has a synth range of ${first}-${ceiling}, which holds fewer than the ${poolSize} distinct values it has to generate; widen it or generate fewer records`
46819
46878
  );
46820
46879
  for (let i6 = 0; i6 < poolSize; i6++) pool.push(first + i6);
46821
46880
  return pool;
@@ -48155,23 +48214,23 @@ var init_Diagnostics = __esm({
48155
48214
  return [.../* @__PURE__ */ new Set([...inputs, ...chained])];
48156
48215
  };
48157
48216
  this._walk = (start, next, onCycle) => {
48158
- const path20 = [];
48217
+ const path21 = [];
48159
48218
  const onPath = /* @__PURE__ */ new Set();
48160
48219
  const stack = [{ name: start, done: false }];
48161
48220
  const visited = /* @__PURE__ */ new Set();
48162
48221
  while (stack.length > 0) {
48163
48222
  const frame = stack.pop();
48164
48223
  if (frame.done) {
48165
- onPath.delete(path20.pop());
48224
+ onPath.delete(path21.pop());
48166
48225
  continue;
48167
48226
  }
48168
48227
  if (onPath.has(frame.name)) {
48169
- path20.slice(path20.indexOf(frame.name)).forEach((name) => onCycle.add(name));
48228
+ path21.slice(path21.indexOf(frame.name)).forEach((name) => onCycle.add(name));
48170
48229
  continue;
48171
48230
  }
48172
48231
  if (visited.has(frame.name)) continue;
48173
48232
  visited.add(frame.name);
48174
- path20.push(frame.name);
48233
+ path21.push(frame.name);
48175
48234
  onPath.add(frame.name);
48176
48235
  stack.push({ name: frame.name, done: true });
48177
48236
  const successors = next.get(frame.name) ?? [];
@@ -48848,8 +48907,8 @@ var init_ResourceView = __esm({
48848
48907
  if (!source || origin.path.length < 2) return { value: "declared here", muted: true };
48849
48908
  return { value: `${source.resource}.${source.field}`, link: source.node };
48850
48909
  };
48851
- this._pathText = (path20) => {
48852
- return path20.map((step) => `${step.resource}.${step.field}`).join(" \u2192 ");
48910
+ this._pathText = (path21) => {
48911
+ return path21.map((step) => `${step.resource}.${step.field}`).join(" \u2192 ");
48853
48912
  };
48854
48913
  this._consumerOutputsTable = (env2, consumer) => {
48855
48914
  const declared = new Set((env2.sources ?? []).map((x2) => x2.name));
@@ -49879,17 +49938,30 @@ var init_LocalUsageDB = __esm({
49879
49938
  }
49880
49939
  };
49881
49940
  /**
49882
- * The recorded runs, newest first. Only the tail of the file is decoded: a reader wants the recent
49941
+ * The recorded runs, newest first — the newest `limit` of the rows `keep` accepts.
49942
+ *
49943
+ * Decoded from the end and stopped as soon as that many are held: a reader wants the recent
49883
49944
  * executions, and decoding ten thousand rows to show ten of them is work nobody asked for.
49884
49945
  *
49946
+ * `keep` is applied BEFORE the limit, which is the only way "the newest N runs of this kind" can
49947
+ * mean it: filtering afterwards lets the rows a caller does not want displace the ones it does,
49948
+ * so one sample recorded here would silently push a real run out of a history someone is reading.
49949
+ *
49885
49950
  * Never throws. A missing file reads as no history, and so does a half-written one — this store is
49886
49951
  * appended to while it is being read, so failing on that would make it unreadable during a run.
49887
49952
  */
49888
- this.list = (limit = MAX_LISTED_ROWS) => {
49953
+ this.list = (limit = MAX_LISTED_ROWS, keep) => {
49889
49954
  try {
49890
49955
  if (!(0, import_fs12.existsSync)(CLI_DB_PATH))
49891
49956
  return [];
49892
- return this._getRows().slice(-Math.max(1, limit)).map((line) => this._fromLine(line)).reverse();
49957
+ const rows = this._getRows();
49958
+ const wanted = Math.max(1, limit);
49959
+ const found = [];
49960
+ for (let index = rows.length - 1; index >= 0 && found.length < wanted; index--) {
49961
+ const usage = this._fromLine(rows[index]);
49962
+ if (!keep || keep(usage)) found.push(usage);
49963
+ }
49964
+ return found;
49893
49965
  } catch (error) {
49894
49966
  this._handleError("list", error);
49895
49967
  return [];
@@ -50070,9 +50142,10 @@ var init_UsageManager = __esm({
50070
50142
  });
50071
50143
 
50072
50144
  // ../../packages/engines/src/ops/RunOverlay.ts
50073
- var MAX_RUNS, MAX_OPERATIONS, RunOverlayClass, RunOverlay;
50145
+ var import_crypto4, MAX_RUNS, MAX_OPERATIONS, RunOverlayClass, RunOverlay;
50074
50146
  var init_RunOverlay = __esm({
50075
50147
  "../../packages/engines/src/ops/RunOverlay.ts"() {
50148
+ import_crypto4 = __toESM(require("crypto"), 1);
50076
50149
  init_src();
50077
50150
  init_LineageEngine();
50078
50151
  init_LocalUsageDB();
@@ -50081,6 +50154,8 @@ var init_RunOverlay = __esm({
50081
50154
  MAX_OPERATIONS = 40;
50082
50155
  RunOverlayClass = class {
50083
50156
  constructor() {
50157
+ /** The last store token read, and the overlay token it resolved to. */
50158
+ this._signed = { store: null, overlay: "" };
50084
50159
  /**
50085
50160
  * The recent executions and their aggregates. ONE read of the store answers both, so a page
50086
50161
  * showing a graph, a run list and a timing breakdown costs one file read rather than three.
@@ -50091,20 +50166,40 @@ var init_RunOverlay = __esm({
50091
50166
  this.read = (limit = MAX_RUNS) => {
50092
50167
  Affirm_default(limit > 0, "Run overlay limit must be greater than zero");
50093
50168
  if (!UsageManager_default.recordsLocally()) return { runs: [], metrics: {} };
50094
- const stats = LocalUsageDB_default.list(limit).filter((stat) => stat?.consumer);
50169
+ const stats = LocalUsageDB_default.list(limit, (stat) => !!stat?.consumer && stat.invokedBy !== "SAMPLE");
50095
50170
  return { runs: stats.map((stat) => this._run(stat)), metrics: this._metrics(stats) };
50096
50171
  };
50097
50172
  /**
50098
- * A token that changes when the run store does. Empty when there is nothing to read.
50173
+ * A token that changes when the OVERLAY does. Empty when there is nothing to read.
50099
50174
  *
50100
50175
  * A run finishing changes no configuration file, so the config hash cannot notice it — this is
50101
- * the second half of what the preview polls, and it is cheap for the same reason the hash is
50102
- * affordable: it reads metadata, not records.
50176
+ * the second half of what the preview polls.
50177
+ *
50178
+ * It signs the painted runs rather than the store, because the two do not move together: a
50179
+ * sample is recorded like any other execution and then filtered out of `read`, so the store's
50180
+ * own token changes while the picture does not. Signing the store there announced a project
50181
+ * change for every sample — and a page told its resource changed drops the sample it is showing,
50182
+ * which is exactly the rows someone had just asked for.
50183
+ *
50184
+ * Still cheap to poll: the store's mtime/size gates the read, so the rows are decoded only after
50185
+ * something has actually been written, never once a second.
50103
50186
  */
50104
50187
  this.signature = () => {
50105
50188
  if (!UsageManager_default.recordsLocally()) return "";
50106
- return LocalUsageDB_default.signature();
50189
+ const store = LocalUsageDB_default.signature();
50190
+ if (store === this._signed.store) return this._signed.overlay;
50191
+ const overlay = this._token(this.read());
50192
+ this._signed = { store, overlay };
50193
+ return overlay;
50107
50194
  };
50195
+ /**
50196
+ * A token over the whole overlay, runs and metrics alike.
50197
+ *
50198
+ * Everything it carries, not the newest row and a count: `endUsage` updates a row in place and a
50199
+ * finish action writes runs of its own, so a narrower token would miss a transition the graph
50200
+ * exists to show.
50201
+ */
50202
+ this._token = (overlay) => import_crypto4.default.createHash("sha256").update(JSON.stringify(overlay)).digest("hex");
50108
50203
  this._run = (stat) => {
50109
50204
  const startedAt = this._iso(stat.startedAt);
50110
50205
  return {
@@ -50350,63 +50445,6 @@ var init_PushdownEngine = __esm({
50350
50445
  }
50351
50446
  });
50352
50447
 
50353
- // ../../packages/engines/src/producer/SampleEngine.ts
50354
- var MAX_CHAIN_DEPTH2, SampleEngineClass, SampleEngine;
50355
- var init_SampleEngine = __esm({
50356
- "../../packages/engines/src/producer/SampleEngine.ts"() {
50357
- init_src();
50358
- init_src4();
50359
- init_ProducerEngine();
50360
- MAX_CHAIN_DEPTH2 = 20;
50361
- SampleEngineClass = class {
50362
- constructor() {
50363
- /**
50364
- * @param resourceName a producer or consumer name, as declared.
50365
- * @throws when the name is neither, or when the source cannot be read.
50366
- */
50367
- this.read = async (resourceName, size = 10) => {
50368
- Affirm_default(resourceName, "Missing resource name to sample");
50369
- Affirm_default(size > 0, "Sample size must be greater than 0");
50370
- const producer = Environment_default.getProducer(resourceName);
50371
- if (producer)
50372
- return { kind: "producer", name: resourceName, records: await ProducerEngine_default.readSampleData(producer, size, false) };
50373
- const consumer = Environment_default.getConsumer(resourceName);
50374
- Affirm_default(consumer, `Resource "${resourceName}" not found. Please check if it exists as a producer or consumer.`);
50375
- return { kind: "consumer", name: resourceName, records: await this._readConsumer(consumer, size, []) };
50376
- };
50377
- /**
50378
- * A consumer has no data of its own until it runs, so what it shows is its FIRST input, projected
50379
- * through its field list — near enough to answer "what will come out of this" without a run.
50380
- */
50381
- this._readConsumer = async (consumer, size, visited) => {
50382
- Affirm_default(!visited.includes(consumer.name), `Consumer "${consumer.name}" reads itself through ${visited.join(" \u2192 ")}`);
50383
- Affirm_default(visited.length < MAX_CHAIN_DEPTH2, `Consumer chain from "${visited[0]}" is more than ${MAX_CHAIN_DEPTH2} deep`);
50384
- const input = consumer.producers[0];
50385
- Affirm_default(input, `Consumer "${consumer.name}" has no producers configured`);
50386
- const producer = Environment_default.getProducer(input.name);
50387
- if (!producer) {
50388
- const upstream = Environment_default.getConsumer(input.name);
50389
- Affirm_default(upstream, `Producer or consumer "${input.name}" not found for consumer "${consumer.name}"`);
50390
- return this._readConsumer(upstream, size, [...visited, consumer.name]);
50391
- }
50392
- const records = await ProducerEngine_default.readSampleData(producer, size, false);
50393
- if (consumer.fields.some((field) => field.key === "*")) return records;
50394
- return records.map((record) => this._project(consumer, record));
50395
- };
50396
- this._project = (consumer, record) => {
50397
- const projected = {};
50398
- for (const field of consumer.fields) {
50399
- if (field.key === "*") continue;
50400
- projected[field.alias || field.key] = record[field.key];
50401
- }
50402
- return projected;
50403
- };
50404
- }
50405
- };
50406
- SampleEngine = new SampleEngineClass();
50407
- }
50408
- });
50409
-
50410
50448
  // ../../packages/engines/src/transform/LookupRegistry.ts
50411
50449
  var LookupRegistryClass, LookupRegistry, LookupRegistry_default;
50412
50450
  var init_LookupRegistry = __esm({
@@ -50443,12 +50481,12 @@ var init_LookupRegistry = __esm({
50443
50481
  });
50444
50482
 
50445
50483
  // ../../packages/engines/src/transform/LookupKey.ts
50446
- var import_crypto4, DELIMITER, idFor, LookupKey, LookupKey_default;
50484
+ var import_crypto5, DELIMITER, idFor, LookupKey, LookupKey_default;
50447
50485
  var init_LookupKey = __esm({
50448
50486
  "../../packages/engines/src/transform/LookupKey.ts"() {
50449
- import_crypto4 = __toESM(require("crypto"), 1);
50487
+ import_crypto5 = __toESM(require("crypto"), 1);
50450
50488
  DELIMITER = String.fromCharCode(0);
50451
- idFor = (ref) => import_crypto4.default.createHash("sha1").update([ref.consumer, ref.key, ref.value].join(DELIMITER)).digest("hex").slice(0, 16);
50489
+ idFor = (ref) => import_crypto5.default.createHash("sha1").update([ref.consumer, ref.key, ref.value].join(DELIMITER)).digest("hex").slice(0, 16);
50452
50490
  LookupKey = { idFor };
50453
50491
  LookupKey_default = LookupKey;
50454
50492
  }
@@ -51085,7 +51123,6 @@ var init_src10 = __esm({
51085
51123
  init_ProducerEngine();
51086
51124
  init_ProducerManager();
51087
51125
  init_PushdownEngine();
51088
- init_SampleEngine();
51089
51126
  init_TransformationEngine();
51090
51127
  init_TypeCaster();
51091
51128
  init_LookupRegistry();
@@ -51255,14 +51292,14 @@ var init_OutputExecutor = __esm({
51255
51292
  });
51256
51293
 
51257
51294
  // ../../packages/executors/src/ConsumerExecutor.ts
51258
- var import_path18, import_fs13, import_readline6, import_promises13, import_crypto5, LIMIT_SCAN_BYTES, NEWLINE_BYTE, ConsumerExecutorClass, ConsumerExecutor, ConsumerExecutor_default;
51295
+ var import_path18, import_fs13, import_readline6, import_promises13, import_crypto6, LIMIT_SCAN_BYTES, NEWLINE_BYTE, ConsumerExecutorClass, ConsumerExecutor, ConsumerExecutor_default;
51259
51296
  var init_ConsumerExecutor = __esm({
51260
51297
  "../../packages/executors/src/ConsumerExecutor.ts"() {
51261
51298
  import_path18 = __toESM(require("path"));
51262
51299
  import_fs13 = __toESM(require("fs"));
51263
51300
  import_readline6 = __toESM(require("readline"));
51264
51301
  import_promises13 = __toESM(require("fs/promises"));
51265
- import_crypto5 = __toESM(require("crypto"));
51302
+ import_crypto6 = __toESM(require("crypto"));
51266
51303
  init_src10();
51267
51304
  init_src10();
51268
51305
  init_src10();
@@ -51501,7 +51538,7 @@ var init_ConsumerExecutor = __esm({
51501
51538
  let newLineCount = 0;
51502
51539
  const seen = /* @__PURE__ */ new Set();
51503
51540
  for await (const line of lineReader) {
51504
- const hash = import_crypto5.default.createHash("sha1").update(line).digest("base64");
51541
+ const hash = import_crypto6.default.createHash("sha1").update(line).digest("base64");
51505
51542
  if (!seen.has(hash)) {
51506
51543
  seen.add(hash);
51507
51544
  if (!writer.write(line + "\n"))
@@ -51777,8 +51814,6 @@ var init_ProducerExecutor = __esm({
51777
51814
  init_src8();
51778
51815
  init_src4();
51779
51816
  init_src10();
51780
- init_src();
51781
- init_src10();
51782
51817
  init_src10();
51783
51818
  import_path19 = __toESM(require("path"));
51784
51819
  init_src2();
@@ -51809,20 +51844,14 @@ var init_ProducerExecutor = __esm({
51809
51844
  * the source file name or a mask, and what that file name is. None of it depends on the line, so
51810
51845
  * it is compiled once per chunk instead of rebuilt per line — including the mask's own
51811
51846
  * validation, which used to run once per dimension per line.
51847
+ *
51848
+ * The fills themselves come from `ProducerManager`, which is also where the sample path gets
51849
+ * them: one producer stage, reached from two places.
51812
51850
  */
51813
51851
  this.compileLine = (dimensions, chunk) => {
51814
51852
  Affirm_default(dimensions, "Invalid producer dimensions");
51815
51853
  Affirm_default(chunk, "Invalid chunk");
51816
- const fills = [];
51817
- for (const dimension of dimensions) {
51818
- const { prodDimension } = dimension;
51819
- const mask = ProducerManager_default.getMask(prodDimension);
51820
- const sourceFilename = prodDimension.sourceFilename === true;
51821
- if (!sourceFilename && !Algo_default.hasVal(mask))
51822
- continue;
51823
- fills.push({ key: dimension.name, mask, sourceFilename, type: prodDimension.type });
51824
- }
51825
- return { fills, sourceFilename: import_path19.default.basename(chunk.fileUri) };
51854
+ return { fills: ProducerManager_default.compileFills(dimensions), sourceFilename: import_path19.default.basename(chunk.fileUri) };
51826
51855
  };
51827
51856
  /**
51828
51857
  * The record(s) one source line produces. Almost always exactly one — a producer with a group
@@ -51843,14 +51872,8 @@ var init_ProducerExecutor = __esm({
51843
51872
  if (plan.fills.length === 0)
51844
51873
  return records;
51845
51874
  counter = performance.now();
51846
- for (const record of records) {
51847
- for (const fill2 of plan.fills) {
51848
- if (fill2.sourceFilename)
51849
- record[fill2.key] = plan.sourceFilename;
51850
- if (Algo_default.hasVal(fill2.mask))
51851
- record[fill2.key] = CryptoEngine_default.hashValue(fill2.mask, record[fill2.key]?.toString(), fill2.type);
51852
- }
51853
- }
51875
+ for (const record of records)
51876
+ ProducerManager_default.applyFills(record, plan.fills, plan.sourceFilename);
51854
51877
  tracker.measure("process-line:dimensions-filename-mask", performance.now() - counter);
51855
51878
  return records;
51856
51879
  };
@@ -52618,7 +52641,8 @@ var init_ExecutorOrchestrator = __esm({
52618
52641
  } catch (error) {
52619
52642
  Logger_default.log(`[${usageId}] Consumer "${consumer.name}" failed: ${Helper_default.asError(error).message} | ${OrchestratorHelper_default.formatMemoryUsage()}`);
52620
52643
  Logger_default.error(error);
52621
- await ConsumerOnFinishManager_default.onConsumerError(consumer, usageId, this.makeOnFinishRunner(consumer, request, usageId));
52644
+ if (!request.skipExport)
52645
+ await ConsumerOnFinishManager_default.onConsumerError(consumer, usageId, this.makeOnFinishRunner(consumer, request, usageId));
52622
52646
  Logger_default.log(`[${usageId}] Running cleanup after failure`);
52623
52647
  await this.performCleanupOperations(scope, tracker);
52624
52648
  UsageManager_default.failUsage(usageId, Helper_default.asError(error).message);
@@ -53571,6 +53595,163 @@ init_src4();
53571
53595
  init_OutputExecutor();
53572
53596
  init_ProducerExecutor();
53573
53597
 
53598
+ // ../../packages/executors/src/SampleRunner.ts
53599
+ var import_fs18 = __toESM(require("fs"));
53600
+ var import_promises18 = __toESM(require("fs/promises"));
53601
+ var import_path24 = __toESM(require("path"));
53602
+ var import_readline11 = __toESM(require("readline"));
53603
+ init_src();
53604
+ init_src3();
53605
+ init_src4();
53606
+ init_src8();
53607
+ init_src10();
53608
+ init_src7();
53609
+ init_src2();
53610
+ init_ExecutorOrchestrator();
53611
+ var MAX_WALK_DEPTH = 20;
53612
+ var SAMPLE_USER = { user: { _id: "sample", name: "sample", type: "licence" } };
53613
+ var SampleRunnerClass = class {
53614
+ constructor() {
53615
+ /**
53616
+ * @param resourceName a producer or consumer name, as declared.
53617
+ * @param rows the most rows to return, capped at `MAX_SAMPLE_ROWS`.
53618
+ * @throws when the name is neither, when a source cannot be read, or when the pipeline itself
53619
+ * fails — a consumer whose transformation throws is a sample that reports the reason.
53620
+ */
53621
+ this.read = async (resourceName, rows = 10, details) => {
53622
+ Affirm_default(resourceName, "Missing resource name to sample");
53623
+ Affirm_default(rows > 0, "Sample size must be greater than 0");
53624
+ const size = Math.min(rows, Constants_default.defaults.MAX_SAMPLE_ROWS);
53625
+ const producer = Environment_default.getProducer(resourceName);
53626
+ if (producer) {
53627
+ const records = await ProducerEngine_default.readSampleData(producer, size, false);
53628
+ return {
53629
+ kind: "producer",
53630
+ name: resourceName,
53631
+ records,
53632
+ note: `${records.length} row(s) read from the source now, with this producer's dimensions, types and masks applied.`
53633
+ };
53634
+ }
53635
+ const consumer = Environment_default.getConsumer(resourceName);
53636
+ Affirm_default(consumer, `Resource "${resourceName}" not found. Please check if it exists as a producer or consumer.`);
53637
+ return this._runConsumer(consumer, size, details);
53638
+ };
53639
+ /**
53640
+ * Runs the consumer for real and reads back what it produced.
53641
+ *
53642
+ * The output is captured instead of exported, so nothing reaches the consumer's destination and
53643
+ * no onSuccess action fires: looking at data must never be indistinguishable from publishing it.
53644
+ */
53645
+ this._runConsumer = async (consumer, size, details) => {
53646
+ this._assertStageable(consumer);
53647
+ const capturePath = import_path24.default.resolve(import_path24.default.join(ExecutorScope_default.getBasePath(), `sample_${consumer.name}_${Helper_default.uuid()}.jsonl`));
53648
+ ExecutorScope_default.ensurePath(capturePath);
53649
+ try {
53650
+ const result = await ExecutorOrchestrator_default.launch({
53651
+ consumer,
53652
+ // `SAMPLE`, so the run it records is distinguishable from one the project did. The
53653
+ // executor treats it like any other invocation; only `RunOverlay` reads the marker.
53654
+ details: { ...details ?? SAMPLE_USER, invokedBy: "SAMPLE" },
53655
+ // The rows asked for, bounded the way `remora run -l` bounds them — pushed to the
53656
+ // source and stopped early per worker wherever nothing downstream can drop a record.
53657
+ options: { limit: size },
53658
+ // Nothing reaches the destination and no finish action fires; the merged dataset
53659
+ // comes back through the same capture the lookup and consumer-input runs use.
53660
+ skipExport: true,
53661
+ captureMainDatasetTo: capturePath,
53662
+ captureFormat: "JSONL"
53663
+ });
53664
+ const captured = await this._readCapture(capturePath, size);
53665
+ const records = captured.map((record) => this._inFieldOrder(consumer, record));
53666
+ Logger_default.log(`[sample] Consumer "${consumer.name}": ${records.length} row(s) from ${result.inputCount} input line(s)`);
53667
+ return {
53668
+ kind: "consumer",
53669
+ name: consumer.name,
53670
+ records,
53671
+ note: `${records.length} row(s) from the real pipeline \u2014 filters, transformations, masking and validation all applied, exactly as in a run. Nothing was written to the destination.`
53672
+ };
53673
+ } finally {
53674
+ await import_promises18.default.rm(capturePath, { force: true });
53675
+ }
53676
+ };
53677
+ /**
53678
+ * Refuses, naming the source, when a run could not read one of this consumer's producers.
53679
+ *
53680
+ * `RedshiftDriver.ready` and `HttpApiSourceDriver.ready` are unimplemented, so the pipeline
53681
+ * cannot reach those sources at all. Checked up front because the alternative is a run that
53682
+ * spins up a worker pool and then surfaces "Not implemented yet" from three layers down, which
53683
+ * names neither the producer nor the source that caused it.
53684
+ */
53685
+ this._assertStageable = (consumer) => {
53686
+ for (const producer of this._inputProducers(consumer)) {
53687
+ const source = Environment_default.getSource(producer.source);
53688
+ Affirm_default(source, `Producer "${producer.name}" of consumer "${consumer.name}" has no source "${producer.source}"`);
53689
+ if (!DriverFactory_default.canStage(source.engine))
53690
+ throw new Error(`Cannot sample consumer "${consumer.name}": its producer "${producer.name}" reads source "${source.name}", and a "${source.engine}" source cannot be run through the pipeline yet. Sample the producer "${producer.name}" instead to see its rows.`);
53691
+ }
53692
+ };
53693
+ /**
53694
+ * Every producer a run of this consumer would stage — its own, plus those of the consumers it
53695
+ * reads as input or consults as a code_lookup table, since each of those is executed too.
53696
+ */
53697
+ this._inputProducers = (consumer, visited = []) => {
53698
+ Affirm_default(!visited.includes(consumer.name), `Consumer "${consumer.name}" reads itself through ${visited.join(" \u2192 ")}`);
53699
+ Affirm_default(visited.length < MAX_WALK_DEPTH, `Consumer chain from "${visited[0]}" is more than ${MAX_WALK_DEPTH} deep`);
53700
+ const seen = [...visited, consumer.name];
53701
+ const upstream = [
53702
+ ...consumer.producers.map((entry) => entry.name),
53703
+ ...ConsumerManager_default.collectLookupRefs(ConsumerManager_default.getExpandedFields(consumer)).map((ref) => ref.consumer)
53704
+ ];
53705
+ return upstream.flatMap((name) => {
53706
+ const producer = Environment_default.getProducer(name);
53707
+ if (producer) return [producer];
53708
+ const nested = Environment_default.getConsumer(name);
53709
+ Affirm_default(nested, `Producer or consumer "${name}" not found for consumer "${consumer.name}"`);
53710
+ return this._inputProducers(nested, seen);
53711
+ });
53712
+ };
53713
+ /**
53714
+ * One row's keys in the order the consumer DECLARES its fields.
53715
+ *
53716
+ * A record comes out of a worker keyed in the order the producer's dimensions were read, which is
53717
+ * the source's layout and not the consumer's — so a consumer declaring country, plan, is_active
53718
+ * over a producer that stores is_active first would be shown its own columns in someone else's
53719
+ * order. Invisible in an exported JSON object; the whole shape of the table here.
53720
+ *
53721
+ * Anything the record carries that the field list does not name is appended rather than dropped:
53722
+ * a column that turns up unexpectedly is exactly the one worth seeing.
53723
+ */
53724
+ this._inFieldOrder = (consumer, record) => {
53725
+ const declared = ConsumerManager_default.getExpandedFields(consumer).filter((field) => !field.cField.hidden).map((field) => field.finalKey);
53726
+ const ordered = {};
53727
+ for (const key of declared) {
53728
+ if (key in record) ordered[key] = record[key];
53729
+ }
53730
+ for (const key of Object.keys(record)) {
53731
+ if (!(key in ordered)) ordered[key] = record[key];
53732
+ }
53733
+ return ordered;
53734
+ };
53735
+ /** The captured dataset, which `captureFormat: 'JSONL'` guarantees is one JSON object per line. */
53736
+ this._readCapture = async (capturePath, size) => {
53737
+ if (!import_fs18.default.existsSync(capturePath)) return [];
53738
+ const records = [];
53739
+ const lines = import_readline11.default.createInterface({ input: import_fs18.default.createReadStream(capturePath), crlfDelay: Infinity });
53740
+ try {
53741
+ for await (const line of lines) {
53742
+ if (!line.length) continue;
53743
+ records.push(JSON.parse(line));
53744
+ if (records.length >= size) break;
53745
+ }
53746
+ } finally {
53747
+ lines.close();
53748
+ }
53749
+ return records;
53750
+ };
53751
+ }
53752
+ };
53753
+ var SampleRunner = new SampleRunnerClass();
53754
+
53574
53755
  // src/workers/ExecutorWorker.ts
53575
53756
  registerExecutorWorker();
53576
53757
  /*! Bundled license information: