@autter/runtime-node 1.4.0 → 1.6.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/index.d.cts CHANGED
@@ -1,4 +1,6 @@
1
1
  import { IncomingMessage, ServerResponse } from 'node:http';
2
+ import { b as RedactOptions, c as Redactor$1, d as RuntimeLogLevel, e as RuntimeOperationKind, f as RuntimeSink, F as FileSinkOptions, g as RuntimeLogContext, a as RuntimeOutcome, h as RuntimeCarrier, B as BreadcrumbInput } from './sinks-CPGDyfU8.cjs';
3
+ export { i as Breadcrumb, j as BreadcrumbLevel, k as BreadcrumbType, l as CODE_PATTERN, n as ClientErrorBody, o as ConsoleSinkOptions, O as OtlpSinkOptions, p as RuntimeError, q as RuntimeErrorCatalog, r as RuntimeErrorDefinition, s as RuntimeErrorExtras, t as RuntimeErrorFactory, u as RuntimeErrorLike, v as RuntimeErrorOptions, R as RuntimeEvent, w as RuntimeLogContextValue, x as RuntimeSinkContext, y as consoleSink, z as defineRuntimeErrors, A as errorAttributes, D as fileSink, E as isRuntimeErrorLike, G as otlpSink, H as redactAttributes, I as redactText, J as toClientError } from './sinks-CPGDyfU8.cjs';
2
4
  import { Attributes } from '@opentelemetry/api';
3
5
 
4
6
  /**
@@ -23,8 +25,25 @@ interface RelayOptions {
23
25
  perIpRateLimit?: number | false;
24
26
  /** Called when the async forward fails (default: console.warn). */
25
27
  onError?: (err: unknown) => void;
28
+ /**
29
+ * Scrub secrets/PII from browser events (message, stack, name, nested
30
+ * context) before they leave your server — the same patterns as
31
+ * `initAutterServer({ redactAttributes })`. Catches old browser SDK
32
+ * versions and anything the browser's lean redactor misses. Pass a
33
+ * RedactOptions object to add your own patterns; `false` disables.
34
+ * Default true.
35
+ */
36
+ redact?: boolean | RedactOptions;
37
+ /**
38
+ * Warn once (server-side) when browser features in the forwarded events
39
+ * — CSP violations, network/timing capture — need a newer ingester than
40
+ * the one answering. Reads the ingester's version header from the forward
41
+ * response; never blocks the 202. Also disabled by AUTTER_COMPAT_CHECK=0.
42
+ * Default true.
43
+ */
44
+ compatCheck?: boolean;
26
45
  }
27
- declare function sanitizeBrowserPayload(raw: unknown): object | null;
46
+ declare function sanitizeBrowserPayload(raw: unknown, redactor?: Redactor$1): object | null;
28
47
  /**
29
48
  * Fetch-style handler (Next.js App Router route, Remix, Hono, Bun, Deno):
30
49
  *
@@ -43,42 +62,55 @@ declare function createBrowserRelayHandler(opts: RelayOptions): (req: IncomingMe
43
62
  body?: unknown;
44
63
  }, res: ServerResponse) => void;
45
64
 
46
- interface RedactOptions {
65
+ /** Mutable view of a record handed to enrichers before redaction/bounding. */
66
+ interface RuntimeEnrichEvent {
67
+ level: RuntimeLogLevel;
68
+ message: string;
69
+ attributes: Record<string, unknown>;
70
+ }
71
+ interface RuntimeEnrichContext {
72
+ /** The inbound request of a request summary (Node IncomingMessage or fetch Request). */
73
+ req?: unknown;
74
+ /** The response object, when the framework exposes one. */
75
+ res?: unknown;
76
+ operation: {
77
+ id: string;
78
+ name: string;
79
+ kind: RuntimeOperationKind;
80
+ };
81
+ }
82
+ /** Adds attributes to operation/request summaries. Must be cheap; errors are ignored. */
83
+ type RuntimeEnricher = (event: RuntimeEnrichEvent, context: RuntimeEnrichContext) => void;
84
+ interface RuntimeLoggingOptions {
85
+ /** Print records to the console (default sinks only). Default true. */
86
+ console?: boolean;
87
+ /** Drop plain log records below this level. Summaries are always kept. */
88
+ minLevel?: RuntimeLogLevel;
47
89
  /**
48
- * Extra patterns matched against lower-cased attribute KEYS; a match
49
- * masks the whole value. Strings become case-insensitive substring
50
- * patterns. Extends the built-in list.
90
+ * Fold debug/info messages logged inside an operation into its summary
91
+ * (`autter.operation.logs`, max 50) instead of emitting separate records;
92
+ * warn/error are folded AND emitted. Default true.
51
93
  */
52
- additionalKeyPatterns?: (RegExp | string)[];
94
+ inline?: boolean;
95
+ /** Run on every operation/request summary before redaction. */
96
+ enrich?: RuntimeEnricher[];
53
97
  /**
54
- * Extra patterns scrubbed inside string VALUES (same semantics as the
55
- * built-in email/token patterns). Extends the built-in list.
98
+ * Replace the default sinks (`[otlpSink(), consoleSink()]`, plus
99
+ * `fileSink()` when NODE_ENV=development).
56
100
  */
57
- additionalValuePatterns?: (RegExp | string)[];
58
- /** Replacement token. Default "[redacted]". */
59
- mask?: string;
101
+ sinks?: RuntimeSink[];
60
102
  /**
61
- * Scrub email-shaped substrings from ALL string values, not just
62
- * sensitive keys. Default true.
103
+ * Local NDJSON files (`.autter/runtime/YYYY-MM-DD.jsonl`). Default: on only
104
+ * when NODE_ENV === "development". Ignored when `sinks` is set.
63
105
  */
64
- scrubEmailValues?: boolean;
65
- }
66
- /**
67
- * Return a copy of `attributes` with PII/secrets masked. Never mutates the
68
- * input; non-string primitives pass through untouched; `undefined` values
69
- * are dropped (OpenTelemetry rejects them).
70
- */
71
- declare function redactAttributes(attributes?: Attributes | null, options?: RedactOptions): Attributes;
72
-
73
- type RuntimeLogContextValue = string | number | boolean | null | undefined | RuntimeLogContextValue[] | {
74
- [key: string]: RuntimeLogContextValue;
75
- };
76
- type RuntimeLogContext = Record<string, RuntimeLogContextValue>;
77
- type RuntimeLogLevel = "debug" | "info" | "warning" | "error";
78
- type RuntimeOutcome = "succeeded" | "failed" | "degraded" | "cancelled" | "pending";
79
- interface RuntimeLoggingOptions {
80
- console?: boolean;
81
- minLevel?: RuntimeLogLevel;
106
+ file?: boolean | FileSinkOptions;
107
+ /**
108
+ * EXPERIMENTAL zero-code request summaries for plain `node:http` servers
109
+ * via the HTTP instrumentation hook (initAutterServer only). Uses
110
+ * AsyncLocalStorage.enterWith, which can leak context across keep-alive /
111
+ * pipelined requests on one socket — prefer `autterRequests()`. Default false.
112
+ */
113
+ requests?: boolean;
82
114
  }
83
115
  interface RuntimeOperation {
84
116
  readonly id: string;
@@ -86,6 +118,12 @@ interface RuntimeOperation {
86
118
  outcome(status: RuntimeOutcome, message?: string): void;
87
119
  step<T>(name: string, fn: () => T | Promise<T>): Promise<T>;
88
120
  }
121
+ interface RuntimeOperationOptions {
122
+ /** Carrier from `runtimeContext.carrier()` (object or JSON string): links parent op, request id and trace. */
123
+ from?: RuntimeCarrier | string | null;
124
+ /** Receives the log flush promise once the operation completes (serverless `waitUntil`). */
125
+ waitUntil?: (promise: Promise<unknown>) => void;
126
+ }
89
127
  declare function runtimeLogStats(): {
90
128
  buffered: number;
91
129
  dropped: number;
@@ -103,8 +141,45 @@ declare const runtimeLogger: {
103
141
  warn: (message: string, extra?: RuntimeLogContext) => void;
104
142
  error: (error: unknown, extra?: RuntimeLogContext) => void;
105
143
  };
106
- /** Context stays local to this operation; queues/processes require explicit propagation. */
107
- declare function withRuntimeOperation<T>(name: string, fn: (operation: RuntimeOperation) => T | Promise<T>, attributes?: RuntimeLogContext): Promise<T>;
144
+ /**
145
+ * Run `fn` as a named operation: one summary record (outcome, duration,
146
+ * steps, context, inline messages) when it ends. Nested operations link via
147
+ * `autter.operation.parent_id`; pass `{ from: carrier }` to continue work
148
+ * handed over through a queue or another process.
149
+ */
150
+ declare function withRuntimeOperation<T>(name: string, fn: (operation: RuntimeOperation) => T | Promise<T>, attributes?: RuntimeLogContext, options?: RuntimeOperationOptions): Promise<T>;
151
+ /**
152
+ * Like `runtimeContext.fork` but not awaited: errors are recorded on the
153
+ * child operation (and its span) and never become unhandled rejections.
154
+ */
155
+ declare function runInBackground<T>(name: string, fn: (operation: RuntimeOperation) => T | Promise<T>): Promise<T | undefined>;
156
+ /**
157
+ * The current request/operation, from anywhere in its async call tree.
158
+ * Every method is a safe no-op (or plain log) outside an operation.
159
+ */
160
+ declare const runtimeContext: {
161
+ /** Merge attributes into the current summary's context. */
162
+ set(attributes: RuntimeLogContext): void;
163
+ /** Set the outcome explicitly (wins over status/thrown-error rules). */
164
+ outcome(status: RuntimeOutcome, message?: string): void;
165
+ debug(message: string, attributes?: RuntimeLogContext): void;
166
+ info(message: string, attributes?: RuntimeLogContext): void;
167
+ warn(message: string, attributes?: RuntimeLogContext): void;
168
+ /** Log an error and attach it (code/why/fix) to the current summary. */
169
+ error(error: unknown, attributes?: RuntimeLogContext): void;
170
+ /** Current operation id. */
171
+ readonly id: string | undefined;
172
+ /** Current request id (inherited by child operations and forks). */
173
+ readonly requestId: string | undefined;
174
+ /**
175
+ * Run `fn` as a child operation whose parent link is captured NOW, so it
176
+ * still links after the parent has finished (background work).
177
+ */
178
+ fork<T>(name: string, fn: (operation: RuntimeOperation) => T | Promise<T>): Promise<T>;
179
+ /** Serialisable link for queue payloads: `{ v: 1, op, req?, traceparent? }`. */
180
+ carrier(): RuntimeCarrier | undefined;
181
+ };
182
+ type RuntimeContextHandle = typeof runtimeContext;
108
183
 
109
184
  /**
110
185
  * Curated OpenTelemetry setup for Autter Runtime — deliberately NOT the
@@ -125,10 +200,16 @@ declare function withRuntimeOperation<T>(name: string, fn: (operation: RuntimeOp
125
200
  * request metrics : aggregated every 60 s, keyed by route
126
201
  * template (Express routes detected
127
202
  * out of the box — see captureExpressRoute)
128
- * logs : not collected
203
+ * operation / request summaries: 100% (withRuntimeOperation,
204
+ * autterRequests, … — one wide
205
+ * record per unit of work, never
206
+ * sampled)
207
+ * logs : runtimeLogger / runtimeContext records
208
+ * (debug/info folded into the current
209
+ * operation's summary; minLevel applies)
129
210
  *
130
211
  * Exports OTLP/HTTP JSON to the Autter ingester (`/v1/traces`,
131
- * `/v1/metrics`) with the ingest key as a bearer header.
212
+ * `/v1/metrics`, `/v1/logs`) with the ingest key as a bearer header.
132
213
  */
133
214
  interface AutterServerOptions {
134
215
  /** Structured logs and completed operation summaries. */
@@ -161,8 +242,23 @@ interface AutterServerOptions {
161
242
  metricIntervalMs?: number;
162
243
  /** Collect RSS, heap, limit, and GC signals per process instance. Default true. */
163
244
  memoryMetrics?: boolean;
164
- /** Capture crashing exceptions via process.uncaughtExceptionMonitor (default true). */
245
+ /**
246
+ * Capture crashing exceptions via process.uncaughtExceptionMonitor, and
247
+ * unhandled promise rejections (see `captureUnhandledRejections`).
248
+ * Default true; false turns both off.
249
+ */
165
250
  captureGlobalErrors?: boolean;
251
+ /**
252
+ * Record unhandled promise rejections as errors (severity "error",
253
+ * `autter.unhandled_rejection: true`). Never changes whether the process
254
+ * crashes: when Node would have crashed (the default
255
+ * `--unhandled-rejections=throw` mode and no other `unhandledRejection`
256
+ * listener), the rejection is rethrown on the next tick, so the process
257
+ * still exits with code 1 and the crash is recorded as fatal like any
258
+ * other. False restores 1.5.0 behaviour: a rejection is only seen when it
259
+ * crashes the process. Default true.
260
+ */
261
+ captureUnhandledRejections?: boolean;
166
262
  /**
167
263
  * Flush all exporters when the process exits (`beforeExit`, SIGINT,
168
264
  * SIGTERM) so a forgotten `shutdown()` doesn't silently drop buffered
@@ -171,12 +267,17 @@ interface AutterServerOptions {
171
267
  */
172
268
  autoFlush?: boolean;
173
269
  /**
174
- * Mask PII/secrets in custom attributes before they leave the process:
175
- * emails, tokens (JWT, sk-, gh_, AWS, Slack), bearer headers,
176
- * `scheme://user:pass@` URLs, and anything whose attribute KEY looks
177
- * sensitive (password/token/secret/cookie/…). Mirrors the browser
178
- * relay's whitelist sanitiser. `true`/undefined = defaults; pass a
179
- * RedactOptions object to extend the patterns; `false` disables.
270
+ * Mask secrets/PII before anything leaves the process — custom
271
+ * attributes, exception messages and stack traces, span status
272
+ * messages, URLs/query strings and attributes set by any
273
+ * instrumentation (scrubbed again at export), and structured logs:
274
+ * JWTs, bearer/basic credentials, Cookie/Authorization header text,
275
+ * `scheme://user:pass@` connection strings, vendor API keys (sk-, sk_live_,
276
+ * ghp_, xox*-, AIza, AKIA, …), PEM private keys, `password=`/`?token=`
277
+ * assignments, emails, card numbers, and anything whose attribute KEY
278
+ * looks sensitive (password/token/secret/cookie/authorization/session/…).
279
+ * `true`/undefined = defaults; pass a RedactOptions object to add your
280
+ * own key/value patterns; `false` disables (not recommended).
180
281
  * Default true.
181
282
  */
182
283
  redactAttributes?: boolean | RedactOptions;
@@ -194,6 +295,22 @@ interface AutterServerOptions {
194
295
  llmTracing?: boolean;
195
296
  /** Extra instrumentations (e.g. `new ExpressInstrumentation()`). */
196
297
  instrumentations?: unknown[];
298
+ /**
299
+ * Check once, in the background, that the ingester supports the features
300
+ * in use (operation logging, memory metrics, …) and print one warning per
301
+ * mismatch naming both versions and the fix. Never throws or delays
302
+ * startup. Also disabled by AUTTER_COMPAT_CHECK=0. Default true.
303
+ * Run `npx @autter/runtime-node doctor` for a full report.
304
+ */
305
+ compatCheck?: boolean;
306
+ /**
307
+ * @internal Wrapper packages (`@autter/runtime-next`) report their own
308
+ * name/version as the OTLP `telemetry.distro.*` resource attributes.
309
+ */
310
+ distro?: {
311
+ name: string;
312
+ version: string;
313
+ };
197
314
  }
198
315
  type AutterSeverity = "fatal" | "error" | "warning" | "info";
199
316
  /** Token usage (and optionally exact cost) of one LLM call. */
@@ -205,6 +322,8 @@ interface LlmUsage {
205
322
  * built-in per-model price estimate.
206
323
  */
207
324
  costUsd?: number;
325
+ /** Prompt tokens served from the provider cache (rolled into `autter.operation.ai`). */
326
+ cacheReadTokens?: number;
208
327
  }
209
328
  interface LlmCallInfo {
210
329
  /** Provider system: "openai", "anthropic", "google", "mistral_ai", … */
@@ -240,7 +359,12 @@ interface TrackedLlmCall extends LlmCallInfo, LlmUsage {
240
359
  error?: unknown;
241
360
  }
242
361
  interface AutterServer {
243
- /** Report a handled exception. Always recorded (never sampled out). */
362
+ /**
363
+ * Report a handled exception. Always recorded (never sampled out).
364
+ * `attributes.fingerprint` (a string or up to 10 strings) groups every
365
+ * error that carries it into one issue (ingester 1.6.0+); it is sent as
366
+ * `autter.fingerprint` on the exception event, not as a span attribute.
367
+ */
244
368
  captureException(error: unknown, attributes?: Attributes): void;
245
369
  /**
246
370
  * Report a warning (or info) without an exception — deprecated code
@@ -433,4 +557,399 @@ declare function startCaughtExceptionSampler(options?: {
433
557
  stop(): void;
434
558
  };
435
559
 
436
- export { type AutoFlushHandle, type AutoFlushOptions, type AutterServer, type AutterServerOptions, type AutterSeverity, type FlushTarget, type InstrumentLlmOptions, type LlmCallHandle, type LlmCallInfo, type LlmUsage, type RedactOptions, type RelayOptions, type RuntimeLogContext, type RuntimeLogContextValue, type RuntimeLogLevel, type RuntimeLoggingOptions, type RuntimeOperation, type RuntimeOutcome, type SafeCapture, type TrackedLlmCall, captureException, captureMessage, createBrowserRelayFetchHandler, createBrowserRelayHandler, createRuntimeLogger, emitLlmSelftestTrace, flushRuntimeLogs, initAutterServer, installAutterAutoFlush, instrumentLlmClient, makeSafeCapture, redactAttributes, reportOutcome, runtimeLogStats, runtimeLogger, sanitizeBrowserPayload, startCaughtExceptionSampler, trackLlmCall, withLlmCall, withProcessSpan, withRuntimeOperation };
560
+ interface AutterRequestsOptions {
561
+ /** Path globs never summarised: `*` = one segment, `**` = any depth. */
562
+ ignore?: string[];
563
+ /** Request id header to honour and echo. Default "x-request-id". */
564
+ requestIdHeader?: string;
565
+ /** Honour an inbound request id that matches `^[\w.-]{8,128}$`. Default true. */
566
+ trustRequestId?: boolean;
567
+ }
568
+ interface AutterErrorResponseOptions {
569
+ /**
570
+ * Report the error as an exception. Default: errors with status ≥ 500,
571
+ * RuntimeErrors and any error with a valid code (expected ones included —
572
+ * they are recorded but never open incidents).
573
+ */
574
+ capture?: boolean | ((error: unknown) => boolean);
575
+ /** Header the request id is read from when no request summary is active. */
576
+ requestIdHeader?: string;
577
+ }
578
+ interface RuntimeRequestOptions {
579
+ /** Summary name suffix (default: the route). Name = `METHOD name`. */
580
+ name?: string;
581
+ /** Route template for `http.route` (default: pathname with ids → `:id`). */
582
+ route?: string;
583
+ requestIdHeader?: string;
584
+ trustRequestId?: boolean;
585
+ ignore?: string[];
586
+ /** Receives the log flush promise after the response (serverless `waitUntil`). */
587
+ waitUntil?: (promise: Promise<unknown>) => void;
588
+ }
589
+ /**
590
+ * Report an error that reached a request boundary — a framework error hook,
591
+ * an exception filter, Next.js `onRequestError`. Unlike `captureException`
592
+ * it reports each error once (an error a handler or another hook already
593
+ * captured is skipped), and it records the exception, with its stack, on the
594
+ * request's HTTP server span even when a child span is active, so the failed
595
+ * request and its exception stay one issue. When the error was already
596
+ * captured (e.g. by `withRuntimeRequest`), `attributes` still go on the
597
+ * request span, without a second exception.
598
+ */
599
+ declare function captureRequestError(error: unknown, attributes?: Attributes): void;
600
+ /**
601
+ * Express / Connect middleware. Mount it early (before routers; after body
602
+ * parsers is fine too):
603
+ *
604
+ * app.use(autterRequests({ ignore: ["/healthz", "/metrics"] }));
605
+ *
606
+ * Echoes the request id in the response (`x-request-id`), stamps it on the
607
+ * server span and every record, and emits the summary on finish/close.
608
+ */
609
+ declare function autterRequests(options?: AutterRequestsOptions): (req: IncomingMessage, res: ServerResponse, next: (error?: unknown) => void) => void;
610
+ /**
611
+ * Express error middleware: records the error on the request summary,
612
+ * reports it (see `capture`), and answers with
613
+ * `{ error: { message, code?, why?, fix?, link?, requestId? } }` and the
614
+ * error's status (default 500). Never includes `internal`, stacks or causes;
615
+ * unstructured 5xx messages are replaced with a generic one.
616
+ *
617
+ * app.use(autterErrorResponse()); // after your routes
618
+ */
619
+ declare function autterErrorResponse(options?: AutterErrorResponseOptions): (error: unknown, req: IncomingMessage, res: ServerResponse, next: (error?: unknown) => void) => void;
620
+ interface FastifyLikeInstance {
621
+ addHook(name: string, hook: (...args: never[]) => unknown): unknown;
622
+ }
623
+ /**
624
+ * Fastify plugin (Fastify 4/5, no dependency on fastify itself):
625
+ *
626
+ * app.register(autterFastify, { ignore: ["/healthz"] });
627
+ *
628
+ * Skips plugin encapsulation, so its hooks cover every route.
629
+ */
630
+ declare function autterFastify(fastify: FastifyLikeInstance, options: AutterRequestsOptions | undefined, done: (error?: Error) => void): void;
631
+ /**
632
+ * Wrap a fetch-style handler `(request, ...rest) => Response` in a request
633
+ * summary. Thrown errors are recorded and reported, then rethrown.
634
+ *
635
+ * export const POST = withRuntimeRequest(handler, { name: "checkout" });
636
+ */
637
+ declare function withRuntimeRequest<A extends unknown[]>(handler: (request: Request, ...rest: A) => Response | Promise<Response>, options?: RuntimeRequestOptions): (request: Request, ...rest: A) => Promise<Response>;
638
+
639
+ /**
640
+ * Server error hooks for Express, Fastify and Koa. Each records the error,
641
+ * with its stack, on the request's span through `captureException`, then
642
+ * lets the framework carry on exactly as before (same error, same response).
643
+ * Without them a thrown error only reaches Autter as the 500 the framework
644
+ * answers with: a stackless `HttpServerError` grouped by route.
645
+ *
646
+ * No framework is a dependency: the request/reply shapes are structural.
647
+ */
648
+ interface AutterErrorHandlerOptions {
649
+ /**
650
+ * Which errors to report. Default: errors with status ≥ 500 (or no
651
+ * status), RuntimeErrors and any error with a valid code — so 4xx errors
652
+ * such as `ctx.throw(404)` or validation failures stay out of your issues.
653
+ * `true` reports every error; a predicate decides per error.
654
+ */
655
+ capture?: boolean | ((error: unknown) => boolean);
656
+ }
657
+ /**
658
+ * Express (4/5) / Connect error middleware that reports the error and calls
659
+ * `next(err)` unchanged. Mount it after your routes and before your own
660
+ * error handlers:
661
+ *
662
+ * app.use(autterErrorHandler());
663
+ * app.use((err, req, res, next) => res.status(500).send("…")); // yours
664
+ *
665
+ * (`autterErrorResponse()` reports too, and also answers the request.)
666
+ */
667
+ declare function autterErrorHandler(options?: AutterErrorHandlerOptions): (error: unknown, req: IncomingMessage, _res: ServerResponse, next: (error?: unknown) => void) => void;
668
+ interface FastifyHookInstance {
669
+ addHook(name: string, hook: (...args: never[]) => unknown): unknown;
670
+ }
671
+ /**
672
+ * Fastify (4/5) `onError` hook that reports the error; Fastify's error
673
+ * handler still builds the response. Register it as a plugin, or call it on
674
+ * the instance directly:
675
+ *
676
+ * app.register(autterFastifyErrors); // or: autterFastifyErrors(app)
677
+ * app.register(autterFastifyErrors, { capture: true });
678
+ *
679
+ * It skips plugin encapsulation, so it covers every route. `autterFastify`
680
+ * (request summaries) already includes the same hook; you don't need both.
681
+ */
682
+ declare function autterFastifyErrors(fastify: FastifyHookInstance, options?: AutterErrorHandlerOptions, done?: (error?: Error) => void): void;
683
+ interface KoaContextLike {
684
+ req?: IncomingMessage;
685
+ }
686
+ /**
687
+ * Koa (2/3) middleware that reports errors thrown downstream and rethrows
688
+ * them, so Koa's own error handling (and `app.on("error")`) is unchanged.
689
+ * Mount it first:
690
+ *
691
+ * app.use(autterKoa());
692
+ */
693
+ declare function autterKoa(options?: AutterErrorHandlerOptions): (ctx: KoaContextLike, next: () => Promise<unknown>) => Promise<void>;
694
+
695
+ /**
696
+ * Unhandled promise rejections, recorded without changing whether the
697
+ * process crashes.
698
+ *
699
+ * Node's `--unhandled-rejections` mode decides what an unhandled rejection
700
+ * does, and most modes behave differently once ANY `unhandledRejection`
701
+ * listener exists:
702
+ *
703
+ * mode no listener with a listener
704
+ * throw (default) crash (uncaught exception) listener only — no crash
705
+ * strict crash, then the listener same
706
+ * warn warning listener + warning
707
+ * warn-with-error-code warning + exit code 1 listener only
708
+ * none nothing listener only
709
+ *
710
+ * Adding our listener would therefore silently stop the default crash. So
711
+ * when ours is the only listener we do what Node would have done:
712
+ *
713
+ * - throw: rethrow the reason (or Node's `UnhandledPromiseRejection`
714
+ * wrapper for a non-error reason) on the next tick. The process crashes
715
+ * with exit code 1 as before, and `uncaughtExceptionMonitor` records it
716
+ * as a crash (fatal), exactly as it did without this listener. The one
717
+ * visible difference: `uncaughtException` listeners and the monitor
718
+ * receive origin `"uncaughtException"` instead of `"unhandledRejection"`.
719
+ * - warn-with-error-code: print Node's warning and set `process.exitCode = 1`.
720
+ * - strict: no listener at all — Node raises the rejection as an uncaught
721
+ * exception first, which the crash monitor already records.
722
+ *
723
+ * Everything else — the app has its own listener (it decides what happens),
724
+ * or the mode never crashes — is recorded as an error (severity "error", not
725
+ * fatal) with `autter.unhandled_rejection: true`.
726
+ */
727
+ type UnhandledRejectionsMode = "throw" | "strict" | "warn" | "warn-with-error-code" | "none";
728
+ /**
729
+ * Node's effective `--unhandled-rejections` mode: `NODE_OPTIONS` first, then
730
+ * the command line (which wins), last occurrence wins. Default `"throw"`.
731
+ */
732
+ declare function unhandledRejectionsMode(execArgv?: readonly string[], nodeOptions?: string | undefined): UnhandledRejectionsMode;
733
+
734
+ /**
735
+ * Logger-only mode: requests, operations, coded errors, sinks and enrichers
736
+ * WITHOUT starting the OpenTelemetry NodeSDK. For apps that already run
737
+ * their own OTel (a second NodeSDK would fight it) or want logs only.
738
+ *
739
+ * initAutterLogging({ apiKey: process.env.AUTTER_RUNTIME_KEY, service: "api" });
740
+ *
741
+ * Exceptions are recorded on the app's active span when there is one
742
+ * (`exceptions: "auto"`); otherwise — or always with `exceptions: "log"` —
743
+ * they become error log records with `exception.*` and
744
+ * `autter.capture.mode = "log"`, which the ingester (1.5.0+) promotes to
745
+ * occurrences. Without an apiKey records go to the console/file sinks only.
746
+ */
747
+ interface AutterLoggingOptions {
748
+ /** Private ingest key (autter_rt_…). Omit to log locally only. */
749
+ apiKey?: string;
750
+ /** Ingester base URL. Default: https://otlp.autter.dev */
751
+ endpoint?: string;
752
+ service: string;
753
+ environment?: string;
754
+ release?: string;
755
+ logging?: RuntimeLoggingOptions;
756
+ /** Same semantics as initAutterServer. Default true. */
757
+ redactAttributes?: boolean | RedactOptions;
758
+ /** Capture crashes via process.uncaughtExceptionMonitor, and unhandled
759
+ * rejections (`captureUnhandledRejections`). Default true. */
760
+ captureGlobalErrors?: boolean;
761
+ /** Same semantics as initAutterServer: record unhandled rejections as
762
+ * errors without changing whether the process crashes. Default true. */
763
+ captureUnhandledRejections?: boolean;
764
+ /** Flush records on beforeExit/SIGINT/SIGTERM. Default true. */
765
+ autoFlush?: boolean;
766
+ debug?: boolean;
767
+ /**
768
+ * "auto" (default): record on the app's active recording span if any,
769
+ * else emit a promoted log record. "log": always emit the log record
770
+ * (use when the app's own spans are not exported to Autter).
771
+ */
772
+ exceptions?: "auto" | "log";
773
+ }
774
+ interface AutterLogging {
775
+ captureException(error: unknown, attributes?: Attributes): void;
776
+ flush(): Promise<void>;
777
+ shutdown(): Promise<void>;
778
+ }
779
+ declare function initAutterLogging(options: AutterLoggingOptions): AutterLogging;
780
+
781
+ /** `user_agent.browser` ("Chrome 120"), `user_agent.os`, `user_agent.device`, `user_agent.bot`. */
782
+ declare function enrichUserAgent(): RuntimeEnricher;
783
+ /** `http.request.body.size` / `http.response.body.size` from Content-Length. */
784
+ declare function enrichRequestSize(): RuntimeEnricher;
785
+ /** `geo.country.iso_code` from CDN/edge headers (Cloudflare, Vercel, CloudFront, Fastly). Country only. */
786
+ declare function enrichEdgeGeo(): RuntimeEnricher;
787
+ /** `deployment.region` and `deployment.commit` from common platform environment variables. */
788
+ declare function enrichDeployment(env?: Record<string, string | undefined>): RuntimeEnricher;
789
+
790
+ /**
791
+ * Version compatibility between Autter Runtime SDKs, this ingester and its
792
+ * ClickHouse schema — the single source of truth.
793
+ *
794
+ * `compat-manifest.json` lists every feature that needs a minimum ingester
795
+ * version, a ClickHouse migration, a dedicated ingest route, or a minimum SDK
796
+ * version. Everything else derives from it:
797
+ *
798
+ * - the ingester serves it (evaluated against its own version and applied
799
+ * migrations) at `GET /v1/compat`;
800
+ * - `@autter/runtime-node` / `@autter/runtime-next` bundle this file at build
801
+ * time to warn once when an enabled feature needs a newer ingester, and to
802
+ * power `npx @autter/runtime-node doctor`;
803
+ * - the Python adapter (`adapters/python/compat.py`) embeds a copy that a
804
+ * parity test keeps identical.
805
+ *
806
+ * This module is dependency-free and side-effect-free on purpose: it is
807
+ * bundled into the Node SDK. Keep it that way.
808
+ *
809
+ * Adding a feature: append an entry to compat-manifest.json with the first
810
+ * ingester release that handles it, the migrations it writes to, its route
811
+ * (if it has its own), and the first SDK release that emits it. The
812
+ * consistency test in compat.test.ts checks ids, migrations and routes, and
813
+ * that no version is ahead of its package's package.json: a feature for the
814
+ * next release bumps the packages it names in the same change.
815
+ */
816
+ interface CompatFeature {
817
+ id: string;
818
+ title: string;
819
+ /** First ingester release that stores this feature correctly. */
820
+ ingester: string;
821
+ /** ClickHouse migrations (migrations.ts ids) the feature writes to. */
822
+ migrations: string[];
823
+ /** Dedicated ingest route, when the feature has one. */
824
+ route?: string;
825
+ /** First release of each SDK package that emits the feature. */
826
+ sdks: Record<string, string>;
827
+ /** Browser payload event types that belong to this feature. */
828
+ browserEvents?: string[];
829
+ }
830
+ interface CompatManifest {
831
+ manifestVersion: number;
832
+ docs: string;
833
+ upgrade: {
834
+ ingester: string;
835
+ sdk: string;
836
+ };
837
+ features: CompatFeature[];
838
+ }
839
+ declare const COMPAT_MANIFEST: CompatManifest;
840
+ /** Response header carrying the ingester version on every ingest response. */
841
+ declare const INGESTER_VERSION_HEADER = "x-autter-ingester-version";
842
+ type SchemaStatus = "ready" | "pending" | "failed" | "unconfigured";
843
+ /** What is known about the ingester an SDK talks to. */
844
+ interface IngesterCompatInfo {
845
+ /** Exact version, or null when the ingester predates /v1/compat. */
846
+ version: string | null;
847
+ /** True when /v1/compat returned 404 (ingester 1.4.0 or older). */
848
+ legacy?: boolean;
849
+ schema?: {
850
+ status: SchemaStatus;
851
+ applied: string[];
852
+ };
853
+ /** Legacy ingesters: route → exists, learned by probing. */
854
+ routes?: Record<string, boolean>;
855
+ }
856
+ type CompatIssueKind = "ingester_too_old" | "schema_not_applied" | "sdk_too_old";
857
+ interface CompatIssue {
858
+ feature: string;
859
+ title: string;
860
+ kind: CompatIssueKind;
861
+ /** Package that must change: the ingester or an SDK package name. */
862
+ component: string;
863
+ required: string;
864
+ /** What was found ("1.3.4", "1.4.0 or older", …). */
865
+ actual: string;
866
+ /** One line: what is wrong, versions, and the fix. */
867
+ message: string;
868
+ fix: string;
869
+ }
870
+ interface SdkIdentity {
871
+ name: string;
872
+ version: string;
873
+ }
874
+ /**
875
+ * Every incompatibility for the features in use. Unknown is never an issue:
876
+ * when the ingester version can't be determined (and a legacy route probe
877
+ * didn't prove a route missing) nothing is reported, so the check has no
878
+ * false positives.
879
+ */
880
+ declare function evaluateCompat(input: {
881
+ features: Iterable<string>;
882
+ ingester: IngesterCompatInfo | null;
883
+ sdk?: SdkIdentity | null;
884
+ manifest?: CompatManifest;
885
+ }): CompatIssue[];
886
+
887
+ /**
888
+ * One-time, fire-and-forget version compatibility check.
889
+ *
890
+ * When a feature that needs a newer ingester is in use (operation logging,
891
+ * memory metrics, …), ask the ingester once — `GET /v1/compat`, or the
892
+ * `x-autter-ingester-version` header on responses the SDK already receives —
893
+ * and print ONE clear warning per incompatible feature naming both versions
894
+ * and the fix. Never throws, never blocks or delays startup or exit (the
895
+ * socket is unref'd), and is silent when versions match or can't be
896
+ * determined. Disable with `compatCheck: false` or AUTTER_COMPAT_CHECK=0.
897
+ */
898
+ declare const SDK_IDENTITY: SdkIdentity;
899
+ /**
900
+ * What the ingester at `endpoint` supports. `null` = unknown (unreachable,
901
+ * auth proxy, 5xx). A 404 on /v1/compat means a pre-compat ingester (1.4.0 or
902
+ * older); `probeRoutes` then learns which feature routes exist (an
903
+ * unauthenticated POST answers 401/503/… when the route exists, 404 when not).
904
+ */
905
+ declare function fetchIngesterCompat(endpoint: string, options?: {
906
+ probeRoutes?: string[];
907
+ unref?: boolean;
908
+ timeoutMs?: number;
909
+ }): Promise<{
910
+ info: IngesterCompatInfo | null;
911
+ report: unknown;
912
+ error?: string;
913
+ }>;
914
+
915
+ /**
916
+ * `npx @autter/runtime-node doctor` — one-shot version compatibility report:
917
+ * installed Autter SDK versions, the ingester's version and ClickHouse schema
918
+ * level, the ingest key (optional), and every feature the installed SDKs can
919
+ * use that the ingester can't store. Exit codes: 0 compatible, 1 a mismatch
920
+ * or rejected key, 2 the ingester could not be reached or identified.
921
+ */
922
+ interface DoctorOptions {
923
+ endpoint?: string;
924
+ key?: string;
925
+ features?: string[];
926
+ json?: boolean;
927
+ cwd?: string;
928
+ timeoutMs?: number;
929
+ }
930
+ interface DoctorReport {
931
+ endpoint: string;
932
+ sdks: SdkIdentity[];
933
+ ingester: IngesterCompatInfo | null;
934
+ ingesterError?: string;
935
+ key?: "accepted" | "rejected" | "client_key" | "storage_unavailable" | "unknown";
936
+ features: Array<{
937
+ id: string;
938
+ title: string;
939
+ status: "ok" | "incompatible" | "unknown";
940
+ }>;
941
+ issues: CompatIssue[];
942
+ exitCode: 0 | 1 | 2;
943
+ }
944
+ declare function runDoctor(options?: DoctorOptions): Promise<DoctorReport>;
945
+
946
+ /**
947
+ * Record a step (type "custom" unless you say otherwise) on the current
948
+ * request. The next exception captured in it carries the last 30. Message
949
+ * and data are scrubbed with your `redact` settings. Never throws.
950
+ *
951
+ * addBreadcrumb({ message: "Loaded cart", data: { items: 3 } });
952
+ */
953
+ declare function addBreadcrumb(input: BreadcrumbInput): void;
954
+
955
+ export { type AutoFlushHandle, type AutoFlushOptions, type AutterErrorHandlerOptions, type AutterErrorResponseOptions, type AutterLogging, type AutterLoggingOptions, type AutterRequestsOptions, type AutterServer, type AutterServerOptions, type AutterSeverity, BreadcrumbInput, COMPAT_MANIFEST, type CompatIssue, type DoctorOptions, type DoctorReport, FileSinkOptions, type FlushTarget, INGESTER_VERSION_HEADER, type IngesterCompatInfo, type InstrumentLlmOptions, type LlmCallHandle, type LlmCallInfo, type LlmUsage, RedactOptions, type RelayOptions, RuntimeCarrier, type RuntimeContextHandle, type RuntimeEnrichContext, type RuntimeEnrichEvent, type RuntimeEnricher, RuntimeLogContext, RuntimeLogLevel, type RuntimeLoggingOptions, type RuntimeOperation, RuntimeOperationKind, type RuntimeOperationOptions, RuntimeOutcome, type RuntimeRequestOptions, RuntimeSink, SDK_IDENTITY, type SafeCapture, type SdkIdentity, type TrackedLlmCall, type UnhandledRejectionsMode, addBreadcrumb, autterErrorHandler, autterErrorResponse, autterFastify, autterFastifyErrors, autterKoa, autterRequests, captureException, captureMessage, captureRequestError, createBrowserRelayFetchHandler, createBrowserRelayHandler, createRuntimeLogger, emitLlmSelftestTrace, enrichDeployment, enrichEdgeGeo, enrichRequestSize, enrichUserAgent, evaluateCompat, fetchIngesterCompat, flushRuntimeLogs, initAutterLogging, initAutterServer, installAutterAutoFlush, instrumentLlmClient, makeSafeCapture, reportOutcome, runDoctor, runInBackground, runtimeContext, runtimeLogStats, runtimeLogger, sanitizeBrowserPayload, startCaughtExceptionSampler, trackLlmCall, unhandledRejectionsMode, withLlmCall, withProcessSpan, withRuntimeOperation, withRuntimeRequest };