hobots 1.10.0 → 2.0.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 +3 -3
- package/dist/index.cjs +62 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -11
- package/dist/index.d.ts +14 -11
- package/dist/index.js +62 -39
- package/dist/index.js.map +1 -1
- package/dist/protocol.cjs.map +1 -1
- package/dist/protocol.d.cts +19 -6
- package/dist/protocol.d.ts +19 -6
- package/docs/api-reference.md +8 -8
- package/docs/concepts.md +15 -13
- package/docs/protocol.md +3 -3
- package/docs/tasks.md +1 -1
- package/docs/telemetry.md +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -113,7 +113,7 @@ try {
|
|
|
113
113
|
await processarNota(nota);
|
|
114
114
|
} catch (error) {
|
|
115
115
|
Hobots.captureException(error, {
|
|
116
|
-
|
|
116
|
+
type: 'error',
|
|
117
117
|
tags: { cliente: 'acme' },
|
|
118
118
|
fingerprint: ['sefaz-timeout'], // agrupa manualmente; sem isso o servidor agrupa pelo stack
|
|
119
119
|
});
|
|
@@ -123,9 +123,9 @@ try {
|
|
|
123
123
|
Hobots.captureMessage('Lote processado com sucesso', 'success');
|
|
124
124
|
```
|
|
125
125
|
|
|
126
|
-
Ambas retornam o `event_id` (ou `undefined` se não inicializado). O hint aceita `
|
|
126
|
+
Ambas retornam o `event_id` (ou `undefined` se não inicializado). O hint aceita `type`, `tags`, `extra` e `fingerprint`. Erros **não tratados** são capturados e flushados automaticamente antes do processo sair — desligue com `captureUnhandled: false`. Os eventos aparecem agrupados em issues no módulo **Issues** do app.
|
|
127
127
|
|
|
128
|
-
`
|
|
128
|
+
`EventType = 'fatal' | 'error' | 'warning' | 'success'`. Não há `'info'`/`'debug'`: narração não abre issue — para isso existem o log da execução e as breadcrumbs. Use `'success'` para reportar itens concluídos com êxito — contam como eventos, mas **não** viram issue nem disparam alertas.
|
|
129
129
|
|
|
130
130
|
### Breadcrumbs
|
|
131
131
|
|
package/dist/index.cjs
CHANGED
|
@@ -161,7 +161,7 @@ function scrubSecrets(text) {
|
|
|
161
161
|
|
|
162
162
|
// src/utils.ts
|
|
163
163
|
var SDK_NAME = "hobots";
|
|
164
|
-
var SDK_VERSION = true ? "
|
|
164
|
+
var SDK_VERSION = true ? "2.0.1" : "0.0.0-dev";
|
|
165
165
|
function isNode() {
|
|
166
166
|
return typeof process !== "undefined" && typeof process.versions?.node === "string";
|
|
167
167
|
}
|
|
@@ -291,34 +291,34 @@ function safeString(value) {
|
|
|
291
291
|
}
|
|
292
292
|
return String(value);
|
|
293
293
|
}
|
|
294
|
-
function eventFromException(error,
|
|
295
|
-
let
|
|
294
|
+
function eventFromException(error, type = "error") {
|
|
295
|
+
let exceptionType;
|
|
296
296
|
let value;
|
|
297
297
|
let frames = [];
|
|
298
298
|
if (error instanceof Error) {
|
|
299
|
-
|
|
299
|
+
exceptionType = error.name || "Error";
|
|
300
300
|
value = scrubSecrets(error.message);
|
|
301
301
|
frames = parseStacktrace(error.stack);
|
|
302
302
|
} else {
|
|
303
|
-
|
|
303
|
+
exceptionType = "NonError";
|
|
304
304
|
value = scrubSecrets(safeString(error));
|
|
305
305
|
}
|
|
306
306
|
return {
|
|
307
307
|
event_id: uuid4(),
|
|
308
308
|
timestamp: nowISO(),
|
|
309
|
-
|
|
309
|
+
type,
|
|
310
310
|
exception: {
|
|
311
|
-
type,
|
|
311
|
+
type: exceptionType,
|
|
312
312
|
value,
|
|
313
313
|
...frames.length > 0 ? { stacktrace: { frames } } : {}
|
|
314
314
|
}
|
|
315
315
|
};
|
|
316
316
|
}
|
|
317
|
-
function eventFromMessage(message,
|
|
317
|
+
function eventFromMessage(message, type = "warning") {
|
|
318
318
|
return {
|
|
319
319
|
event_id: uuid4(),
|
|
320
320
|
timestamp: nowISO(),
|
|
321
|
-
|
|
321
|
+
type,
|
|
322
322
|
message: scrubSecrets(message)
|
|
323
323
|
};
|
|
324
324
|
}
|
|
@@ -354,7 +354,8 @@ var Transport = class {
|
|
|
354
354
|
return;
|
|
355
355
|
}
|
|
356
356
|
if (this.buffer.length >= this.maxQueueSize) {
|
|
357
|
-
this.buffer.
|
|
357
|
+
const dropIndex = this.buffer.findIndex((queued) => queued.type !== "heartbeat");
|
|
358
|
+
this.buffer.splice(dropIndex === -1 ? 0 : dropIndex, 1);
|
|
358
359
|
this.logger.warn("fila cheia, item mais antigo descartado");
|
|
359
360
|
}
|
|
360
361
|
this.buffer.push(item);
|
|
@@ -445,6 +446,8 @@ function runtimeContexts() {
|
|
|
445
446
|
}
|
|
446
447
|
var Client = class {
|
|
447
448
|
logger;
|
|
449
|
+
/** Eco de lifecycle (Transaction.emit) e afins só imprimem no terminal com debug ligado. */
|
|
450
|
+
debug;
|
|
448
451
|
// campos privados + getters de protótipo: o clientSecret não aparece em
|
|
449
452
|
// JSON.stringify/serialização enumerável do Client (ex.: um objeto capturado
|
|
450
453
|
// que referencie o client), mas client.options/config seguem funcionando
|
|
@@ -453,7 +456,8 @@ var Client = class {
|
|
|
453
456
|
transport;
|
|
454
457
|
constructor(options, config = resolveConfig(options)) {
|
|
455
458
|
this.#options = options;
|
|
456
|
-
this.
|
|
459
|
+
this.debug = !!options.debug;
|
|
460
|
+
this.logger = makeLogger(this.debug);
|
|
457
461
|
this.#config = config;
|
|
458
462
|
this.transport = new Transport({
|
|
459
463
|
endpoint: this.#config.ingestEndpoint,
|
|
@@ -477,10 +481,10 @@ var Client = class {
|
|
|
477
481
|
return `Client { baseUrl: '${this.#config.baseUrl}' }`;
|
|
478
482
|
}
|
|
479
483
|
captureException(error, hint, scope2) {
|
|
480
|
-
return this.captureEvent(eventFromException(error, hint?.
|
|
484
|
+
return this.captureEvent(eventFromException(error, hint?.type ?? "error"), hint, scope2);
|
|
481
485
|
}
|
|
482
|
-
captureMessage(message,
|
|
483
|
-
return this.captureEvent(eventFromMessage(message,
|
|
486
|
+
captureMessage(message, type, hint, scope2) {
|
|
487
|
+
return this.captureEvent(eventFromMessage(message, type), hint, scope2);
|
|
484
488
|
}
|
|
485
489
|
captureEvent(event, hint, scope2) {
|
|
486
490
|
const sampleRate = this.options.sampleRate ?? 1;
|
|
@@ -502,7 +506,7 @@ var Client = class {
|
|
|
502
506
|
const extra = { ...scope2.extra, ...event.extra, ...hint?.extra };
|
|
503
507
|
return {
|
|
504
508
|
...event,
|
|
505
|
-
|
|
509
|
+
type: hint?.type ?? event.type ?? scope2.type ?? "error",
|
|
506
510
|
release: event.release ?? this.options.release,
|
|
507
511
|
environment: event.environment ?? this.options.environment ?? "production",
|
|
508
512
|
tags: Object.keys(tags).length > 0 ? tags : void 0,
|
|
@@ -540,6 +544,8 @@ var Client = class {
|
|
|
540
544
|
|
|
541
545
|
// src/heartbeat.ts
|
|
542
546
|
var DEFAULT_INTERVAL_MS = 3e4;
|
|
547
|
+
var MIN_REPORTED_INTERVAL_MS = 1e3;
|
|
548
|
+
var MAX_REPORTED_INTERVAL_MS = 9e5;
|
|
543
549
|
var Heartbeat = class {
|
|
544
550
|
constructor(client2, options = {}, instanceId) {
|
|
545
551
|
this.client = client2;
|
|
@@ -556,12 +562,22 @@ var Heartbeat = class {
|
|
|
556
562
|
start() {
|
|
557
563
|
if (this.timer) return;
|
|
558
564
|
this.beat();
|
|
559
|
-
this.
|
|
565
|
+
this.schedule();
|
|
566
|
+
}
|
|
567
|
+
/** Jitter de ±10%: uma frota iniciada junto não sincroniza todos os beats no mesmo segundo. */
|
|
568
|
+
schedule() {
|
|
569
|
+
this.timer = setTimeout(
|
|
570
|
+
() => {
|
|
571
|
+
this.beat();
|
|
572
|
+
this.schedule();
|
|
573
|
+
},
|
|
574
|
+
this.intervalMs * (0.9 + Math.random() * 0.2)
|
|
575
|
+
);
|
|
560
576
|
unrefTimer(this.timer);
|
|
561
577
|
}
|
|
562
578
|
stop() {
|
|
563
579
|
if (this.timer) {
|
|
564
|
-
|
|
580
|
+
clearTimeout(this.timer);
|
|
565
581
|
this.timer = void 0;
|
|
566
582
|
}
|
|
567
583
|
}
|
|
@@ -569,7 +585,7 @@ var Heartbeat = class {
|
|
|
569
585
|
this.client.sendHeartbeat({
|
|
570
586
|
instance_id: this.instanceId,
|
|
571
587
|
name: this.name,
|
|
572
|
-
interval_ms: this.intervalMs,
|
|
588
|
+
interval_ms: Math.min(Math.max(this.intervalMs, MIN_REPORTED_INTERVAL_MS), MAX_REPORTED_INTERVAL_MS),
|
|
573
589
|
timestamp: nowISO(),
|
|
574
590
|
metadata: this.metadata
|
|
575
591
|
});
|
|
@@ -608,7 +624,7 @@ function installGlobalHandlers(client2, getScope) {
|
|
|
608
624
|
process.on(
|
|
609
625
|
"uncaughtException",
|
|
610
626
|
markInternal((error) => {
|
|
611
|
-
client2.captureException(error, {
|
|
627
|
+
client2.captureException(error, { type: "fatal", tags: { mechanism: "uncaughtException" } }, getScope());
|
|
612
628
|
void client2.flush(2e3).then(async () => {
|
|
613
629
|
if (externalListenerCount("uncaughtException") === 0) {
|
|
614
630
|
await awaitEmergencyTasks(EMERGENCY_REPORT_CAP_MS);
|
|
@@ -621,7 +637,7 @@ function installGlobalHandlers(client2, getScope) {
|
|
|
621
637
|
process.on(
|
|
622
638
|
"unhandledRejection",
|
|
623
639
|
markInternal((reason) => {
|
|
624
|
-
client2.captureException(reason, {
|
|
640
|
+
client2.captureException(reason, { type: "error", tags: { mechanism: "unhandledRejection" } }, getScope());
|
|
625
641
|
void client2.flush(2e3);
|
|
626
642
|
})
|
|
627
643
|
);
|
|
@@ -652,7 +668,7 @@ var Scope = class _Scope {
|
|
|
652
668
|
user;
|
|
653
669
|
contexts = {};
|
|
654
670
|
extra = {};
|
|
655
|
-
|
|
671
|
+
type;
|
|
656
672
|
fingerprint;
|
|
657
673
|
breadcrumbs = [];
|
|
658
674
|
setTag(key, value) {
|
|
@@ -683,8 +699,8 @@ var Scope = class _Scope {
|
|
|
683
699
|
Object.assign(this.extra, extras);
|
|
684
700
|
return this;
|
|
685
701
|
}
|
|
686
|
-
|
|
687
|
-
this.
|
|
702
|
+
setType(type) {
|
|
703
|
+
this.type = type;
|
|
688
704
|
return this;
|
|
689
705
|
}
|
|
690
706
|
setFingerprint(fingerprint) {
|
|
@@ -708,7 +724,7 @@ var Scope = class _Scope {
|
|
|
708
724
|
next.user = this.user ? { ...this.user } : void 0;
|
|
709
725
|
next.contexts = Object.fromEntries(Object.entries(this.contexts).map(([k, v]) => [k, { ...v }]));
|
|
710
726
|
next.extra = { ...this.extra };
|
|
711
|
-
next.
|
|
727
|
+
next.type = this.type;
|
|
712
728
|
next.fingerprint = this.fingerprint ? [...this.fingerprint] : void 0;
|
|
713
729
|
next.breadcrumbs = [...this.breadcrumbs];
|
|
714
730
|
return next;
|
|
@@ -816,15 +832,18 @@ var Transaction = class {
|
|
|
816
832
|
// antes do RunReporter conectar) — replay no setLifecycleSink
|
|
817
833
|
lifecycleBacklog = [];
|
|
818
834
|
/**
|
|
819
|
-
*
|
|
820
|
-
*
|
|
821
|
-
*
|
|
835
|
+
* Registra o início/fim de uma step ou transaction: espelha a linha no sink de
|
|
836
|
+
* lifecycle, se conectado (é assim que os eventos de performance aparecem nos
|
|
837
|
+
* logs da run no app), e ecoa no terminal apenas com `debug: true` — o SDK não
|
|
838
|
+
* escreve no stdout do processo do usuário sem permissão.
|
|
822
839
|
*/
|
|
823
840
|
emit(kind, phase, label, startTs, endTs, status) {
|
|
824
841
|
const durationMs = startTs && endTs ? new Date(endTs).getTime() - new Date(startTs).getTime() : void 0;
|
|
825
|
-
const tag = colorize(`[hobots:${kind}] ${phase.toUpperCase()}`, phase === "finish" ? colorForStatus(status ?? "ok") : "cyan");
|
|
826
842
|
const suffix = durationMs != null ? ` (${durationMs}ms)` : "";
|
|
827
|
-
|
|
843
|
+
if (this.client?.debug) {
|
|
844
|
+
const tag = colorize(`[hobots:${kind}] ${phase.toUpperCase()}`, phase === "finish" ? colorForStatus(status ?? "ok") : "cyan");
|
|
845
|
+
console.log(`${tag} ${label}${suffix}`);
|
|
846
|
+
}
|
|
828
847
|
const level = phase === "finish" ? levelForStatus(status ?? "ok") : "info";
|
|
829
848
|
const message = `[${kind}] ${phase.toUpperCase()} ${label}${suffix}`;
|
|
830
849
|
if (this.lifecycleSink) this.lifecycleSink(level, message);
|
|
@@ -1871,13 +1890,17 @@ var TaskAgent = class {
|
|
|
1871
1890
|
try {
|
|
1872
1891
|
const response = await this.api.poll(
|
|
1873
1892
|
{
|
|
1874
|
-
|
|
1893
|
+
instance: {
|
|
1875
1894
|
// identidade canônica do agente no painel — o mesmo instanceId do
|
|
1876
1895
|
// heartbeat da Telemetria, então os dois canais convergem em um agente
|
|
1877
1896
|
instance_id: this.instanceId,
|
|
1878
1897
|
version: SDK_VERSION,
|
|
1879
1898
|
host: hostname(),
|
|
1880
|
-
tasks: [...this.registry.keys()]
|
|
1899
|
+
tasks: [...this.registry.keys()],
|
|
1900
|
+
// o servidor calcula a tolerância de offline (3×) a partir disto em
|
|
1901
|
+
// agentes poll-only — sem o campo, um poll lento flaparia offline/online.
|
|
1902
|
+
// Clampado à faixa aceita pelo schema do poll (1s..15min).
|
|
1903
|
+
poll_interval_ms: Math.min(Math.max(this.pollIntervalMs, 1e3), 9e5)
|
|
1881
1904
|
},
|
|
1882
1905
|
max: Math.min(MAX_CONCURRENCY, this.concurrency - this.activeRuns.size),
|
|
1883
1906
|
tasks: [...this.registry.keys()]
|
|
@@ -2061,10 +2084,10 @@ function init(options) {
|
|
|
2061
2084
|
function getClient() {
|
|
2062
2085
|
return client;
|
|
2063
2086
|
}
|
|
2064
|
-
function toRunLogLevel(
|
|
2065
|
-
return
|
|
2087
|
+
function toRunLogLevel(type) {
|
|
2088
|
+
return type === "fatal" ? "error" : type;
|
|
2066
2089
|
}
|
|
2067
|
-
function forwardAttachment(attachment, message,
|
|
2090
|
+
function forwardAttachment(attachment, message, type) {
|
|
2068
2091
|
const attach = currentRunAttach();
|
|
2069
2092
|
if (!attach) {
|
|
2070
2093
|
client?.logger.warn("attachment descartado \u2014 s\xF3 \xE9 suportado dentro de uma execu\xE7\xE3o de task (register/start)");
|
|
@@ -2073,7 +2096,7 @@ function forwardAttachment(attachment, message, level) {
|
|
|
2073
2096
|
const withMeta = typeof attachment === "object" && "image" in attachment ? attachment : { image: attachment };
|
|
2074
2097
|
void attach(withMeta.image, {
|
|
2075
2098
|
message,
|
|
2076
|
-
level: toRunLogLevel(
|
|
2099
|
+
level: toRunLogLevel(type),
|
|
2077
2100
|
caption: withMeta.caption,
|
|
2078
2101
|
contentType: withMeta.contentType
|
|
2079
2102
|
});
|
|
@@ -2081,15 +2104,15 @@ function forwardAttachment(attachment, message, level) {
|
|
|
2081
2104
|
function captureException(error, hint) {
|
|
2082
2105
|
if (hint?.attachment) {
|
|
2083
2106
|
const message = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
2084
|
-
forwardAttachment(hint.attachment, message, hint.
|
|
2107
|
+
forwardAttachment(hint.attachment, message, hint.type ?? "error");
|
|
2085
2108
|
}
|
|
2086
2109
|
return client?.captureException(error, hint, scope);
|
|
2087
2110
|
}
|
|
2088
|
-
function captureMessage(message,
|
|
2111
|
+
function captureMessage(message, type = "warning", hint) {
|
|
2089
2112
|
if (hint?.attachment) {
|
|
2090
|
-
forwardAttachment(hint.attachment, message, hint.
|
|
2113
|
+
forwardAttachment(hint.attachment, message, hint.type ?? type);
|
|
2091
2114
|
}
|
|
2092
|
-
return client?.captureMessage(message,
|
|
2115
|
+
return client?.captureMessage(message, type, hint, scope);
|
|
2093
2116
|
}
|
|
2094
2117
|
function addBreadcrumb(crumb) {
|
|
2095
2118
|
scope.addBreadcrumb(crumb, client?.options.maxBreadcrumbs ?? DEFAULT_MAX_BREADCRUMBS);
|