hobots 3.2.2 → 3.3.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/README.md CHANGED
@@ -95,6 +95,7 @@ Os dois podem coexistir no mesmo processo: um monitor sempre ligado + tasks sob
95
95
  | --- | --- |
96
96
  | `hobots.log.debug/info/warn/error/success(msg, ...rest)` | Log da execução, enviado incrementalmente ao app (e ecoado no terminal). Aceita vários argumentos como `console.log` — `hobots.log.info('nota', numero, 'ok')` — cada um vira texto, unidos por espaço. **Nunca lança** — pode ir em `finally`. |
97
97
  | `hobots.items.succeeded(payload?, { id }?)` · `.failed(msg, payload?, …)` · `.occurrence(msg, payload?, …)` · `.add({...})` | **A entrega do robô**: cada item processado, com status e atributos (viram colunas no app — declare-as com a opção `items: { columns: [...] }` do `register`/`start`). Bufferizado, em lotes. |
98
+ | `hobots.items.each(lista, async (el, item) => …, { id, payload }?)` | **Loop com o contrato garantido**: cada elemento vira step + item; exceção no handler = item `failed` com a mensagem e o loop **segue** até o fim; progresso automático. Só cancelamento/timeout interrompem. |
98
99
  | `hobots.progress.total(n)` · `.advance(k = 1)` · `.set(atual, total?)` · `.current` · `.snapshot` | Barra, percentual e tempo restante na solicitação. Sem `total()` nada é enviado. |
99
100
  | `hobots.transaction.startChild(op, descrição)` | Uma **step** no waterfall de performance da execução. A transaction da execução já existe e é finalizada pelo SDK. |
100
101
  | `hobots.attach(imagem, { caption?, level? })` | Pendura um **print** (PNG/JPEG/WEBP, até 5 MB, 20 por execução) na linha de log. Nunca lança nem bloqueia. |
@@ -0,0 +1,31 @@
1
+ // protocol-limits.json
2
+ var protocol_limits_default = {
3
+ MAX_ITEMS_PER_ENVELOPE: 50,
4
+ HEARTBEAT_DEFAULT_INTERVAL_MS: 3e4,
5
+ HEARTBEAT_MIN_INTERVAL_MS: 1e3,
6
+ HEARTBEAT_MAX_INTERVAL_MS: 9e5,
7
+ MAX_CLAIM_PER_POLL: 5,
8
+ MAX_LOG_MESSAGE_CHARS: 1e4,
9
+ MAX_LOGS_PER_PATCH: 100,
10
+ MAX_ERROR_MESSAGE_CHARS: 1e4,
11
+ MAX_ERROR_STACK_CHARS: 2e4,
12
+ MAX_ITEMS_PER_RUN: 1e4,
13
+ MAX_ITEMS_PER_REQUEST: 500,
14
+ MAX_ITEM_MESSAGE_CHARS: 2e3,
15
+ MAX_ITEM_PAYLOAD_BYTES: 16384,
16
+ MAX_ITEM_COLUMNS: 12,
17
+ MAX_ITEM_COLUMN_LABEL_CHARS: 60,
18
+ MAX_ATTACHMENT_BYTES: 5242880,
19
+ MAX_ATTACHMENTS_PER_RUN: 20,
20
+ MAX_ATTACHMENTS_PER_LOG_ENTRY: 4,
21
+ MAX_CAPTION_CHARS: 200,
22
+ MAX_IMAGE_DIMENSION_PX: 2e4
23
+ };
24
+
25
+ // src/wire-limits.ts
26
+ var WIRE_LIMITS = protocol_limits_default;
27
+
28
+ export {
29
+ WIRE_LIMITS
30
+ };
31
+ //# sourceMappingURL=chunk-5WIIACWC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../protocol-limits.json","../src/wire-limits.ts"],"sourcesContent":["{\n \"MAX_ITEMS_PER_ENVELOPE\": 50,\n \"HEARTBEAT_DEFAULT_INTERVAL_MS\": 30000,\n \"HEARTBEAT_MIN_INTERVAL_MS\": 1000,\n \"HEARTBEAT_MAX_INTERVAL_MS\": 900000,\n \"MAX_CLAIM_PER_POLL\": 5,\n \"MAX_LOG_MESSAGE_CHARS\": 10000,\n \"MAX_LOGS_PER_PATCH\": 100,\n \"MAX_ERROR_MESSAGE_CHARS\": 10000,\n \"MAX_ERROR_STACK_CHARS\": 20000,\n \"MAX_ITEMS_PER_RUN\": 10000,\n \"MAX_ITEMS_PER_REQUEST\": 500,\n \"MAX_ITEM_MESSAGE_CHARS\": 2000,\n \"MAX_ITEM_PAYLOAD_BYTES\": 16384,\n \"MAX_ITEM_COLUMNS\": 12,\n \"MAX_ITEM_COLUMN_LABEL_CHARS\": 60,\n \"MAX_ATTACHMENT_BYTES\": 5242880,\n \"MAX_ATTACHMENTS_PER_RUN\": 20,\n \"MAX_ATTACHMENTS_PER_LOG_ENTRY\": 4,\n \"MAX_CAPTION_CHARS\": 200,\n \"MAX_IMAGE_DIMENSION_PX\": 20000\n}\n","import limits from '../protocol-limits.json';\n\n/**\n * Limites do wire SDK ↔ servidor — fonte ÚNICA (protocol-limits.json): o SDK\n * Node lê daqui, a @hobots/api importa de `hobots/protocol` e o SDK Python\n * confere a paridade num teste que lê o mesmo JSON. Mudar um limite = mudar o\n * JSON (e, no Python, a constante espelhada — o teste acusa).\n */\nexport const WIRE_LIMITS = limits as Readonly<typeof limits>;\nexport type WireLimits = typeof WIRE_LIMITS;\n"],"mappings":";AAAA;AAAA,EACE,wBAA0B;AAAA,EAC1B,+BAAiC;AAAA,EACjC,2BAA6B;AAAA,EAC7B,2BAA6B;AAAA,EAC7B,oBAAsB;AAAA,EACtB,uBAAyB;AAAA,EACzB,oBAAsB;AAAA,EACtB,yBAA2B;AAAA,EAC3B,uBAAyB;AAAA,EACzB,mBAAqB;AAAA,EACrB,uBAAyB;AAAA,EACzB,wBAA0B;AAAA,EAC1B,wBAA0B;AAAA,EAC1B,kBAAoB;AAAA,EACpB,6BAA+B;AAAA,EAC/B,sBAAwB;AAAA,EACxB,yBAA2B;AAAA,EAC3B,+BAAiC;AAAA,EACjC,mBAAqB;AAAA,EACrB,wBAA0B;AAC5B;;;ACbO,IAAM,cAAc;","names":[]}
package/dist/index.cjs CHANGED
@@ -95,7 +95,7 @@ function scrubSecrets(text) {
95
95
 
96
96
  // src/utils.ts
97
97
  var SDK_NAME = "hobots";
98
- var SDK_VERSION = true ? "3.2.2" : "0.0.0-dev";
98
+ var SDK_VERSION = true ? "3.3.0" : "0.0.0-dev";
99
99
  function isNode() {
100
100
  return typeof process !== "undefined" && typeof process.versions?.node === "string";
101
101
  }
@@ -269,12 +269,39 @@ function eventFromException(error, type = "error") {
269
269
  };
270
270
  }
271
271
 
272
+ // protocol-limits.json
273
+ var protocol_limits_default = {
274
+ MAX_ITEMS_PER_ENVELOPE: 50,
275
+ HEARTBEAT_DEFAULT_INTERVAL_MS: 3e4,
276
+ HEARTBEAT_MIN_INTERVAL_MS: 1e3,
277
+ HEARTBEAT_MAX_INTERVAL_MS: 9e5,
278
+ MAX_CLAIM_PER_POLL: 5,
279
+ MAX_LOG_MESSAGE_CHARS: 1e4,
280
+ MAX_LOGS_PER_PATCH: 100,
281
+ MAX_ERROR_MESSAGE_CHARS: 1e4,
282
+ MAX_ERROR_STACK_CHARS: 2e4,
283
+ MAX_ITEMS_PER_RUN: 1e4,
284
+ MAX_ITEMS_PER_REQUEST: 500,
285
+ MAX_ITEM_MESSAGE_CHARS: 2e3,
286
+ MAX_ITEM_PAYLOAD_BYTES: 16384,
287
+ MAX_ITEM_COLUMNS: 12,
288
+ MAX_ITEM_COLUMN_LABEL_CHARS: 60,
289
+ MAX_ATTACHMENT_BYTES: 5242880,
290
+ MAX_ATTACHMENTS_PER_RUN: 20,
291
+ MAX_ATTACHMENTS_PER_LOG_ENTRY: 4,
292
+ MAX_CAPTION_CHARS: 200,
293
+ MAX_IMAGE_DIMENSION_PX: 2e4
294
+ };
295
+
296
+ // src/wire-limits.ts
297
+ var WIRE_LIMITS = protocol_limits_default;
298
+
272
299
  // src/transport.ts
273
300
  var REQUEST_TIMEOUT_MS = 1e4;
274
301
  var DEFAULT_FLUSH_INTERVAL_MS = 2e3;
275
302
  var DEFAULT_MAX_BATCH_SIZE = 10;
276
303
  var DEFAULT_MAX_QUEUE_SIZE = 200;
277
- var MAX_ITEMS_PER_ENVELOPE = 50;
304
+ var MAX_ITEMS_PER_ENVELOPE = WIRE_LIMITS.MAX_ITEMS_PER_ENVELOPE;
278
305
  var Transport = class {
279
306
  endpoint;
280
307
  key;
@@ -598,9 +625,9 @@ async function fallbackInstanceId() {
598
625
  }
599
626
 
600
627
  // src/heartbeat.ts
601
- var DEFAULT_INTERVAL_MS = 3e4;
602
- var MIN_REPORTED_INTERVAL_MS = 1e3;
603
- var MAX_REPORTED_INTERVAL_MS = 9e5;
628
+ var DEFAULT_INTERVAL_MS = WIRE_LIMITS.HEARTBEAT_DEFAULT_INTERVAL_MS;
629
+ var MIN_REPORTED_INTERVAL_MS = WIRE_LIMITS.HEARTBEAT_MIN_INTERVAL_MS;
630
+ var MAX_REPORTED_INTERVAL_MS = WIRE_LIMITS.HEARTBEAT_MAX_INTERVAL_MS;
604
631
  var Heartbeat = class {
605
632
  constructor(client, options = {}, instanceId, sampler) {
606
633
  this.client = client;
@@ -1160,6 +1187,88 @@ function isTerminalError(error) {
1160
1187
  return error instanceof Error && TERMINAL_ERROR_NAMES.has(error.name);
1161
1188
  }
1162
1189
 
1190
+ // src/tasks/each.ts
1191
+ function thrownMessage(error) {
1192
+ if (error instanceof Error) return error.message || error.name;
1193
+ return safeString(error);
1194
+ }
1195
+ var ItemHandle = class {
1196
+ constructor(index, id, step, basePayload, sink) {
1197
+ this.index = index;
1198
+ this.id = id;
1199
+ this.step = step;
1200
+ this.basePayload = basePayload;
1201
+ this.sink = sink;
1202
+ }
1203
+ index;
1204
+ id;
1205
+ step;
1206
+ basePayload;
1207
+ sink;
1208
+ reported = false;
1209
+ status;
1210
+ succeeded(payload) {
1211
+ this.record("succeeded", void 0, payload);
1212
+ }
1213
+ failed(message, payload) {
1214
+ this.record("failed", message, payload);
1215
+ if (this.step && this.step.status !== "error") this.step.setStatus("error", message);
1216
+ }
1217
+ occurrence(message, payload) {
1218
+ this.record("occurrence", message, payload);
1219
+ if (this.step && this.step.status === "ok") this.step.setStatus("warning", message);
1220
+ }
1221
+ record(status, message, payload) {
1222
+ this.reported = true;
1223
+ this.status = status;
1224
+ const merged = { ...this.basePayload ?? {}, ...payload ?? {} };
1225
+ this.sink.report(status, message, Object.keys(merged).length > 0 ? merged : void 0, this.id);
1226
+ }
1227
+ };
1228
+ function resolveId(options, element, index) {
1229
+ const raw = options.id?.(element, index);
1230
+ if (raw === null || raw === void 0) return void 0;
1231
+ const text = safeString(raw);
1232
+ return text.length > 0 ? text : void 0;
1233
+ }
1234
+ function knownLength(list) {
1235
+ const length = list?.length;
1236
+ return typeof length === "number" && Number.isInteger(length) && length >= 1 ? length : void 0;
1237
+ }
1238
+ async function eachItem(list, handler, options, sink) {
1239
+ const result = { total: 0, succeeded: 0, failed: 0, occurrence: 0 };
1240
+ const length = options.progress === false ? void 0 : knownLength(list);
1241
+ if (length !== void 0) sink.declareTotal(length);
1242
+ let index = 0;
1243
+ for await (const element of list) {
1244
+ sink.checkCanceled();
1245
+ const id = resolveId(options, element, index);
1246
+ const description = options.description?.(element, index) ?? id ?? `#${index + 1}`;
1247
+ const step = sink.startStep(options.op ?? "item", description);
1248
+ const handle = new ItemHandle(index, id, step, options.payload?.(element, index), sink);
1249
+ try {
1250
+ await handler(element, handle, index);
1251
+ if (!handle.reported) handle.succeeded();
1252
+ } catch (error) {
1253
+ if (isTerminalError(error) || sink.isAborted()) {
1254
+ step?.setStatus("cancelled");
1255
+ step?.finish();
1256
+ throw error;
1257
+ }
1258
+ const message = thrownMessage(error);
1259
+ if (!handle.reported) handle.failed(message);
1260
+ step?.setError(error);
1261
+ sink.logError(`item ${description} falhou: ${message}`);
1262
+ }
1263
+ step?.finish();
1264
+ if (options.progress !== false) sink.advance();
1265
+ result.total += 1;
1266
+ result[handle.status ?? "succeeded"] += 1;
1267
+ index += 1;
1268
+ }
1269
+ return result;
1270
+ }
1271
+
1163
1272
  // src/retry.ts
1164
1273
  var DEFAULT_RETRIES = 3;
1165
1274
  var DEFAULT_DELAY_MS = 1e3;
@@ -1187,10 +1296,11 @@ async function retryWithBackoff(fn, options = {}) {
1187
1296
  }
1188
1297
 
1189
1298
  // src/tasks/attachments.ts
1190
- var MAX_ATTACHMENT_BYTES = 5242880;
1191
- var MAX_ATTACHMENTS_PER_RUN = 20;
1299
+ var MAX_ATTACHMENT_BYTES = WIRE_LIMITS.MAX_ATTACHMENT_BYTES;
1300
+ var MAX_ATTACHMENTS_PER_RUN = WIRE_LIMITS.MAX_ATTACHMENTS_PER_RUN;
1301
+ var MAX_ATTACHMENTS_PER_LOG_ENTRY = WIRE_LIMITS.MAX_ATTACHMENTS_PER_LOG_ENTRY;
1192
1302
  var MAX_CONCURRENT_UPLOADS = 3;
1193
- var MAX_CAPTION_CHARS = 200;
1303
+ var MAX_CAPTION_CHARS = WIRE_LIMITS.MAX_CAPTION_CHARS;
1194
1304
  var ATTACHMENT_DRAIN_MS = 15e3;
1195
1305
  async function readImageFile(path) {
1196
1306
  if (!isNode()) {
@@ -1204,7 +1314,7 @@ function startsWith(bytes, signature, offset = 0) {
1204
1314
  return signature.every((byte, index) => bytes[offset + index] === byte);
1205
1315
  }
1206
1316
  var PNG_MAGIC = [137, 80, 78, 71, 13, 10, 26, 10];
1207
- var MAX_PNG_DIMENSION = 2e4;
1317
+ var MAX_PNG_DIMENSION = WIRE_LIMITS.MAX_IMAGE_DIMENSION_PX;
1208
1318
  var JPEG_MAGIC = [255, 216, 255];
1209
1319
  var RIFF_MAGIC = [82, 73, 70, 70];
1210
1320
  var WEBP_MAGIC = [87, 69, 66, 80];
@@ -1267,13 +1377,13 @@ var DEFAULT_LOG_FLUSH_INTERVAL_MS = 2e3;
1267
1377
  var DEFAULT_CANCEL_CHECK_INTERVAL_MS = 5e3;
1268
1378
  var MIN_CANCEL_CHECK_INTERVAL_MS = 1e3;
1269
1379
  var MAX_CANCEL_CHECK_INTERVAL_MS = 15e3;
1270
- var MAX_LOG_MESSAGE_CHARS = 1e4;
1271
- var MAX_LOGS_PER_PATCH = 100;
1380
+ var MAX_LOG_MESSAGE_CHARS = WIRE_LIMITS.MAX_LOG_MESSAGE_CHARS;
1381
+ var MAX_LOGS_PER_PATCH = WIRE_LIMITS.MAX_LOGS_PER_PATCH;
1272
1382
  var FLUSH_AT_BUFFERED = 20;
1273
- var MAX_ITEMS_PER_RUN = 1e4;
1274
- var MAX_ITEMS_PER_REQUEST = 500;
1275
- var MAX_ITEM_MESSAGE_CHARS = 2e3;
1276
- var MAX_ITEM_PAYLOAD_BYTES = 16384;
1383
+ var MAX_ITEMS_PER_RUN = WIRE_LIMITS.MAX_ITEMS_PER_RUN;
1384
+ var MAX_ITEMS_PER_REQUEST = WIRE_LIMITS.MAX_ITEMS_PER_REQUEST;
1385
+ var MAX_ITEM_MESSAGE_CHARS = WIRE_LIMITS.MAX_ITEM_MESSAGE_CHARS;
1386
+ var MAX_ITEM_PAYLOAD_BYTES = WIRE_LIMITS.MAX_ITEM_PAYLOAD_BYTES;
1277
1387
  var ITEMS_FLUSH_AT_BUFFERED = 100;
1278
1388
  var ITEMS_DRAIN_MS = 15e3;
1279
1389
  var PROGRESS_FLUSH_MS = 1e3;
@@ -1307,6 +1417,17 @@ var RunReporter = class {
1307
1417
  tx.setLifecycleSink((level, message) => this.push(level, message));
1308
1418
  const checkCanceled = () => this.checkCanceled();
1309
1419
  tx.setStepGuard(checkCanceled);
1420
+ const eachSink = {
1421
+ report: (status, message, payload, id) => this.addItem(status, message, payload, id),
1422
+ startStep: (op, description) => tx.startChild(op, description),
1423
+ declareTotal: (total) => {
1424
+ if (this.progressTotal == null) this.setProgress(this.progressCurrent, total);
1425
+ },
1426
+ advance: () => this.setProgress(this.progressCurrent + 1, this.progressTotal),
1427
+ checkCanceled,
1428
+ isAborted: () => this.abortController.signal.aborted,
1429
+ logError: (message) => this.log("error", message)
1430
+ };
1310
1431
  const items = {
1311
1432
  add: (item) => {
1312
1433
  checkCanceled();
@@ -1323,7 +1444,8 @@ var RunReporter = class {
1323
1444
  occurrence: (message, payload, options) => {
1324
1445
  checkCanceled();
1325
1446
  this.addItem("occurrence", message, payload, options?.id);
1326
- }
1447
+ },
1448
+ each: (list, handler, options = {}) => eachItem(list, handler, options, eachSink)
1327
1449
  };
1328
1450
  const progress = Object.defineProperties(
1329
1451
  {
@@ -1468,7 +1590,7 @@ var RunReporter = class {
1468
1590
  ts,
1469
1591
  level,
1470
1592
  message: truncate(scrubbed, MAX_LOG_MESSAGE_CHARS),
1471
- ...attachments && attachments.length > 0 ? { attachments } : {}
1593
+ ...attachments && attachments.length > 0 ? { attachments: attachments.slice(0, WIRE_LIMITS.MAX_ATTACHMENTS_PER_LOG_ENTRY) } : {}
1472
1594
  });
1473
1595
  if (this.buffer.length >= FLUSH_AT_BUFFERED) {
1474
1596
  this.flushLogs();
@@ -2169,6 +2291,7 @@ async function executeRun(input) {
2169
2291
  await runWithRunContext({ reporter }, () => invokeWithRetry(task, run, reporter, logger));
2170
2292
  await reporter.finalizeAttachments();
2171
2293
  await reporter.finalizeItems();
2294
+ warnIncompleteProgress(reporter);
2172
2295
  tx.setStatus("ok");
2173
2296
  tx.finish();
2174
2297
  reporter.reportSuccess();
@@ -2186,6 +2309,13 @@ async function executeRun(input) {
2186
2309
  tracker.remove(reporter);
2187
2310
  }
2188
2311
  }
2312
+ function warnIncompleteProgress(reporter) {
2313
+ const snapshot = reporter.context.progress.snapshot;
2314
+ if (!snapshot || snapshot.current >= snapshot.total) return;
2315
+ reporter.context.log.warn(
2316
+ `execu\xE7\xE3o conclu\xEDda com progresso incompleto: ${snapshot.current}/${snapshot.total} \u2014 o handler terminou sem processar (ou sem avan\xE7ar) todos os itens previstos`
2317
+ );
2318
+ }
2189
2319
  function invokeWithRetry(task, run, reporter, logger) {
2190
2320
  const { retries, retryDelayMs } = task.options;
2191
2321
  const total = retries + 1;
@@ -2240,8 +2370,8 @@ function waitForCancelGrace(signal, graceMs) {
2240
2370
  signal.addEventListener("abort", () => setTimeout(resolve2, graceMs), { once: true });
2241
2371
  });
2242
2372
  }
2243
- var MAX_ERROR_MESSAGE_CHARS = 1e4;
2244
- var MAX_ERROR_STACK_CHARS = 2e4;
2373
+ var MAX_ERROR_MESSAGE_CHARS = WIRE_LIMITS.MAX_ERROR_MESSAGE_CHARS;
2374
+ var MAX_ERROR_STACK_CHARS = WIRE_LIMITS.MAX_ERROR_STACK_CHARS;
2245
2375
  function toRunError(error) {
2246
2376
  if (error instanceof Error) {
2247
2377
  return {
@@ -2256,7 +2386,7 @@ function toRunError(error) {
2256
2386
  // src/tasks/agent.ts
2257
2387
  var DEFAULT_POLL_INTERVAL_MS = 2e3;
2258
2388
  var MIN_POLL_INTERVAL_MS = 1e3;
2259
- var MAX_CLAIM_PER_POLL = 5;
2389
+ var MAX_CLAIM_PER_POLL = WIRE_LIMITS.MAX_CLAIM_PER_POLL;
2260
2390
  var TaskAgent = class {
2261
2391
  constructor(options) {
2262
2392
  this.options = options;
@@ -2391,8 +2521,8 @@ var TaskAgent = class {
2391
2521
 
2392
2522
  // src/tasks/item-columns.ts
2393
2523
  var KEY_RE = /^[a-zA-Z0-9_.-]{1,100}$/;
2394
- var MAX_COLUMNS = 12;
2395
- var MAX_LABEL_CHARS = 60;
2524
+ var MAX_COLUMNS = WIRE_LIMITS.MAX_ITEM_COLUMNS;
2525
+ var MAX_LABEL_CHARS = WIRE_LIMITS.MAX_ITEM_COLUMN_LABEL_CHARS;
2396
2526
  function normalizeItemColumns(slug, input, logger) {
2397
2527
  if (input === void 0 || input === null) return void 0;
2398
2528
  const columns = input.columns;
@@ -2900,7 +3030,22 @@ var fallbackLog = {
2900
3030
  success: (message, ...rest) => printRunLog(FALLBACK_TASK, "success", joinLogArgs(message, rest))
2901
3031
  };
2902
3032
  var noop = () => void 0;
2903
- var fallbackItems = { add: noop, succeeded: noop, failed: noop, occurrence: noop };
3033
+ var fallbackEachSink = {
3034
+ report: noop,
3035
+ startStep: () => void 0,
3036
+ declareTotal: noop,
3037
+ advance: noop,
3038
+ checkCanceled: noop,
3039
+ isAborted: () => false,
3040
+ logError: (message) => printRunLog(FALLBACK_TASK, "error", message)
3041
+ };
3042
+ var fallbackItems = {
3043
+ add: noop,
3044
+ succeeded: noop,
3045
+ failed: noop,
3046
+ occurrence: noop,
3047
+ each: (list, handler, options = {}) => eachItem(list, handler, options, fallbackEachSink)
3048
+ };
2904
3049
  var fallbackProgress = { total: noop, advance: noop, set: noop, current: 0, snapshot: null };
2905
3050
  var neverAborts = new AbortController().signal;
2906
3051
  var detachedTransaction;