hobots 6.0.0 → 6.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -291,6 +291,8 @@ interface RunItemHandle<T = unknown> {
291
291
  readonly reported: boolean;
292
292
  /** Desfecho registrado até agora (undefined = nenhum). */
293
293
  readonly status: RunItemStatus | undefined;
294
+ /** Motivo do desfecho (`failed`/`occurrence`) — vai na linha `[item] FINISH` do log. */
295
+ readonly message: string | undefined;
294
296
  /** Tentativa em curso (1-based) — o handler pode ler para logar. */
295
297
  readonly attempt: number;
296
298
  /**
package/dist/index.d.ts CHANGED
@@ -291,6 +291,8 @@ interface RunItemHandle<T = unknown> {
291
291
  readonly reported: boolean;
292
292
  /** Desfecho registrado até agora (undefined = nenhum). */
293
293
  readonly status: RunItemStatus | undefined;
294
+ /** Motivo do desfecho (`failed`/`occurrence`) — vai na linha `[item] FINISH` do log. */
295
+ readonly message: string | undefined;
294
296
  /** Tentativa em curso (1-based) — o handler pode ler para logar. */
295
297
  readonly attempt: number;
296
298
  /**
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
 
5
5
  // src/utils.ts
6
6
  var SDK_NAME = "hobots";
7
- var SDK_VERSION = true ? "6.0.0" : "0.0.0-dev";
7
+ var SDK_VERSION = true ? "6.1.0" : "0.0.0-dev";
8
8
  function isNode() {
9
9
  return typeof process !== "undefined" && typeof process.versions?.node === "string";
10
10
  }
@@ -1479,6 +1479,10 @@ function stepId() {
1479
1479
  function stepLabel(op, description) {
1480
1480
  return [op, description].filter(Boolean).join(" \u2014 ") || "(sem nome)";
1481
1481
  }
1482
+ var ITEM_OP = "item";
1483
+ function lifecycleLabel(kind, op, description) {
1484
+ return stepLabel(kind === "item" && op === ITEM_OP ? void 0 : op, description);
1485
+ }
1482
1486
  function errorText(error) {
1483
1487
  if (error instanceof Error) return error.message || error.name;
1484
1488
  return safeString(error);
@@ -1511,21 +1515,25 @@ function colorForStatus(status) {
1511
1515
  }
1512
1516
  }
1513
1517
  var Step = class {
1514
- constructor(transaction, parentStepId, op, description) {
1518
+ constructor(transaction, parentStepId, op, description, kind = "step") {
1515
1519
  this.transaction = transaction;
1516
1520
  this.parentStepId = parentStepId;
1517
1521
  this.op = op;
1518
1522
  this.description = description;
1523
+ this.kind = kind;
1519
1524
  }
1520
1525
  transaction;
1521
1526
  parentStepId;
1522
1527
  op;
1523
1528
  description;
1529
+ kind;
1524
1530
  stepId = stepId();
1525
1531
  startTimestamp = nowISO();
1526
1532
  status = "ok";
1527
1533
  endTimestamp;
1528
1534
  _data;
1535
+ // desfecho que a linha de FINISH reporta no lugar do status da etapa (item do `each`)
1536
+ outcome;
1529
1537
  // ── API do bot ────────────────────────────────────────────────────────────
1530
1538
  warning(message, options) {
1531
1539
  if (this.status !== "error") this._setStatus("warning", message);
@@ -1564,6 +1572,13 @@ var Step = class {
1564
1572
  this._close();
1565
1573
  return this;
1566
1574
  }
1575
+ /**
1576
+ * Faz a linha de FINISH reportar `status`/`reason` em vez do status da etapa — o
1577
+ * `items.each` usa para o log do item seguir o desfecho dele (sucesso/falha/ocorrência).
1578
+ */
1579
+ _setOutcome(status, reason) {
1580
+ this.outcome = { status, reason };
1581
+ }
1567
1582
  _setData(key, value) {
1568
1583
  this._data = { ...this._data, [key]: value };
1569
1584
  }
@@ -1595,14 +1610,15 @@ var Step = class {
1595
1610
  _close() {
1596
1611
  if (this.endTimestamp) return;
1597
1612
  this.endTimestamp = nowISO();
1613
+ const { status, reason } = this.outcome ?? { status: this.status, reason: this.reason() };
1598
1614
  this.transaction.emit(
1599
- "step",
1615
+ this.kind,
1600
1616
  "finish",
1601
- stepLabel(this.op, this.description),
1617
+ lifecycleLabel(this.kind, this.op, this.description),
1602
1618
  this.startTimestamp,
1603
1619
  this.endTimestamp,
1604
- this.status,
1605
- this.reason()
1620
+ status,
1621
+ reason
1606
1622
  );
1607
1623
  }
1608
1624
  toJSON() {
@@ -1657,7 +1673,7 @@ var Transaction = class {
1657
1673
  emit(kind, phase, label, startTs, endTs, status, reason) {
1658
1674
  const durationMs = startTs && endTs ? new Date(endTs).getTime() - new Date(startTs).getTime() : void 0;
1659
1675
  const suffix = (durationMs != null ? ` (${durationMs}ms)` : "") + (reason ? `: ${reason}` : "");
1660
- const localFinish = this.echo && kind === "step" && phase === "finish";
1676
+ const localFinish = this.echo && kind !== "transaction" && phase === "finish";
1661
1677
  if (this.client?.debug || localFinish) {
1662
1678
  const tag = colorize(`[hobots:${kind}] ${phase.toUpperCase()}`, phase === "finish" ? colorForStatus(status ?? "ok") : "cyan");
1663
1679
  console.log(`${tag} ${label}${suffix}`);
@@ -1704,13 +1720,16 @@ var Transaction = class {
1704
1720
  if (!step) return fn();
1705
1721
  return runWithStepScope({ transaction: this, step }, fn);
1706
1722
  }
1707
- /** Abre uma etapa sob a corrente (ou direta da transaction). Interno: o bot usa `step()`. */
1708
- start(op, description) {
1723
+ /**
1724
+ * Abre uma etapa sob a corrente (ou direta da transaction). Interno: o bot usa `step()`; o
1725
+ * `items.each` abre com `kind: 'item'` (linhas `[item] START/FINISH`).
1726
+ */
1727
+ start(op, description, kind = "step") {
1709
1728
  this.stepGuard?.();
1710
1729
  const parent = this.currentStep();
1711
- const step = new Step(this, parent ? parent.stepId : this.stepId, op, description);
1730
+ const step = new Step(this, parent ? parent.stepId : this.stepId, op, description, kind);
1712
1731
  this.steps.push(step);
1713
- this.emit("step", "start", stepLabel(op, description));
1732
+ this.emit(kind, "start", lifecycleLabel(kind, op, description));
1714
1733
  return step;
1715
1734
  }
1716
1735
  /**
@@ -1874,6 +1893,7 @@ var ItemHandle = class {
1874
1893
  id;
1875
1894
  reported = false;
1876
1895
  status;
1896
+ message;
1877
1897
  attempt = 1;
1878
1898
  accumulated = {};
1879
1899
  data(payload) {
@@ -1895,6 +1915,7 @@ var ItemHandle = class {
1895
1915
  this.attempt = attempt;
1896
1916
  this.reported = false;
1897
1917
  this.status = void 0;
1918
+ this.message = void 0;
1898
1919
  this.accumulated = {};
1899
1920
  if (this.step && (this.step.status === "error" || this.step.status === "warning")) {
1900
1921
  this.step._setStatus("ok");
@@ -1904,6 +1925,7 @@ var ItemHandle = class {
1904
1925
  record(status, message, payload, image3) {
1905
1926
  this.reported = true;
1906
1927
  this.status = status;
1928
+ this.message = message;
1907
1929
  const merged = { ...this.basePayload ?? {}, ...this.accumulated, ...payload ?? {} };
1908
1930
  this.sink.report(status, message, Object.keys(merged).length > 0 ? merged : void 0, this.id, image3);
1909
1931
  }
@@ -1955,6 +1977,10 @@ function knownLength(list) {
1955
1977
  const length = list?.length;
1956
1978
  return typeof length === "number" && Number.isInteger(length) && length >= 1 ? length : void 0;
1957
1979
  }
1980
+ var ITEM_LOG_STATUS = { succeeded: "ok", occurrence: "warning", failed: "error" };
1981
+ function setItemOutcome(step, status, message) {
1982
+ step._setOutcome(ITEM_LOG_STATUS[status], status === "succeeded" ? void 0 : message);
1983
+ }
1958
1984
  function skippedMessage(count) {
1959
1985
  if (count === 1) return "1 item pulado: j\xE1 conclu\xEDdo neste trabalho numa execu\xE7\xE3o anterior";
1960
1986
  return `${count} itens pulados: j\xE1 conclu\xEDdos neste trabalho numa execu\xE7\xE3o anterior`;
@@ -1964,18 +1990,29 @@ async function eachItem(list, handler, options, sink) {
1964
1990
  const requested = options.retries ?? DEFAULT_EACH_RETRIES;
1965
1991
  const totalRetries = Number.isFinite(requested) ? Math.max(0, Math.floor(requested)) : DEFAULT_EACH_RETRIES;
1966
1992
  const explicitId = options.id !== void 0;
1967
- const length = options.progress === false ? void 0 : knownLength(list);
1968
- if (length !== void 0) sink.declareTotal(length);
1969
1993
  const done = options.skipDone === false ? void 0 : await sink.doneItemIds();
1970
- let index = 0;
1971
- for await (const element of list) {
1994
+ const isDone = (id) => id !== void 0 && done !== void 0 && done.has(id);
1995
+ const length = knownLength(list);
1996
+ let entries;
1997
+ if (length !== void 0 && done !== void 0 && done.size > 0) {
1998
+ const pending = [];
1999
+ let position = 0;
2000
+ for await (const element of list) {
2001
+ const id = resolveId(options.id, element, position, explicitId);
2002
+ if (isDone(id)) result.skipped += 1;
2003
+ else pending.push({ element, index: position, id });
2004
+ position += 1;
2005
+ }
2006
+ if (options.progress !== false && pending.length > 0) sink.declareTotal(pending.length);
2007
+ entries = pending;
2008
+ } else {
2009
+ if (options.progress !== false && length !== void 0) sink.declareTotal(length);
2010
+ entries = lazyEntries(list, options.id, explicitId);
2011
+ }
2012
+ for await (const { element, index, id } of entries) {
1972
2013
  sink.checkCanceled();
1973
- const id = resolveId(options.id, element, index, explicitId);
1974
- if (done?.size && id !== void 0 && done.has(id)) {
1975
- result.total += 1;
2014
+ if (isDone(id)) {
1976
2015
  result.skipped += 1;
1977
- if (options.progress !== false) sink.advance();
1978
- index += 1;
1979
2016
  continue;
1980
2017
  }
1981
2018
  const description = options.description?.(element, index) ?? id ?? `#${index + 1}`;
@@ -2025,20 +2062,34 @@ async function eachItem(list, handler, options, sink) {
2025
2062
  } else {
2026
2063
  const message = thrownMessage(error);
2027
2064
  if (!handle.reported) handle.failed(message);
2028
- step?._failed(error);
2065
+ if (step) {
2066
+ setItemOutcome(step, handle.status ?? "failed", handle.message);
2067
+ step._failed(error);
2068
+ }
2029
2069
  sink.logError(`item ${description} falhou: ${message}`);
2030
2070
  }
2031
2071
  }
2032
- step?._close();
2072
+ const final = handle.status ?? "succeeded";
2073
+ if (step) {
2074
+ setItemOutcome(step, final, handle.message);
2075
+ step._close();
2076
+ }
2033
2077
  if (options.progress !== false) sink.advance();
2034
2078
  result.total += 1;
2035
- result[handle.status ?? "succeeded"] += 1;
2036
- index += 1;
2079
+ result[final] += 1;
2037
2080
  if (options.flush !== false) await sink.flush();
2038
2081
  }
2082
+ result.total += result.skipped;
2039
2083
  if (result.skipped > 0) sink.logInfo(skippedMessage(result.skipped));
2040
2084
  return result;
2041
2085
  }
2086
+ async function* lazyEntries(list, spec, explicit) {
2087
+ let index = 0;
2088
+ for await (const element of list) {
2089
+ yield { element, index, id: resolveId(spec, element, index, explicit) };
2090
+ index += 1;
2091
+ }
2092
+ }
2042
2093
  function noteAttempts(step, attempts) {
2043
2094
  if (attempts > 1) step._setData("attempts", attempts);
2044
2095
  }
@@ -2424,7 +2475,7 @@ var RunReporter = class {
2424
2475
  tx.setStepGuard(checkCanceled);
2425
2476
  const eachSink = {
2426
2477
  report: (status, message, payload, id, image3) => this.addItem(status, message, payload, id, image3),
2427
- startStep: (op, description) => tx.start(op, description),
2478
+ startStep: (op, description) => tx.start(op, description, "item"),
2428
2479
  stepScope: (step, fn) => tx.scope(step, fn),
2429
2480
  declareTotal: (total) => {
2430
2481
  if (this.progressTotal == null) this.setProgress(this.progressCurrent, total);