@heystack/otel 0.14.0 → 0.16.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/workers.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type Span } from "@opentelemetry/api";
1
+ import { SpanKind, type Span } from "@opentelemetry/api";
2
2
  import { type Context, type ContextManager } from "@opentelemetry/api";
3
3
  import { BasicTracerProvider, type ReadableSpan, type SpanExporter } from "@opentelemetry/sdk-trace-base";
4
4
  import { type HeystackOptions } from "./core.js";
@@ -92,6 +92,8 @@ export declare class HeystackSpanExporter implements SpanExporter {
92
92
  readonly ingestHost: string;
93
93
  private readonly headers;
94
94
  private shutdownState;
95
+ /** Exports rejected before network work because the in-flight cap was hit. */
96
+ droppedPending: number;
95
97
  /**
96
98
  * In-flight export fetches. Each `export()` adds its settled-when-the-POST-
97
99
  * completes promise here and removes it on `finally`. `forceFlush()` awaits
@@ -112,10 +114,21 @@ export declare class HeystackSpanExporter implements SpanExporter {
112
114
  waitUntil?: (p: Promise<unknown>) => void;
113
115
  constructor(options: HeystackOptions);
114
116
  export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void): void;
117
+ /**
118
+ * POST the batch with ONE bounded automatic retry for network errors / 429 /
119
+ * 5xx. Honours `Retry-After` (seconds form) on 429 within the total backoff
120
+ * cap; after retries are exhausted it preserves plain FAILED semantics via
121
+ * `resultCallback`. The returned promise never rejects.
122
+ */
123
+ private postWithRetry;
124
+ /** Reserve a tracked slot synchronously before starting any network work. */
125
+ private reservePending;
115
126
  /**
116
127
  * Hand `p` to the OpenNext Cloudflare request context's `ctx.waitUntil`.
117
128
  * Resolves the accessor first (awaiting its in-flight load if needed) so an
118
129
  * export that fired before the dynamic import settled is still covered.
130
+ * Sets `outcome.borrowed` when (and only when) a real `waitUntil` received
131
+ * `p` — the signal the no-handoff advisory keys off.
119
132
  */
120
133
  private handOffToCloudflareContext;
121
134
  private warnedWaitUntilFailure;
@@ -235,13 +248,11 @@ export interface WorkersConfig {
235
248
  /** Minimal public surface exposed by Cloudflare's Workers custom-spans API. */
236
249
  export interface CloudflareNativeSpan {
237
250
  readonly isTraced: boolean;
238
- setAttribute(key: string, value: string | number | boolean | undefined): void;
239
- end(): void;
251
+ setAttribute(key: string, value?: string | number | boolean): void;
240
252
  }
241
253
  /** `ExecutionContext.tracing` without importing the `cloudflare:workers` module. */
242
254
  export interface CloudflareNativeTracing {
243
- enterSpan<T>(name: string, callback: (span: CloudflareNativeSpan) => T): T;
244
- startActiveSpan<T>(name: string, callback: (span: CloudflareNativeSpan) => T): T;
255
+ enterSpan<T, A extends unknown[]>(name: string, callback: (span: CloudflareNativeSpan, ...args: A) => T, ...args: A): T;
245
256
  }
246
257
  /**
247
258
  * A `BasicTracerProvider` with the underlying `HeystackSpanExporter` attached so
@@ -273,9 +284,9 @@ export declare function createTracerProvider(config: HeystackOptions & {
273
284
  * context across `await` boundaries — child spans created after an `await`
274
285
  * inside a handler are correctly parented to the request's root span.
275
286
  *
276
- * WinterCG-safe: it does NOT import `node:async_hooks`; instead it uses the
277
- * ALS global that the runtime exposes. Falls back to `SyncStackContextManager`
278
- * when the global is absent (Deno, Bun without globals, etc.).
287
+ * WinterCG-safe: it does NOT import the Node-only async_hooks builtin; instead
288
+ * it uses the ALS global that the runtime exposes. Falls back to
289
+ * `SyncStackContextManager` when the global is absent (Deno, Bun without globals, etc.).
279
290
  */
280
291
  export declare class AlsContextManager implements ContextManager {
281
292
  private _als;
@@ -311,10 +322,12 @@ export declare function __resetContextManager(): void;
311
322
  * FLUSH: on Next-on-OpenNext (where `@opennextjs/cloudflare` is present) the
312
323
  * exporter automatically borrows the Cloudflare request context's
313
324
  * `ctx.waitUntil` so the export POST completes before the isolate is torn down
314
- * — no app hook needed. For other workerd setups without it, call
315
- * `flushHeystack()` (which awaits the export fetch) from a response hook, hand
316
- * it to `ctx.waitUntil(flushHeystack())`, or pass an explicit `waitUntil` in
317
- * the config (highest priority). Returns the provider.
325
+ * — no app hook needed. For other workerd setups without it, wrap your
326
+ * handler's ExecutionContext with {@link wrapExecutionContext} (every
327
+ * `ctx.waitUntil` then also schedules the export drain), call `flushHeystack()`
328
+ * (which awaits the export fetch) from a response hook, hand it to
329
+ * `ctx.waitUntil(flushHeystack())`, or pass an explicit `waitUntil` in the
330
+ * config (highest priority). Returns the provider.
318
331
  */
319
332
  export declare function initHeystackWorkers(config: WorkersConfig & {
320
333
  apiKey: string;
@@ -328,21 +341,25 @@ export declare function initHeystackWorkers(config: WorkersConfig & {
328
341
  * isolate is torn down. Best-effort no-op if no provider is registered.
329
342
  */
330
343
  export declare function flushHeystack(): Promise<void>;
344
+ export declare function wrapExecutionContext<Props>(ctx: ExecutionContext<Props>): ExecutionContext<Props>;
331
345
  /** Reset the singleton global provider. Internal/testing helper. */
332
346
  export declare function __resetProvider(): void;
333
- /** Reset the once-only no-key warning. Internal/testing helper. */
347
+ /** Reset the once-only warnings. Internal/testing helper. */
334
348
  export declare function __resetWarnings(): void;
335
349
  /**
336
350
  * The shape of a Worker's default export. `fetch` is the common entrypoint, but
337
- * a Worker may also export `queue` / `scheduled` / `tail` (and arbitrary other
338
- * siblings). `instrument()` forwards ALL of these untouched except `fetch`
339
- * (always traced) and `queue` / `scheduled` (traced when present) — so wrapping
340
- * a Worker never drops a handler Cloudflare requires for deploy.
351
+ * a Worker may also export `queue` / `scheduled` / `tail` / `email` (and
352
+ * arbitrary other siblings). `instrument()` traces `fetch`, `queue`,
353
+ * `scheduled`, `email` and `tail` when present — every other sibling is
354
+ * forwarded untouched, so wrapping never drops a handler Cloudflare requires
355
+ * for deploy.
341
356
  */
342
357
  interface WorkerHandler<E> {
343
358
  fetch?(req: Request, env: E, ctx: ExecutionContext): Promise<Response> | Response;
344
359
  queue?(batch: MessageBatch<any>, env: E, ctx: ExecutionContext): Promise<void> | void;
345
360
  scheduled?(controller: ScheduledController, env: E, ctx: ExecutionContext): Promise<void> | void;
361
+ email?(message: ForwardableEmailMessage, env: E, ctx: ExecutionContext): Promise<void> | void;
362
+ tail?(events: TraceItem[], env: E, ctx: ExecutionContext): Promise<void> | void;
346
363
  [key: string]: unknown;
347
364
  }
348
365
  /**
@@ -370,8 +387,8 @@ interface WorkerHandler<E> {
370
387
  * ALL HANDLERS FORWARDED: the returned object spreads `handler`, so sibling
371
388
  * handlers a Worker exports (`tail`, `email`, etc.) are preserved — Cloudflare
372
389
  * won't reject the deploy for a missing handler. `fetch` is always replaced with
373
- * a traced wrapper; `queue` and `scheduled` are wrapped (and traced) when
374
- * present; everything else is forwarded untouched.
390
+ * a traced wrapper; `queue`, `scheduled`, `email` and `tail` are wrapped (and
391
+ * traced) when present; everything else is forwarded untouched.
375
392
  *
376
393
  * If no API key is available (neither `config.apiKey` nor
377
394
  * `env.HEYSTACK_API_KEY`), the handler runs untraced.
@@ -383,7 +400,7 @@ interface WorkerHandler<E> {
383
400
  * traced key is present (and non-optional) exactly when it exists on the input
384
401
  * handler, so a `{ fetch }` Worker yields a non-optional `fetch`.
385
402
  */
386
- type Instrumented<E, H> = Omit<H, "fetch" | "queue" | "scheduled"> & (H extends {
403
+ type Instrumented<E, H> = Omit<H, "fetch" | "queue" | "scheduled" | "email" | "tail"> & (H extends {
387
404
  fetch: unknown;
388
405
  } ? {
389
406
  fetch: (req: Request, env: E, ctx: ExecutionContext) => Promise<Response>;
@@ -395,6 +412,14 @@ type Instrumented<E, H> = Omit<H, "fetch" | "queue" | "scheduled"> & (H extends
395
412
  scheduled: unknown;
396
413
  } ? {
397
414
  scheduled: (controller: ScheduledController, env: E, ctx: ExecutionContext) => Promise<void>;
415
+ } : unknown) & (H extends {
416
+ email: unknown;
417
+ } ? {
418
+ email: (message: ForwardableEmailMessage, env: E, ctx: ExecutionContext) => Promise<void>;
419
+ } : unknown) & (H extends {
420
+ tail: unknown;
421
+ } ? {
422
+ tail: (events: TraceItem[], env: E, ctx: ExecutionContext) => Promise<void>;
398
423
  } : unknown);
399
424
  /**
400
425
  * Recover a handler's concrete env type from whichever traced entrypoint it
@@ -412,6 +437,52 @@ type EnvOf<H> = H extends {
412
437
  scheduled: (controller: ScheduledController, env: infer E, ctx: ExecutionContext) => unknown;
413
438
  } ? E : unknown;
414
439
  export declare function instrument<H extends WorkerHandler<any>>(handler: H, config: WorkersConfig): Instrumented<EnvOf<H>, H>;
440
+ export interface InstrumentClassesOpts {
441
+ /**
442
+ * Factory that creates and starts a new span. Called at method-invocation
443
+ * time. For integration: omit it — the global `heystack` tracer is used with
444
+ * the active context (or the explicit `parentCtx`). For unit tests inject a
445
+ * fake so no global provider is required.
446
+ */
447
+ startSpan?: (name: string, attrs: Record<string, unknown>, kind?: SpanKind, parentCtx?: Context) => Span;
448
+ }
449
+ /**
450
+ * Decorate a Durable Object class:
451
+ * - constructor: wraps the incoming `state` so `state.storage` operations emit
452
+ * INTERNAL child spans (`DO storage get|put|delete|list`) with key/count attrs.
453
+ * - `fetch()`: SERVER span named `<METHOD> <path>` (like instrument()'s fetch
454
+ * wrapper), continuing an inbound W3C traceparent, recording status/duration.
455
+ * - `alarm()`: INTERNAL span.
456
+ * - RPC prototype methods: SERVER span `DO rpc <method>` with rpc.method attrs.
457
+ *
458
+ * Every root span schedules flushHeystack() via `state.waitUntil` so exports
459
+ * complete before isolate teardown. Instance arrow-function methods assigned in
460
+ * the constructor are not enumerable on the prototype and stay unwrapped.
461
+ *
462
+ * export class Counter extends DurableObject {
463
+ * // ...
464
+ * }
465
+ * export const Counter = wrapDurableObjectClass(BaseCounter); // or decorate in place
466
+ */
467
+ export declare function wrapDurableObjectClass<C extends {
468
+ readonly prototype: object;
469
+ }>(cls: C, opts?: InstrumentClassesOpts): C;
470
+ /**
471
+ * Decorate a Cloudflare Workflows entrypoint class so its `run(event, step)`
472
+ * emits a `workflow run` INTERNAL span with workflow identity/schedule metadata
473
+ * (never payload content), and the handed-out
474
+ * step object's methods become traced versions (see {@link wrapWorkflowStep}):
475
+ * `step.do(name, fn)` → INTERNAL span named `step <name>` with success/error
476
+ * status (step retries manifest as whole-run replays by the engine and are not
477
+ * observable at this layer, so no retry attribute is emitted), and
478
+ * sleep/sleepUntil/waitForEvent/waitForName → short marker spans.
479
+ *
480
+ * Every completed run schedules flushHeystack() via the entrypoint state's
481
+ * `waitUntil` so exports survive isolate teardown.
482
+ */
483
+ export declare function wrapWorkflowEntrypointClass<C extends {
484
+ readonly prototype: object;
485
+ }>(cls: C, opts?: InstrumentClassesOpts): C;
415
486
  /**
416
487
  * Run `fn` inside a new child span named `name`. The span is automatically
417
488
  * parented to the currently-active span (via `context.active()`), started