hobots 6.0.0 → 6.1.1
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/README.md +1 -0
- package/dist/index.cjs +80 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +80 -26
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -231,6 +231,7 @@ Sem handler próprio de sinal, um `SIGINT`/`SIGTERM` (ou erro fatal) com execuç
|
|
|
231
231
|
|
|
232
232
|
## Changelog resumido
|
|
233
233
|
|
|
234
|
+
- **6.1.0** — cada item do `items.each` termina com uma linha `[item] FINISH <chave> (<ms>)` no log, no nível do desfecho (`success`/`error`/`warning`), no lugar do `[step] FINISH item — …`; `item.message` com o motivo. Detalhes no [CHANGELOG](./CHANGELOG.md).
|
|
234
235
|
- **6.0.0** — **breaking**: o handler de `items.each` recebe o handle do item, `async (item, index) => …` (`index` opcional); o elemento da lista é `item.value` e a posição, `item.index`. Migração: `async (conta, item, i) => …` → `async (item, i) => { const conta = item.value; … }`. `options.id`/`payload`/`description` continuam recebendo `(element, index)`. **Payload padrão:** sem `options.payload`, os campos do elemento (objeto simples, cópia rasa) viram o payload base do item; `options.payload` substitui, `payload: false` desliga. Detalhes no [CHANGELOG](./CHANGELOG.md).
|
|
235
236
|
- **5.2.0** — **`item.data(payload)`** no handle do `items.each`: guarda atributos do item sem registrar desfecho; mescla a cada chamada e entra em qualquer desfecho (inclusive nos implícitos), entre o `options.payload` do `each` e o payload da chamada de desfecho; zera na nova tentativa. Detalhes no [CHANGELOG](./CHANGELOG.md).
|
|
236
237
|
- **5.1.0** — `items.each` com **nova tentativa por item** (`retries: 1`, `retryDelayMs: 5000`; uma etapa só por elemento, `warning` quando a repetição deu certo, `error` + item `failed` quando esgotou), **`Occurrence`** exportada (`throw new Occurrence(msg, payload?, { image }?)` no handler = item `occurrence`, sem repetir), `id` por campo (`'numero'`), chave composta (`['cnpj', 'competencia']`) ou função — padrão: o campo `id` do elemento — e **reexecução sem repetir**: ao executar novamente o trabalho, pula os ids já `succeeded`/`occurrence` (`skipDone: true`; `POST /v1/runs/{id}/items/done`); resultado com `skipped`; `item.id` sempre `string`, `item.attempt`. Detalhes no [CHANGELOG](./CHANGELOG.md).
|
package/dist/index.cjs
CHANGED
|
@@ -45,7 +45,7 @@ module.exports = __toCommonJS(src_exports);
|
|
|
45
45
|
|
|
46
46
|
// src/utils.ts
|
|
47
47
|
var SDK_NAME = "hobots";
|
|
48
|
-
var SDK_VERSION = true ? "6.
|
|
48
|
+
var SDK_VERSION = true ? "6.1.1" : "0.0.0-dev";
|
|
49
49
|
function isNode() {
|
|
50
50
|
return typeof process !== "undefined" && typeof process.versions?.node === "string";
|
|
51
51
|
}
|
|
@@ -1547,6 +1547,10 @@ function stepId() {
|
|
|
1547
1547
|
function stepLabel(op, description) {
|
|
1548
1548
|
return [op, description].filter(Boolean).join(" \u2014 ") || "(sem nome)";
|
|
1549
1549
|
}
|
|
1550
|
+
var ITEM_OP = "item";
|
|
1551
|
+
function lifecycleLabel(kind, op, description) {
|
|
1552
|
+
return stepLabel(kind === "item" && op === ITEM_OP ? void 0 : op, description);
|
|
1553
|
+
}
|
|
1550
1554
|
function errorText(error) {
|
|
1551
1555
|
if (error instanceof Error) return error.message || error.name;
|
|
1552
1556
|
return safeString(error);
|
|
@@ -1579,21 +1583,25 @@ function colorForStatus(status) {
|
|
|
1579
1583
|
}
|
|
1580
1584
|
}
|
|
1581
1585
|
var Step = class {
|
|
1582
|
-
constructor(transaction, parentStepId, op, description) {
|
|
1586
|
+
constructor(transaction, parentStepId, op, description, kind = "step") {
|
|
1583
1587
|
this.transaction = transaction;
|
|
1584
1588
|
this.parentStepId = parentStepId;
|
|
1585
1589
|
this.op = op;
|
|
1586
1590
|
this.description = description;
|
|
1591
|
+
this.kind = kind;
|
|
1587
1592
|
}
|
|
1588
1593
|
transaction;
|
|
1589
1594
|
parentStepId;
|
|
1590
1595
|
op;
|
|
1591
1596
|
description;
|
|
1597
|
+
kind;
|
|
1592
1598
|
stepId = stepId();
|
|
1593
1599
|
startTimestamp = nowISO();
|
|
1594
1600
|
status = "ok";
|
|
1595
1601
|
endTimestamp;
|
|
1596
1602
|
_data;
|
|
1603
|
+
// desfecho que a linha de FINISH reporta no lugar do status da etapa (item do `each`)
|
|
1604
|
+
outcome;
|
|
1597
1605
|
// ── API do bot ────────────────────────────────────────────────────────────
|
|
1598
1606
|
warning(message, options) {
|
|
1599
1607
|
if (this.status !== "error") this._setStatus("warning", message);
|
|
@@ -1632,6 +1640,13 @@ var Step = class {
|
|
|
1632
1640
|
this._close();
|
|
1633
1641
|
return this;
|
|
1634
1642
|
}
|
|
1643
|
+
/**
|
|
1644
|
+
* Faz a linha de FINISH reportar `status`/`reason` em vez do status da etapa — o
|
|
1645
|
+
* `items.each` usa para o log do item seguir o desfecho dele (sucesso/falha/ocorrência).
|
|
1646
|
+
*/
|
|
1647
|
+
_setOutcome(status, reason) {
|
|
1648
|
+
this.outcome = { status, reason };
|
|
1649
|
+
}
|
|
1635
1650
|
_setData(key, value) {
|
|
1636
1651
|
this._data = { ...this._data, [key]: value };
|
|
1637
1652
|
}
|
|
@@ -1663,14 +1678,15 @@ var Step = class {
|
|
|
1663
1678
|
_close() {
|
|
1664
1679
|
if (this.endTimestamp) return;
|
|
1665
1680
|
this.endTimestamp = nowISO();
|
|
1681
|
+
const { status, reason } = this.outcome ?? { status: this.status, reason: this.reason() };
|
|
1666
1682
|
this.transaction.emit(
|
|
1667
|
-
|
|
1683
|
+
this.kind,
|
|
1668
1684
|
"finish",
|
|
1669
|
-
|
|
1685
|
+
lifecycleLabel(this.kind, this.op, this.description),
|
|
1670
1686
|
this.startTimestamp,
|
|
1671
1687
|
this.endTimestamp,
|
|
1672
|
-
|
|
1673
|
-
|
|
1688
|
+
status,
|
|
1689
|
+
reason
|
|
1674
1690
|
);
|
|
1675
1691
|
}
|
|
1676
1692
|
toJSON() {
|
|
@@ -1725,7 +1741,7 @@ var Transaction = class {
|
|
|
1725
1741
|
emit(kind, phase, label, startTs, endTs, status, reason) {
|
|
1726
1742
|
const durationMs = startTs && endTs ? new Date(endTs).getTime() - new Date(startTs).getTime() : void 0;
|
|
1727
1743
|
const suffix = (durationMs != null ? ` (${durationMs}ms)` : "") + (reason ? `: ${reason}` : "");
|
|
1728
|
-
const localFinish = this.echo && kind
|
|
1744
|
+
const localFinish = this.echo && kind !== "transaction" && phase === "finish";
|
|
1729
1745
|
if (this.client?.debug || localFinish) {
|
|
1730
1746
|
const tag = colorize(`[hobots:${kind}] ${phase.toUpperCase()}`, phase === "finish" ? colorForStatus(status ?? "ok") : "cyan");
|
|
1731
1747
|
console.log(`${tag} ${label}${suffix}`);
|
|
@@ -1772,13 +1788,16 @@ var Transaction = class {
|
|
|
1772
1788
|
if (!step) return fn();
|
|
1773
1789
|
return runWithStepScope({ transaction: this, step }, fn);
|
|
1774
1790
|
}
|
|
1775
|
-
/**
|
|
1776
|
-
|
|
1791
|
+
/**
|
|
1792
|
+
* Abre uma etapa sob a corrente (ou direta da transaction). Interno: o bot usa `step()`; o
|
|
1793
|
+
* `items.each` abre com `kind: 'item'` (linhas `[item] START/FINISH`).
|
|
1794
|
+
*/
|
|
1795
|
+
start(op, description, kind = "step") {
|
|
1777
1796
|
this.stepGuard?.();
|
|
1778
1797
|
const parent = this.currentStep();
|
|
1779
|
-
const step = new Step(this, parent ? parent.stepId : this.stepId, op, description);
|
|
1798
|
+
const step = new Step(this, parent ? parent.stepId : this.stepId, op, description, kind);
|
|
1780
1799
|
this.steps.push(step);
|
|
1781
|
-
this.emit(
|
|
1800
|
+
this.emit(kind, "start", lifecycleLabel(kind, op, description));
|
|
1782
1801
|
return step;
|
|
1783
1802
|
}
|
|
1784
1803
|
/**
|
|
@@ -1942,6 +1961,7 @@ var ItemHandle = class {
|
|
|
1942
1961
|
id;
|
|
1943
1962
|
reported = false;
|
|
1944
1963
|
status;
|
|
1964
|
+
message;
|
|
1945
1965
|
attempt = 1;
|
|
1946
1966
|
accumulated = {};
|
|
1947
1967
|
data(payload) {
|
|
@@ -1963,6 +1983,7 @@ var ItemHandle = class {
|
|
|
1963
1983
|
this.attempt = attempt;
|
|
1964
1984
|
this.reported = false;
|
|
1965
1985
|
this.status = void 0;
|
|
1986
|
+
this.message = void 0;
|
|
1966
1987
|
this.accumulated = {};
|
|
1967
1988
|
if (this.step && (this.step.status === "error" || this.step.status === "warning")) {
|
|
1968
1989
|
this.step._setStatus("ok");
|
|
@@ -1972,6 +1993,7 @@ var ItemHandle = class {
|
|
|
1972
1993
|
record(status, message, payload, image3) {
|
|
1973
1994
|
this.reported = true;
|
|
1974
1995
|
this.status = status;
|
|
1996
|
+
this.message = message;
|
|
1975
1997
|
const merged = { ...this.basePayload ?? {}, ...this.accumulated, ...payload ?? {} };
|
|
1976
1998
|
this.sink.report(status, message, Object.keys(merged).length > 0 ? merged : void 0, this.id, image3);
|
|
1977
1999
|
}
|
|
@@ -2023,6 +2045,10 @@ function knownLength(list) {
|
|
|
2023
2045
|
const length = list?.length;
|
|
2024
2046
|
return typeof length === "number" && Number.isInteger(length) && length >= 1 ? length : void 0;
|
|
2025
2047
|
}
|
|
2048
|
+
var ITEM_LOG_STATUS = { succeeded: "ok", occurrence: "warning", failed: "error" };
|
|
2049
|
+
function setItemOutcome(step, status, message) {
|
|
2050
|
+
step._setOutcome(ITEM_LOG_STATUS[status], status === "succeeded" ? void 0 : message);
|
|
2051
|
+
}
|
|
2026
2052
|
function skippedMessage(count) {
|
|
2027
2053
|
if (count === 1) return "1 item pulado: j\xE1 conclu\xEDdo neste trabalho numa execu\xE7\xE3o anterior";
|
|
2028
2054
|
return `${count} itens pulados: j\xE1 conclu\xEDdos neste trabalho numa execu\xE7\xE3o anterior`;
|
|
@@ -2032,18 +2058,29 @@ async function eachItem(list, handler, options, sink) {
|
|
|
2032
2058
|
const requested = options.retries ?? DEFAULT_EACH_RETRIES;
|
|
2033
2059
|
const totalRetries = Number.isFinite(requested) ? Math.max(0, Math.floor(requested)) : DEFAULT_EACH_RETRIES;
|
|
2034
2060
|
const explicitId = options.id !== void 0;
|
|
2035
|
-
const length = options.progress === false ? void 0 : knownLength(list);
|
|
2036
|
-
if (length !== void 0) sink.declareTotal(length);
|
|
2037
2061
|
const done = options.skipDone === false ? void 0 : await sink.doneItemIds();
|
|
2038
|
-
|
|
2039
|
-
|
|
2062
|
+
const isDone = (id) => id !== void 0 && done !== void 0 && done.has(id);
|
|
2063
|
+
const length = knownLength(list);
|
|
2064
|
+
let entries;
|
|
2065
|
+
if (length !== void 0 && done !== void 0 && done.size > 0) {
|
|
2066
|
+
const pending = [];
|
|
2067
|
+
let position = 0;
|
|
2068
|
+
for await (const element of list) {
|
|
2069
|
+
const id = resolveId(options.id, element, position, explicitId);
|
|
2070
|
+
if (isDone(id)) result.skipped += 1;
|
|
2071
|
+
else pending.push({ element, index: position, id });
|
|
2072
|
+
position += 1;
|
|
2073
|
+
}
|
|
2074
|
+
if (options.progress !== false && pending.length > 0) sink.declareTotal(pending.length);
|
|
2075
|
+
entries = pending;
|
|
2076
|
+
} else {
|
|
2077
|
+
if (options.progress !== false && length !== void 0) sink.declareTotal(length);
|
|
2078
|
+
entries = lazyEntries(list, options.id, explicitId);
|
|
2079
|
+
}
|
|
2080
|
+
for await (const { element, index, id } of entries) {
|
|
2040
2081
|
sink.checkCanceled();
|
|
2041
|
-
|
|
2042
|
-
if (done?.size && id !== void 0 && done.has(id)) {
|
|
2043
|
-
result.total += 1;
|
|
2082
|
+
if (isDone(id)) {
|
|
2044
2083
|
result.skipped += 1;
|
|
2045
|
-
if (options.progress !== false) sink.advance();
|
|
2046
|
-
index += 1;
|
|
2047
2084
|
continue;
|
|
2048
2085
|
}
|
|
2049
2086
|
const description = options.description?.(element, index) ?? id ?? `#${index + 1}`;
|
|
@@ -2093,20 +2130,35 @@ async function eachItem(list, handler, options, sink) {
|
|
|
2093
2130
|
} else {
|
|
2094
2131
|
const message = thrownMessage(error);
|
|
2095
2132
|
if (!handle.reported) handle.failed(message);
|
|
2096
|
-
step
|
|
2097
|
-
|
|
2133
|
+
if (step) {
|
|
2134
|
+
setItemOutcome(step, handle.status ?? "failed", handle.message);
|
|
2135
|
+
step._failed(error);
|
|
2136
|
+
} else {
|
|
2137
|
+
sink.logError(`item ${description} falhou: ${message}`);
|
|
2138
|
+
}
|
|
2098
2139
|
}
|
|
2099
2140
|
}
|
|
2100
|
-
|
|
2141
|
+
const final = handle.status ?? "succeeded";
|
|
2142
|
+
if (step) {
|
|
2143
|
+
setItemOutcome(step, final, handle.message);
|
|
2144
|
+
step._close();
|
|
2145
|
+
}
|
|
2101
2146
|
if (options.progress !== false) sink.advance();
|
|
2102
2147
|
result.total += 1;
|
|
2103
|
-
result[
|
|
2104
|
-
index += 1;
|
|
2148
|
+
result[final] += 1;
|
|
2105
2149
|
if (options.flush !== false) await sink.flush();
|
|
2106
2150
|
}
|
|
2151
|
+
result.total += result.skipped;
|
|
2107
2152
|
if (result.skipped > 0) sink.logInfo(skippedMessage(result.skipped));
|
|
2108
2153
|
return result;
|
|
2109
2154
|
}
|
|
2155
|
+
async function* lazyEntries(list, spec, explicit) {
|
|
2156
|
+
let index = 0;
|
|
2157
|
+
for await (const element of list) {
|
|
2158
|
+
yield { element, index, id: resolveId(spec, element, index, explicit) };
|
|
2159
|
+
index += 1;
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2110
2162
|
function noteAttempts(step, attempts) {
|
|
2111
2163
|
if (attempts > 1) step._setData("attempts", attempts);
|
|
2112
2164
|
}
|
|
@@ -2492,7 +2544,7 @@ var RunReporter = class {
|
|
|
2492
2544
|
tx.setStepGuard(checkCanceled);
|
|
2493
2545
|
const eachSink = {
|
|
2494
2546
|
report: (status, message, payload, id, image3) => this.addItem(status, message, payload, id, image3),
|
|
2495
|
-
startStep: (op, description) => tx.start(op, description),
|
|
2547
|
+
startStep: (op, description) => tx.start(op, description, "item"),
|
|
2496
2548
|
stepScope: (step, fn) => tx.scope(step, fn),
|
|
2497
2549
|
declareTotal: (total) => {
|
|
2498
2550
|
if (this.progressTotal == null) this.setProgress(this.progressCurrent, total);
|
|
@@ -2502,7 +2554,9 @@ var RunReporter = class {
|
|
|
2502
2554
|
isAborted: () => this.abortController.signal.aborted,
|
|
2503
2555
|
logInfo: (message) => this.log("info", message),
|
|
2504
2556
|
logWarning: (message) => this.log("warning", message),
|
|
2557
|
+
/* v8 ignore start -- a run sempre tem `tx`: o item tem etapa e o erro sai na linha `[item] FINISH` (o each só chama logError sem etapa) */
|
|
2505
2558
|
logError: (message) => this.log("error", message),
|
|
2559
|
+
/* v8 ignore stop */
|
|
2506
2560
|
sleep: (ms) => cancelableSleep(ms, this.abortController.signal),
|
|
2507
2561
|
doneItemIds: () => this.listDoneItemIds(),
|
|
2508
2562
|
flush: () => this.flush()
|