@hue-run/sdk 0.22.0 → 0.23.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/CLI.md +10 -10
- package/README.md +17 -3
- package/dist/transport.d.ts +12 -0
- package/dist/transport.js +139 -24
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -1
package/CLI.md
CHANGED
|
@@ -42,7 +42,7 @@ project manifest are refused because managers can update ancestor locks; Python
|
|
|
42
42
|
The generated `hue.setup.mjs` or `hue_setup.py` always selects `captureContent: false` /
|
|
43
43
|
`capture_content=False`. For a supported application, setup installs the dependency and adds the
|
|
44
44
|
managed import and middleware registration to the existing entrypoint; an unreferenced helper is
|
|
45
|
-
not a completed integration. TypeScript uses `@hue-run/sdk@0.
|
|
45
|
+
not a completed integration. TypeScript uses `@hue-run/sdk@0.23.0`, `@opentelemetry/api@1.9.1` and
|
|
46
46
|
`@opentelemetry/context-async-hooks@2.11.0`; Python setup uses its separately tested package pin.
|
|
47
47
|
Content capture requires an ordinary account-managed key and a later explicit application decision.
|
|
48
48
|
|
|
@@ -381,8 +381,8 @@ and Hue never receives or stores them. The command is part of the `hue` executab
|
|
|
381
381
|
# LANGFUSE_PUBLIC_KEY=pk-lf-...
|
|
382
382
|
# LANGFUSE_SECRET_KEY=sk-lf-...
|
|
383
383
|
# LANGFUSE_BASE_URL=https://cloud.langfuse.com
|
|
384
|
-
npx -y @hue-run/sdk@0.
|
|
385
|
-
npx -y @hue-run/sdk@0.
|
|
384
|
+
npx -y @hue-run/sdk@0.23.0 import langfuse --env-path .env.langfuse --from 2026-01-01 --dry-run
|
|
385
|
+
npx -y @hue-run/sdk@0.23.0 import langfuse --env-path .env.langfuse --from 2026-01-01 --media
|
|
386
386
|
```
|
|
387
387
|
|
|
388
388
|
`LANGFUSE_PUBLIC_KEY` and `LANGFUSE_SECRET_KEY` come from the environment or `--env-path`
|
|
@@ -892,7 +892,7 @@ node packages/sdk-typescript/scripts/verify-package.mjs --artifacts-dir .artifac
|
|
|
892
892
|
# Set project to an existing supported fixture; use the same directory on resume.
|
|
893
893
|
project=/absolute/path/to/supported-fixture
|
|
894
894
|
node packages/sdk-typescript/scripts/verify-setup-live.mjs \
|
|
895
|
-
--archive .artifacts/typescript/hue-run-sdk-0.
|
|
895
|
+
--archive .artifacts/typescript/hue-run-sdk-0.23.0.tgz \
|
|
896
896
|
--origin https://STAGING_ORIGIN \
|
|
897
897
|
--project "$project" --command setup \
|
|
898
898
|
--evidence .context/setup-staging-before-claim.json
|
|
@@ -903,7 +903,7 @@ the private local handoff and finish the real browser claim, then reconcile the
|
|
|
903
903
|
|
|
904
904
|
```sh
|
|
905
905
|
node packages/sdk-typescript/scripts/verify-setup-live.mjs \
|
|
906
|
-
--archive .artifacts/typescript/hue-run-sdk-0.
|
|
906
|
+
--archive .artifacts/typescript/hue-run-sdk-0.23.0.tgz \
|
|
907
907
|
--origin https://STAGING_ORIGIN \
|
|
908
908
|
--project "$project" --command claim \
|
|
909
909
|
--evidence .context/setup-staging-after-claim.json
|
|
@@ -945,10 +945,10 @@ otherwise. Never hand the agent Hue-native tools (`context.tools`, `context.mcp`
|
|
|
945
945
|
production agent lacks. Pin the SDK, so the agent's repository gains no dependency:
|
|
946
946
|
|
|
947
947
|
```sh
|
|
948
|
-
npx -y @hue-run/sdk@0.
|
|
949
|
-
npx -y @hue-run/sdk@0.
|
|
950
|
-
npx -y @hue-run/sdk@0.
|
|
951
|
-
npx -y @hue-run/sdk@0.
|
|
948
|
+
npx -y @hue-run/sdk@0.23.0 eval --case "Refund an eligible charge" --command "node agent.js"
|
|
949
|
+
npx -y @hue-run/sdk@0.23.0 eval --case https://app.hue.run/projects/demo/scenarios/<id> --command "python agent.py" --timeout 120 --baseline <run id>
|
|
950
|
+
npx -y @hue-run/sdk@0.23.0 eval --set "Billing regressions" --scorer-version <id> --command "node agent.js" --save-version
|
|
951
|
+
npx -y @hue-run/sdk@0.23.0 eval --worker --command "node agent.js" --agent-key support-agent --revision <new-agent-revision>
|
|
952
952
|
```
|
|
953
953
|
|
|
954
954
|
One invocation creates one run holding every selected case; it prints the run URL
|
|
@@ -986,7 +986,7 @@ export default function runMyAgent(inputs: JsonValue, context: SimulationTargetC
|
|
|
986
986
|
```
|
|
987
987
|
|
|
988
988
|
```sh
|
|
989
|
-
npx -y @hue-run/sdk@0.
|
|
989
|
+
npx -y @hue-run/sdk@0.23.0 eval --case "Refund an eligible charge" ./hue-agent.ts --content
|
|
990
990
|
```
|
|
991
991
|
|
|
992
992
|
`--scenario` remains an alias for `--case` for existing scripts. Pass one selection flag.
|
package/README.md
CHANGED
|
@@ -552,8 +552,8 @@ the Langfuse project's own keys (`LANGFUSE_PUBLIC_KEY`, `LANGFUSE_SECRET_KEY`, a
|
|
|
552
552
|
`HUE_API_KEY`:
|
|
553
553
|
|
|
554
554
|
```sh
|
|
555
|
-
npx -y @hue-run/sdk@0.
|
|
556
|
-
npx -y @hue-run/sdk@0.
|
|
555
|
+
npx -y @hue-run/sdk@0.23.0 import langfuse --env-path .env.langfuse --from 2026-01-01 --dry-run
|
|
556
|
+
npx -y @hue-run/sdk@0.23.0 import langfuse --env-path .env.langfuse --from 2026-01-01 --media
|
|
557
557
|
```
|
|
558
558
|
|
|
559
559
|
- It reads Langfuse Cloud and self-hosted v4 through the Observations API v2, and self-hosted v3
|
|
@@ -1106,6 +1106,16 @@ own. Stop request production
|
|
|
1106
1106
|
before shutdown so late spans cannot race it. A client does not own instrumented
|
|
1107
1107
|
operations still running in the application.
|
|
1108
1108
|
|
|
1109
|
+
In Vercel Functions, the TypeScript SDK automatically registers admitted spans and logs with
|
|
1110
|
+
[`waitUntil`](https://vercel.com/docs/functions/functions-api-reference/vercel-functions-package#waituntil).
|
|
1111
|
+
The response or Workflow step can return while the existing batches and their media uploads finish
|
|
1112
|
+
in the background, within the function's lifetime limit. You can remove a per-step
|
|
1113
|
+
`await hue.flushSafe()` when its purpose is to prevent the function freezing before delivery.
|
|
1114
|
+
Keep an explicit flush when you need its acknowledgement report before continuing. Background
|
|
1115
|
+
delivery preserves capture, redaction, batching and failure reports; it never flushes or shuts down
|
|
1116
|
+
application-owned providers. In other hosts, keep the host's lifetime hook or await a flush before
|
|
1117
|
+
a short-lived process exits.
|
|
1118
|
+
|
|
1109
1119
|
## Live spans
|
|
1110
1120
|
|
|
1111
1121
|
OpenTelemetry exports a span only when it ends, so a long streamed turn would otherwise stay
|
|
@@ -1119,7 +1129,11 @@ arrives.
|
|
|
1119
1129
|
The placeholder then waits for the next batch export like any queued span: up to 500 ms until the
|
|
1120
1130
|
next tick, then the 1 s batch delay when nothing else is queued, so it usually reaches Hue within
|
|
1121
1131
|
about 1.5 s of its span starting. It goes out sooner when a batch is already scheduled, for example
|
|
1122
|
-
because another span has just ended, and at once for a full batch or
|
|
1132
|
+
because another span has just ended, and at once for a full batch or a flush with no completed
|
|
1133
|
+
spans. When a flush has completed spans to deliver, it defers queued placeholders to the next
|
|
1134
|
+
normal announcement after the drain and its uploads finish. This avoids an extra acknowledgement
|
|
1135
|
+
for a still-open parent while preserving delivery of its completed children. A parent that ends
|
|
1136
|
+
before that announcement sends only its completed span. A placeholder goes out later
|
|
1123
1137
|
while an earlier export is still in flight, because the next batch is scheduled only after that
|
|
1124
1138
|
export finishes. A placeholder whose span has ended by the time it is exported is not sent, so a
|
|
1125
1139
|
short span may send none and appear in Hue only when it finishes.
|
package/dist/transport.d.ts
CHANGED
|
@@ -53,6 +53,9 @@ export declare class HueTransport {
|
|
|
53
53
|
private failed;
|
|
54
54
|
private spans;
|
|
55
55
|
private logs;
|
|
56
|
+
private deliveryCohort?;
|
|
57
|
+
private deliveries;
|
|
58
|
+
private deliveryUploads;
|
|
56
59
|
private pendingBytes;
|
|
57
60
|
private dropped;
|
|
58
61
|
/** Records of each trace the SDK never sent (dropped from the queue, refused as invalid or
|
|
@@ -81,6 +84,8 @@ export declare class HueTransport {
|
|
|
81
84
|
private placeholdersRejected;
|
|
82
85
|
private placeholders;
|
|
83
86
|
private placeholderSources;
|
|
87
|
+
private forcedSpanDrains;
|
|
88
|
+
private completedSpanDrains;
|
|
84
89
|
private batchSpans?;
|
|
85
90
|
private limits;
|
|
86
91
|
/** The rate-limit holds in progress, each ended early by calling it. */
|
|
@@ -120,11 +125,16 @@ export declare class HueTransport {
|
|
|
120
125
|
* skipped silently.
|
|
121
126
|
*/
|
|
122
127
|
private enqueue;
|
|
128
|
+
/** Register in the caller's request before it can return; outside Vercel this is a no-op. */
|
|
129
|
+
private protectDelivery;
|
|
130
|
+
private holdDelivery;
|
|
131
|
+
private finishDelivery;
|
|
123
132
|
private track;
|
|
124
133
|
/** Placeholders are built lazily, so input set right after a span starts is included. */
|
|
125
134
|
private announceLiveSpans;
|
|
126
135
|
private stopLiveTimer;
|
|
127
136
|
private stopLiveSpans;
|
|
137
|
+
private beginSpanDrain;
|
|
128
138
|
/** Holds the staged attachments of a log record being admitted with the queued record, within
|
|
129
139
|
* the held-value budget; those the budget refuses are counted as not uploaded. */
|
|
130
140
|
private admitLogParts;
|
|
@@ -144,6 +154,8 @@ export declare class HueTransport {
|
|
|
144
154
|
private protectMedia;
|
|
145
155
|
private releaseMediaIdentity;
|
|
146
156
|
private sweepMedia;
|
|
157
|
+
/** Keep an export's warning aggregation within each cohort's upload ownership. */
|
|
158
|
+
private collectDeliveryUploads;
|
|
147
159
|
/** Bounded: a trace whose root never arrives (an abandoned run) gives way to newer ones. */
|
|
148
160
|
private countDropped;
|
|
149
161
|
/** Cumulative counters and current queue gauges. */
|
package/dist/transport.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
2
|
import { gzip } from "node:zlib";
|
|
3
|
+
import { waitUntil } from "@vercel/functions";
|
|
3
4
|
import { ExportResultCode } from "@opentelemetry/core";
|
|
4
5
|
import { createOtlpNetworkExportDelegate, OTLPExporterBase, OTLPExporterError, } from "@opentelemetry/otlp-exporter-base";
|
|
5
6
|
import { createOtlpHttpExporterMetrics } from "@opentelemetry/otlp-exporter-base/node-http";
|
|
@@ -182,6 +183,10 @@ export class HueTransport {
|
|
|
182
183
|
failed = { traces: 0, logs: 0 };
|
|
183
184
|
spans = new Map();
|
|
184
185
|
logs = new Map();
|
|
186
|
+
// A cohort closes when export starts, so later requests cannot extend its lifetime.
|
|
187
|
+
deliveryCohort;
|
|
188
|
+
deliveries = new WeakMap();
|
|
189
|
+
deliveryUploads = new WeakMap();
|
|
185
190
|
pendingBytes = 0;
|
|
186
191
|
dropped = { traces: 0, logs: 0 };
|
|
187
192
|
/** Records of each trace the SDK never sent (dropped from the queue, refused as invalid or
|
|
@@ -214,6 +219,8 @@ export class HueTransport {
|
|
|
214
219
|
placeholders = new WeakMap();
|
|
215
220
|
// The running span each admitted placeholder announces, so one it has outlived is not sent.
|
|
216
221
|
placeholderSources = new WeakMap();
|
|
222
|
+
forcedSpanDrains = 0;
|
|
223
|
+
completedSpanDrains = 0;
|
|
217
224
|
batchSpans;
|
|
218
225
|
// What the receiver accepts, as it last advertised; the limits of this release until it does.
|
|
219
226
|
limits = DEFAULT_RECEIVER_LIMITS;
|
|
@@ -340,6 +347,7 @@ export class HueTransport {
|
|
|
340
347
|
this.protectMedia(admitted);
|
|
341
348
|
this.countEnded(admitted);
|
|
342
349
|
spans.onEnd(admitted);
|
|
350
|
+
this.protectDelivery(admitted);
|
|
343
351
|
}
|
|
344
352
|
catch {
|
|
345
353
|
if (admitted)
|
|
@@ -352,12 +360,18 @@ export class HueTransport {
|
|
|
352
360
|
// The spans are sent first, then the uploads queued so far are waited for, unless a
|
|
353
361
|
// flush sends them: that flush waits for the uploads after its send.
|
|
354
362
|
forceFlush: async () => {
|
|
363
|
+
const finish = this.beginSpanDrain();
|
|
355
364
|
try {
|
|
356
365
|
await spans.forceFlush();
|
|
357
366
|
}
|
|
358
367
|
finally {
|
|
359
|
-
|
|
360
|
-
|
|
368
|
+
try {
|
|
369
|
+
if (!this.sendingRecords.getStore())
|
|
370
|
+
await this.drainUploads();
|
|
371
|
+
}
|
|
372
|
+
finally {
|
|
373
|
+
finish();
|
|
374
|
+
}
|
|
361
375
|
}
|
|
362
376
|
},
|
|
363
377
|
shutdown: async () => {
|
|
@@ -391,6 +405,7 @@ export class HueTransport {
|
|
|
391
405
|
admitted = queued;
|
|
392
406
|
this.admitLogParts(log, queued);
|
|
393
407
|
logs.onEmit(queued);
|
|
408
|
+
this.protectDelivery(admitted);
|
|
394
409
|
}
|
|
395
410
|
catch {
|
|
396
411
|
if (admitted)
|
|
@@ -399,12 +414,18 @@ export class HueTransport {
|
|
|
399
414
|
}
|
|
400
415
|
},
|
|
401
416
|
forceFlush: async () => {
|
|
417
|
+
const finish = this.beginSpanDrain();
|
|
402
418
|
try {
|
|
403
419
|
await logs.forceFlush();
|
|
404
420
|
}
|
|
405
421
|
finally {
|
|
406
|
-
|
|
407
|
-
|
|
422
|
+
try {
|
|
423
|
+
if (!this.sendingRecords.getStore())
|
|
424
|
+
await this.drainUploads();
|
|
425
|
+
}
|
|
426
|
+
finally {
|
|
427
|
+
finish();
|
|
428
|
+
}
|
|
408
429
|
}
|
|
409
430
|
},
|
|
410
431
|
shutdown: async () => {
|
|
@@ -472,6 +493,14 @@ export class HueTransport {
|
|
|
472
493
|
this.logs.set(snapshot.record, snapshot.bytes);
|
|
473
494
|
if (snapshot.unresolvedResource && !advisory)
|
|
474
495
|
this.issue(signal, "warning", 0, "Unresolved resource attributes omitted from the telemetry snapshot");
|
|
496
|
+
if (!advisory) {
|
|
497
|
+
const cohort = (this.deliveryCohort ??= {
|
|
498
|
+
pending: 0,
|
|
499
|
+
...Promise.withResolvers(),
|
|
500
|
+
});
|
|
501
|
+
cohort.pending++;
|
|
502
|
+
this.deliveries.set(snapshot.record, cohort);
|
|
503
|
+
}
|
|
475
504
|
return snapshot.record;
|
|
476
505
|
}
|
|
477
506
|
catch {
|
|
@@ -481,11 +510,42 @@ export class HueTransport {
|
|
|
481
510
|
return undefined;
|
|
482
511
|
}
|
|
483
512
|
}
|
|
513
|
+
/** Register in the caller's request before it can return; outside Vercel this is a no-op. */
|
|
514
|
+
protectDelivery(record) {
|
|
515
|
+
const cohort = this.deliveries.get(record);
|
|
516
|
+
if (!cohort)
|
|
517
|
+
return;
|
|
518
|
+
try {
|
|
519
|
+
waitUntil(cohort.promise);
|
|
520
|
+
}
|
|
521
|
+
catch {
|
|
522
|
+
// A host hook must never undo admission or change the application's result.
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
/** @internal Seal the batch's cohort before export can yield to another request. */
|
|
526
|
+
exporting(records) {
|
|
527
|
+
if (records.some((record) => this.deliveries.get(record) === this.deliveryCohort))
|
|
528
|
+
this.deliveryCohort = undefined;
|
|
529
|
+
}
|
|
530
|
+
holdDelivery(cohort, work) {
|
|
531
|
+
if (!cohort)
|
|
532
|
+
return;
|
|
533
|
+
cohort.pending++;
|
|
534
|
+
const finish = () => this.finishDelivery(cohort);
|
|
535
|
+
void work.then(finish, finish);
|
|
536
|
+
}
|
|
537
|
+
finishDelivery(cohort) {
|
|
538
|
+
if (--cohort.pending !== 0)
|
|
539
|
+
return;
|
|
540
|
+
if (this.deliveryCohort === cohort)
|
|
541
|
+
this.deliveryCohort = undefined;
|
|
542
|
+
cohort.resolve();
|
|
543
|
+
}
|
|
484
544
|
track(span) {
|
|
485
545
|
try {
|
|
486
546
|
if (!this.liveSpans || this.live.size >= MAX_LIVE_SPANS)
|
|
487
547
|
return;
|
|
488
|
-
if (!(span.spanContext().traceFlags & 1) ||
|
|
548
|
+
if (!(span.spanContext().traceFlags & 1) || span.ended || !announcesLiveSpan(span))
|
|
489
549
|
return;
|
|
490
550
|
this.live.add(span);
|
|
491
551
|
if (!this.liveTimer) {
|
|
@@ -500,6 +560,8 @@ export class HueTransport {
|
|
|
500
560
|
}
|
|
501
561
|
/** Placeholders are built lazily, so input set right after a span starts is included. */
|
|
502
562
|
announceLiveSpans() {
|
|
563
|
+
if (this.forcedSpanDrains)
|
|
564
|
+
return;
|
|
503
565
|
for (const span of this.live) {
|
|
504
566
|
this.live.delete(span);
|
|
505
567
|
try {
|
|
@@ -547,6 +609,28 @@ export class HueTransport {
|
|
|
547
609
|
placeholderSettled(record) {
|
|
548
610
|
return this.placeholderSources.get(record)?.ended === true;
|
|
549
611
|
}
|
|
612
|
+
beginSpanDrain() {
|
|
613
|
+
const completed = [...this.spans.keys()].some((span) => !this.placeholders.has(span));
|
|
614
|
+
this.forcedSpanDrains++;
|
|
615
|
+
if (completed)
|
|
616
|
+
this.completedSpanDrains++;
|
|
617
|
+
return () => {
|
|
618
|
+
this.forcedSpanDrains--;
|
|
619
|
+
if (completed)
|
|
620
|
+
this.completedSpanDrains--;
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
/** @internal A forced drain sends completed spans without another advisory round trip. */
|
|
624
|
+
deferPlaceholders(records) {
|
|
625
|
+
if (!this.completedSpanDrains)
|
|
626
|
+
return false;
|
|
627
|
+
for (const record of records) {
|
|
628
|
+
const source = this.placeholderSources.get(record);
|
|
629
|
+
if (!this.closed && source)
|
|
630
|
+
this.track(source);
|
|
631
|
+
}
|
|
632
|
+
return true;
|
|
633
|
+
}
|
|
550
634
|
/** @internal Exporter callback: false once the receiver refused placeholders; queued ones are then dropped. */
|
|
551
635
|
sendsPlaceholders() {
|
|
552
636
|
return !this.placeholdersRejected;
|
|
@@ -570,6 +654,11 @@ export class HueTransport {
|
|
|
570
654
|
/** @internal Exporter callback: releases queued records after an export attempt settles. */
|
|
571
655
|
finish(signal, records) {
|
|
572
656
|
for (const record of records) {
|
|
657
|
+
const cohort = this.deliveries.get(record);
|
|
658
|
+
if (cohort) {
|
|
659
|
+
this.deliveries.delete(record);
|
|
660
|
+
this.finishDelivery(cohort);
|
|
661
|
+
}
|
|
573
662
|
this.releaseMedia(record);
|
|
574
663
|
this.logMedia.delete(record);
|
|
575
664
|
const pending = signal === "traces" ? this.spans : this.logs;
|
|
@@ -737,6 +826,7 @@ export class HueTransport {
|
|
|
737
826
|
* them. */
|
|
738
827
|
uploadQueued(record, settled, shared, released) {
|
|
739
828
|
this.awaitUploads(settled);
|
|
829
|
+
this.holdDelivery(this.deliveries.get(record), settled);
|
|
740
830
|
const identity = this.mediaIdentity(record);
|
|
741
831
|
const staged = identity
|
|
742
832
|
? (this.media.get(identity) ?? []).reduce((total, file) => total + heldBytesOf(file.bytes), 0)
|
|
@@ -853,8 +943,8 @@ export class HueTransport {
|
|
|
853
943
|
return this.offloadsValues() && this.uploader?.available() === true;
|
|
854
944
|
}
|
|
855
945
|
/** @internal Exporter callback: a redacted span with the values its redaction left to the
|
|
856
|
-
* upload step placed: listed under `hue.blobs` with their uploads queued (
|
|
857
|
-
*
|
|
946
|
+
* upload step placed: listed under `hue.blobs` with their uploads queued (reported once settled,
|
|
947
|
+
* and charged in place of the `queued` record they came from), or cut as before.
|
|
858
948
|
* Never rejects. */
|
|
859
949
|
async offloadSpan(span, collector, upload, tally, late, queued) {
|
|
860
950
|
// A record without a readable context cannot name its trace: its values are cut.
|
|
@@ -878,8 +968,7 @@ export class HueTransport {
|
|
|
878
968
|
memo: this.mediaMemo,
|
|
879
969
|
background: own,
|
|
880
970
|
});
|
|
881
|
-
|
|
882
|
-
late.settled.push(mergeWhenSettled(late.tally, own.tally, own.settled));
|
|
971
|
+
this.collectDeliveryUploads(queued, own, late);
|
|
883
972
|
return { ...span, attributes: attributes };
|
|
884
973
|
}
|
|
885
974
|
catch {
|
|
@@ -919,14 +1008,37 @@ export class HueTransport {
|
|
|
919
1008
|
memo: this.mediaMemo,
|
|
920
1009
|
background: own,
|
|
921
1010
|
});
|
|
922
|
-
|
|
923
|
-
late.settled.push(mergeWhenSettled(late.tally, own.tally, own.settled));
|
|
1011
|
+
this.collectDeliveryUploads(queued, own, late);
|
|
924
1012
|
return placed(result);
|
|
925
1013
|
}
|
|
926
1014
|
catch {
|
|
927
1015
|
return placed(fallbackRecord(log.attributes, collector, valueBytes, tally, traceId, this.mediaMemo));
|
|
928
1016
|
}
|
|
929
1017
|
}
|
|
1018
|
+
/** Keep an export's warning aggregation within each cohort's upload ownership. */
|
|
1019
|
+
collectDeliveryUploads(record, uploads, late) {
|
|
1020
|
+
const { settled, tally } = uploads;
|
|
1021
|
+
if (!settled.length)
|
|
1022
|
+
return;
|
|
1023
|
+
let groups = this.deliveryUploads.get(late);
|
|
1024
|
+
if (!groups)
|
|
1025
|
+
this.deliveryUploads.set(late, (groups = new Map()));
|
|
1026
|
+
const cohort = this.deliveries.get(record);
|
|
1027
|
+
let group = groups.get(cohort);
|
|
1028
|
+
if (!group)
|
|
1029
|
+
groups.set(cohort, (group = { tally: newTally(), settled: [] }));
|
|
1030
|
+
group.settled.push(mergeWhenSettled(group.tally, tally, settled));
|
|
1031
|
+
}
|
|
1032
|
+
/** @internal Report only the uploads each cohort owns, without retaining their records. */
|
|
1033
|
+
reportUploads(late, signal) {
|
|
1034
|
+
const groups = this.deliveryUploads.get(late);
|
|
1035
|
+
this.deliveryUploads.delete(late);
|
|
1036
|
+
for (const [cohort, { settled, tally }] of groups ?? []) {
|
|
1037
|
+
const report = Promise.all(settled).then(() => this.reportOffload(tally, signal, true));
|
|
1038
|
+
this.awaitUploads(report);
|
|
1039
|
+
this.holdDelivery(cohort, report);
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
930
1042
|
/** @internal Exporter callback: counts an export's uploads and reports each kind of fallback
|
|
931
1043
|
* once, as a warning naming its traces: the values were exported cut as before, or, `late`,
|
|
932
1044
|
* their uploads failed after their records listed them. */
|
|
@@ -1147,13 +1259,19 @@ export class HueTransport {
|
|
|
1147
1259
|
*/
|
|
1148
1260
|
flushRecords(providers) {
|
|
1149
1261
|
const from = this.observedSequence;
|
|
1150
|
-
|
|
1262
|
+
let finish;
|
|
1263
|
+
const join = () => {
|
|
1264
|
+
finish = this.beginSpanDrain();
|
|
1265
|
+
return this.sendRecords();
|
|
1266
|
+
};
|
|
1151
1267
|
const sent = providers ? this.sendingRecords.run(true, providers).then(join, join) : join();
|
|
1152
|
-
const report = sent
|
|
1268
|
+
const report = sent
|
|
1269
|
+
.then(async (processors) => {
|
|
1153
1270
|
// Exports the processors did not start (a timer's, still in flight) queued uploads too.
|
|
1154
1271
|
await this.drainUploads();
|
|
1155
1272
|
return this.flushReport(from, processors);
|
|
1156
|
-
})
|
|
1273
|
+
})
|
|
1274
|
+
.finally(() => finish?.());
|
|
1157
1275
|
return { sent, report };
|
|
1158
1276
|
}
|
|
1159
1277
|
/** Sends what the processors and exporters hold, once the previous flush's send ended. */
|
|
@@ -1387,6 +1505,7 @@ class ReportingExporter {
|
|
|
1387
1505
|
this.redact = redact;
|
|
1388
1506
|
}
|
|
1389
1507
|
export(records, callback) {
|
|
1508
|
+
this.transport.exporting(records);
|
|
1390
1509
|
const work = this.exportRecords(records)
|
|
1391
1510
|
.then(() => callback({ code: ExportResultCode.SUCCESS }), () => callback({
|
|
1392
1511
|
code: ExportResultCode.FAILED,
|
|
@@ -1423,23 +1542,16 @@ class ReportingExporter {
|
|
|
1423
1542
|
real.push(placeholder);
|
|
1424
1543
|
return real;
|
|
1425
1544
|
}
|
|
1426
|
-
/**
|
|
1427
|
-
* settle, after the records were sent, never awaited here. */
|
|
1545
|
+
/** Queue final upload reports before releasing the records that own their cohorts. */
|
|
1428
1546
|
async exportRecords(records) {
|
|
1429
1547
|
const late = { tally: newTally(), settled: [] };
|
|
1430
1548
|
try {
|
|
1431
1549
|
await this.placeAndSend(records, late);
|
|
1432
1550
|
}
|
|
1433
1551
|
finally {
|
|
1434
|
-
|
|
1435
|
-
this.reportWhenSettled(late);
|
|
1552
|
+
this.transport.reportUploads(late, this.signal);
|
|
1436
1553
|
}
|
|
1437
1554
|
}
|
|
1438
|
-
/** Reports an export's uploads once they all settle. Built apart from the export, so a pending
|
|
1439
|
-
* upload never keeps the export's records alive. */
|
|
1440
|
-
reportWhenSettled(late) {
|
|
1441
|
-
this.transport.awaitUploads(Promise.all(late.settled).then(() => this.transport.reportOffload(late.tally, this.signal, true)));
|
|
1442
|
-
}
|
|
1443
1555
|
async placeAndSend(records, late) {
|
|
1444
1556
|
// Completed records and placeholders travel in separate requests, completed records first,
|
|
1445
1557
|
// so a rejection count is always one kind of record's. Placeholders are advisory: losing one
|
|
@@ -1850,7 +1962,10 @@ class ReportingExporter {
|
|
|
1850
1962
|
// records' acknowledgements may have just shown, nor after this export's completed records were
|
|
1851
1963
|
// refused or unanswered: a receiver that just refused or timed out gets no second request with
|
|
1852
1964
|
// its own retries. A record this side could not send keeps the placeholders going out.
|
|
1853
|
-
if (pending.length &&
|
|
1965
|
+
if (pending.length &&
|
|
1966
|
+
!refused &&
|
|
1967
|
+
this.transport.sendsPlaceholders() &&
|
|
1968
|
+
!this.transport.deferPlaceholders(records))
|
|
1854
1969
|
await pack(pending, true);
|
|
1855
1970
|
if (failed)
|
|
1856
1971
|
throw new Error("Hue telemetry export failed");
|
package/dist/version.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/** Package version shared by the instrumentation scope and the export User-Agent. */
|
|
2
|
-
export declare const sdkVersion = "0.
|
|
2
|
+
export declare const sdkVersion = "0.23.0";
|
|
3
3
|
/** OTLP dependency version sent in the export User-Agent. */
|
|
4
4
|
export declare const otlpExporterVersion = "0.223.0";
|
package/dist/version.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Generated by scripts/write-version.mjs from package.json; do not edit by hand.
|
|
2
2
|
/** Package version shared by the instrumentation scope and the export User-Agent. */
|
|
3
|
-
export const sdkVersion = "0.
|
|
3
|
+
export const sdkVersion = "0.23.0";
|
|
4
4
|
/** OTLP dependency version sent in the export User-Agent. */
|
|
5
5
|
export const otlpExporterVersion = "0.223.0";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hue-run/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -98,6 +98,7 @@
|
|
|
98
98
|
"@opentelemetry/resources": "2.12.0",
|
|
99
99
|
"@opentelemetry/sdk-logs": "0.223.0",
|
|
100
100
|
"@opentelemetry/sdk-trace": "2.12.0",
|
|
101
|
+
"@vercel/functions": "3.9.7",
|
|
101
102
|
"zod": "^3.25.76 || ^4.6.5"
|
|
102
103
|
},
|
|
103
104
|
"devDependencies": {
|