@autter/runtime-node 1.6.0 → 1.7.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
@@ -184,6 +184,7 @@ Defaults (cheap by construction):
184
184
  | Request metrics | exported every 60 s |
185
185
  | Logs | not collected |
186
186
  | PII in custom attributes | redacted before export (`redactAttributes`) |
187
+ | Query strings in span URLs | parameter names only (`keepQueryParams`) |
187
188
  | Forgotten `shutdown()` | exporters still flushed on exit (`autoFlush`) |
188
189
 
189
190
  **Error-linked trace retention.** Errors export at 100% while traces are
@@ -316,11 +317,21 @@ silent loss. While wiring things up, set `debug: true` (or `AUTTER_DEBUG=1`)
316
317
  to see `[autter] exported N span(s)` lines on stderr as batches leave.
317
318
 
318
319
  Note on usage rollups: requests are counted from the `http.server.duration`
319
- metric (100% accurate) and additionally from sampled server spans. At the
320
- default 1% sampling the span contribution is negligible; if you set
321
- `traceSampleRate: 1` in development, expect request counts roughly doubled.
322
- Tail-retained error traces don't distort this: their spans carry
323
- `autter.tail_retained` and the ingester keeps them out of span-fed rollups.
320
+ metric (100% accurate), under their route template: Express routes and,
321
+ from 1.7.0, Next.js routes (`/blog/[slug]`, from the `next.route` Next puts
322
+ on its own spans). The SDK marks its resource `autter.metrics_wired` and
323
+ `autter.metrics_routes`, and each request span whose metric got a route
324
+ `autter.metric_has_route`; an up-to-date ingester then counts that request
325
+ from the metric alone, whatever `traceSampleRate` is. A request whose
326
+ metric has no route (Koa, Fastify or plain `http` without a router
327
+ instrumentation) is also counted from its sampled spans, which keep its
328
+ path. Ingesters up to 1.7.0 count every sampled server span on top of the
329
+ metric (at `traceSampleRate: 1`, about twice the requests), except
330
+ tail-retained error traces (`autter.tail_retained`). Since 1.7.0 the SDK
331
+ also applies the HTTP instrumentation to servers created before
332
+ `initAutterServer` (Next.js creates its server before `instrumentation.ts`
333
+ runs): until then a Next.js app got request spans and metrics only from
334
+ the first request that happened to load `http` again, if ever.
324
335
 
325
336
  ### Privacy: secrets and PII are scrubbed before they leave the process
326
337
 
@@ -383,6 +394,48 @@ This closes the server-side gap to match the browser relay's payload
383
394
  whitelist; it is best-effort scrubbing of obvious PII shapes, not a DLP
384
395
  engine — keep secrets out of attributes in the first place.
385
396
 
397
+ **Query strings (1.6.1+).** Query strings carry what a path never should:
398
+ emails, one-time codes, API keys, session ids, search terms. So no span
399
+ leaves the process with query values, whichever instrumentation made it
400
+ (HTTP server and client spans, undici/fetch, Express, Fastify, Next.js's own
401
+ spans):
402
+
403
+ - `http.target`, `http.url`, `url.full` and `url.original` are cut at the `?`
404
+ (and `#`): `/search?email=jane@example.com&page=2` becomes `/search`;
405
+ - `url.query` keeps only the parameter names, `email=[redacted]&page=[redacted]`.
406
+ It is added from the URL when the instrumentation did not set it, so you
407
+ still see which parameters a request used. A parameter without `=`
408
+ (`/verify?<token>`) may itself be the secret and is masked whole;
409
+ - span names lose their query too (Next.js names a request span
410
+ `GET /search?q=…` until it knows the route).
411
+
412
+ List the parameters whose values are safe to keep, such as pagination or
413
+ sorting (exact names, after percent-decoding; kept values are still scrubbed
414
+ by `redactAttributes`):
415
+
416
+ ```ts
417
+ initAutterServer({ ..., keepQueryParams: ["page", "sort"] });
418
+ // url.query: "email=[redacted]&page=2"
419
+ ```
420
+
421
+ This runs at export, like the scrubbing above, and also with
422
+ `redactAttributes: false`. Route templates (`http.route`) and request
423
+ summaries never contained a query.
424
+
425
+ ### One SDK per process, however many copies are loaded
426
+
427
+ Bundlers can load this package more than once: Next.js bundles one copy
428
+ into `instrumentation.ts` and another into each route (unless it is in
429
+ `serverExternalPackages`), and a process can load both the ESM and the CJS
430
+ build. Since 1.6.1 every copy shares one process-wide state
431
+ (`globalThis[Symbol.for("@autter/runtime-node")]`): the copy that ran
432
+ `initAutterServer` (or `initAutterLogging`) is the SDK for all of them, so
433
+ `withRuntimeRequest`, `captureException`, `runtimeLogger` and the rest export
434
+ through it from any copy, and an error one copy reported is never reported
435
+ again by another. Before any copy starts, and after `shutdown()`, every copy
436
+ behaves as before: records go to the console. The shared object is
437
+ versioned; a copy that finds a layout it does not know keeps its own state.
438
+
386
439
  ## 3. LLM tracing
387
440
 
388
441
  For the field contract, manual reporting patterns, and provider-agnostic
@@ -119,6 +119,43 @@ var compat_manifest_default = {
119
119
  ingester: "1.6.0",
120
120
  migrations: [],
121
121
  sdks: { "@autter/runtime-browser": "1.6.0", "@autter/runtime-next": "1.6.0", "@autter/runtime-node": "1.6.0" }
122
+ },
123
+ {
124
+ id: "sourcemap_upload_keys",
125
+ title: "Upload-only keys for source maps (autter_rtu_)",
126
+ ingester: "1.7.0",
127
+ migrations: [],
128
+ route: "/v1/sourcemaps",
129
+ sdks: {}
130
+ },
131
+ {
132
+ id: "source_map_verify",
133
+ title: "Source map check by debug id (autter-runtime sourcemaps verify)",
134
+ ingester: "1.7.0",
135
+ migrations: ["0017-source-maps-debug-id"],
136
+ route: "/v1/sourcemaps/check",
137
+ sdks: {}
138
+ },
139
+ {
140
+ id: "ingest_drop_counts",
141
+ title: "Rejected and dropped records per repository",
142
+ ingester: "1.8.0",
143
+ migrations: ["0018-runtime-ingest-drops"],
144
+ sdks: {}
145
+ },
146
+ {
147
+ id: "telemetry_sender_via",
148
+ title: "How each service's telemetry arrives (SDK, OpenTelemetry Collector or browser relay)",
149
+ ingester: "1.8.0",
150
+ migrations: ["0019-sdk-versions-via"],
151
+ sdks: {}
152
+ },
153
+ {
154
+ id: "frame_resolution_counts",
155
+ title: "Resolved and unresolved browser frames per occurrence",
156
+ ingester: "1.8.0",
157
+ migrations: ["0020-occurrence-frame-resolution"],
158
+ sdks: {}
122
159
  }
123
160
  ]
124
161
  };
@@ -464,7 +501,7 @@ var _https = require('https');
464
501
  // package.json
465
502
  var package_default = {
466
503
  name: "@autter/runtime-node",
467
- version: "1.6.0",
504
+ version: "1.7.0",
468
505
  description: "Autter Runtime for Node.js: request wide events, coded errors, operation logging, same-origin browser relay and a curated OpenTelemetry server tracker",
469
506
  license: "MIT",
470
507
  type: "module",
@@ -539,6 +576,55 @@ var package_default = {
539
576
  ]
540
577
  };
541
578
 
579
+ // src/shared.ts
580
+ var SHARED_STATE_KEY = /* @__PURE__ */ Symbol.for("@autter/runtime-node");
581
+ var SHARED_STATE_LAYOUT = 1;
582
+ function isObject(value) {
583
+ return typeof value === "object" && value !== null;
584
+ }
585
+ var VERSION = package_default.version;
586
+ function resolveShared() {
587
+ const fresh = { v: SHARED_STATE_LAYOUT, sdk: VERSION };
588
+ const host = globalThis;
589
+ let existing;
590
+ try {
591
+ existing = host[SHARED_STATE_KEY];
592
+ if (existing === void 0) {
593
+ Object.defineProperty(host, SHARED_STATE_KEY, {
594
+ value: fresh,
595
+ configurable: true,
596
+ enumerable: false,
597
+ writable: false
598
+ });
599
+ return fresh;
600
+ }
601
+ } catch (e) {
602
+ return fresh;
603
+ }
604
+ if (isObject(existing) && existing.v === SHARED_STATE_LAYOUT) return existing;
605
+ debugLog(
606
+ `another copy of @autter/runtime-node uses shared state layout ${String(isObject(existing) ? existing.v : typeof existing)}; this copy (${VERSION}, layout ${SHARED_STATE_LAYOUT}) keeps its own state`
607
+ );
608
+ return fresh;
609
+ }
610
+ var shared = resolveShared();
611
+ function sharedSlot(name, init) {
612
+ const fresh = init();
613
+ try {
614
+ const existing = shared[name];
615
+ if (!isObject(existing)) {
616
+ shared[name] = fresh;
617
+ return fresh;
618
+ }
619
+ for (const [key, value] of Object.entries(fresh)) {
620
+ if (existing[key] === void 0) existing[key] = value;
621
+ }
622
+ return existing;
623
+ } catch (e2) {
624
+ return fresh;
625
+ }
626
+ }
627
+
542
628
  // src/compat.ts
543
629
  var SDK_IDENTITY = {
544
630
  name: "@autter/runtime-node",
@@ -587,7 +673,7 @@ function compatRequest(method, url, options = {}) {
587
673
  if (options.unref !== false) req.on("socket", (socket) => socket.unref());
588
674
  req.on("error", () => done(null));
589
675
  req.end(options.body);
590
- } catch (e) {
676
+ } catch (e3) {
591
677
  done(null);
592
678
  }
593
679
  });
@@ -600,7 +686,7 @@ async function fetchIngesterCompat(endpoint, options = {}) {
600
686
  let report = null;
601
687
  try {
602
688
  report = JSON.parse(res.body);
603
- } catch (e2) {
689
+ } catch (e4) {
604
690
  }
605
691
  const info = _nullishCoalesce(ingesterInfoFromReport(report), () => ( headerInfo(res.headers)));
606
692
  return info ? { info, report } : { info: null, report, error: "unrecognised /v1/compat response" };
@@ -631,7 +717,7 @@ function envDisabled() {
631
717
  return value === "0" || value === "false" || value === "off";
632
718
  }
633
719
  var defaultWarn = (message) => console.warn(`[autter-runtime] ${message}`);
634
- var state = freshState(null, false);
720
+ var compat = sharedSlot("compat", () => ({ state: freshState(null, false) }));
635
721
  function freshState(endpoint, enabled, sdk = SDK_IDENTITY, warn = defaultWarn) {
636
722
  return {
637
723
  endpoint,
@@ -646,7 +732,7 @@ function freshState(endpoint, enabled, sdk = SDK_IDENTITY, warn = defaultWarn) {
646
732
  };
647
733
  }
648
734
  function configureCompatCheck(options) {
649
- state = freshState(
735
+ compat.state = freshState(
650
736
  options.endpoint.replace(/\/$/, ""),
651
737
  options.enabled !== false && !envDisabled(),
652
738
  _nullishCoalesce(options.sdk, () => ( SDK_IDENTITY)),
@@ -654,23 +740,23 @@ function configureCompatCheck(options) {
654
740
  );
655
741
  }
656
742
  function ensureCompatConfigured(endpoint) {
657
- if (state.endpoint === null && !envDisabled()) {
658
- state = freshState(endpoint.replace(/\/$/, ""), true);
743
+ if (compat.state.endpoint === null && !envDisabled()) {
744
+ compat.state = freshState(endpoint.replace(/\/$/, ""), true);
659
745
  }
660
746
  }
661
747
  function noteCompatFeature(id) {
662
748
  try {
663
- if (!state.enabled || state.features.has(id) || !featureById(id)) return;
664
- state.features.add(id);
665
- if (state.info) {
749
+ if (!compat.state.enabled || compat.state.features.has(id) || !featureById(id)) return;
750
+ compat.state.features.add(id);
751
+ if (compat.state.info) {
666
752
  evaluateAndWarn();
667
753
  return;
668
754
  }
669
- if (state.started) return;
670
- state.started = true;
671
- const current = state;
755
+ if (compat.state.started) return;
756
+ compat.state.started = true;
757
+ const current = compat.state;
672
758
  setTimeout(() => void runCheck(current), 0).unref();
673
- } catch (e3) {
759
+ } catch (e5) {
674
760
  }
675
761
  }
676
762
  async function runCheck(current) {
@@ -679,7 +765,7 @@ async function runCheck(current) {
679
765
  const { info, error } = await fetchIngesterCompat(current.endpoint, {
680
766
  probeRoutes: routesFor(current.features)
681
767
  });
682
- if (state !== current) return;
768
+ if (compat.state !== current) return;
683
769
  if (!info) {
684
770
  debugLog(`compat check skipped: ${_nullishCoalesce(error, () => ( "ingester version unknown"))}`);
685
771
  return;
@@ -703,8 +789,8 @@ function routesFor(features) {
703
789
  return routes;
704
790
  }
705
791
  function mergeInfo(next) {
706
- const prev = state.info;
707
- state.info = {
792
+ const prev = compat.state.info;
793
+ compat.state.info = {
708
794
  version: _nullishCoalesce(_nullishCoalesce(next.version, () => ( _optionalChain([prev, 'optionalAccess', _21 => _21.version]))), () => ( null)),
709
795
  ...next.legacy || _optionalChain([prev, 'optionalAccess', _22 => _22.legacy]) ? { legacy: true } : {},
710
796
  ..._nullishCoalesce(next.schema, () => ( _optionalChain([prev, 'optionalAccess', _23 => _23.schema]))) ? { schema: _nullishCoalesce(next.schema, () => ( _optionalChain([prev, 'optionalAccess', _24 => _24.schema]))) } : {},
@@ -712,17 +798,17 @@ function mergeInfo(next) {
712
798
  };
713
799
  }
714
800
  function evaluateAndWarn() {
715
- const info = state.info;
801
+ const info = compat.state.info;
716
802
  if (!info) return;
717
- if (info.version === null && state.endpoint) {
718
- const unprobed = routesFor(state.features).filter(
719
- (route) => _optionalChain([info, 'access', _26 => _26.routes, 'optionalAccess', _27 => _27[route]]) === void 0 && !state.pendingProbes.has(route)
803
+ if (info.version === null && compat.state.endpoint) {
804
+ const unprobed = routesFor(compat.state.features).filter(
805
+ (route) => _optionalChain([info, 'access', _26 => _26.routes, 'optionalAccess', _27 => _27[route]]) === void 0 && !compat.state.pendingProbes.has(route)
720
806
  );
721
807
  if (unprobed.length) {
722
- const current = state;
808
+ const current = compat.state;
723
809
  for (const route of unprobed) current.pendingProbes.add(route);
724
810
  void Promise.all(unprobed.map((route) => probeRoute(current.endpoint, route, {}).then((exists) => [route, exists]))).then((results) => {
725
- if (state !== current) return;
811
+ if (compat.state !== current) return;
726
812
  const routes = {};
727
813
  for (const [route, exists] of results) if (exists !== null) routes[route] = exists;
728
814
  mergeInfo({ version: null, legacy: true, routes });
@@ -731,56 +817,56 @@ function evaluateAndWarn() {
731
817
  });
732
818
  }
733
819
  }
734
- for (const issue of evaluateCompat({ features: state.features, ingester: info })) {
820
+ for (const issue of evaluateCompat({ features: compat.state.features, ingester: info })) {
735
821
  warnOnce(issue);
736
822
  }
737
823
  }
738
824
  function warnOnce(issue) {
739
825
  const key = `${issue.feature}:${issue.kind}`;
740
- if (state.warned.has(key)) return;
741
- state.warned.add(key);
826
+ if (compat.state.warned.has(key)) return;
827
+ compat.state.warned.add(key);
742
828
  try {
743
- state.warn(issue.message);
744
- } catch (e4) {
829
+ compat.state.warn(issue.message);
830
+ } catch (e6) {
745
831
  }
746
832
  }
747
833
  function observeIngesterResponse(response) {
748
834
  try {
749
- if (!state.enabled) return;
835
+ if (!compat.state.enabled) return;
750
836
  const fromHeader = headerInfo(response.headers);
751
837
  if (fromHeader) {
752
- if (_optionalChain([state, 'access', _28 => _28.info, 'optionalAccess', _29 => _29.version]) !== fromHeader.version) {
838
+ if (_optionalChain([compat, 'access', _28 => _28.state, 'access', _29 => _29.info, 'optionalAccess', _30 => _30.version]) !== fromHeader.version) {
753
839
  mergeInfo(fromHeader);
754
840
  evaluateAndWarn();
755
841
  }
756
842
  return;
757
843
  }
758
- if (response.status === 404 && response.route && !_optionalChain([state, 'access', _30 => _30.info, 'optionalAccess', _31 => _31.version])) {
844
+ if (response.status === 404 && response.route && !_optionalChain([compat, 'access', _31 => _31.state, 'access', _32 => _32.info, 'optionalAccess', _33 => _33.version])) {
759
845
  mergeInfo({ version: null, legacy: true, routes: { [response.route]: false } });
760
846
  evaluateAndWarn();
761
847
  }
762
- } catch (e5) {
848
+ } catch (e7) {
763
849
  }
764
850
  }
765
851
  function noteRelayRejection(eventTypes, status, headers) {
766
852
  try {
767
- if (!state.enabled || status !== 400 || headerInfo(headers)) return [];
853
+ if (!compat.state.enabled || status !== 400 || headerInfo(headers)) return [];
768
854
  const named = [];
769
855
  for (const id of featuresForBrowserEvents(eventTypes)) {
770
856
  const feature = featureById(id);
771
857
  const key = `${id}:relay_rejected`;
772
- if (state.warned.has(key)) continue;
773
- state.warned.add(key);
858
+ if (compat.state.warned.has(key)) continue;
859
+ compat.state.warned.add(key);
774
860
  named.push(id);
775
861
  try {
776
- state.warn(
862
+ compat.state.warn(
777
863
  `The ingester rejected a browser batch containing ${feature.title.toLowerCase()} events. ${feature.title} needs ingester >= ${feature.ingester}; yours is 1.4.0 or older (it predates version reporting), so those batches are dropped. ${ingesterUpgradeHint(COMPAT_MANIFEST)}`
778
864
  );
779
- } catch (e6) {
865
+ } catch (e8) {
780
866
  }
781
867
  }
782
868
  return named;
783
- } catch (e7) {
869
+ } catch (e9) {
784
870
  return [];
785
871
  }
786
872
  }
@@ -807,4 +893,5 @@ function noteRelayRejection(eventTypes, status, headers) {
807
893
 
808
894
 
809
895
 
810
- exports.COMPAT_MANIFEST = COMPAT_MANIFEST; exports.INGESTER_VERSION_HEADER = INGESTER_VERSION_HEADER; exports.versionAtLeast = versionAtLeast; exports.featuresForBrowserEvents = featuresForBrowserEvents; exports.evaluateCompat = evaluateCompat; exports.setDebugMode = setDebugMode; exports.isDebugEnabled = isDebugEnabled; exports.debugLog = debugLog; exports.telemetryStats = telemetryStats; exports.CountingExporter = CountingExporter; exports.registerFlushTarget = registerFlushTarget; exports.unregisterFlushTargets = unregisterFlushTargets; exports.installAutterAutoFlush = installAutterAutoFlush; exports.SDK_IDENTITY = SDK_IDENTITY; exports.compatRequest = compatRequest; exports.fetchIngesterCompat = fetchIngesterCompat; exports.configureCompatCheck = configureCompatCheck; exports.ensureCompatConfigured = ensureCompatConfigured; exports.noteCompatFeature = noteCompatFeature; exports.observeIngesterResponse = observeIngesterResponse; exports.noteRelayRejection = noteRelayRejection;
896
+
897
+ exports.COMPAT_MANIFEST = COMPAT_MANIFEST; exports.INGESTER_VERSION_HEADER = INGESTER_VERSION_HEADER; exports.versionAtLeast = versionAtLeast; exports.featuresForBrowserEvents = featuresForBrowserEvents; exports.evaluateCompat = evaluateCompat; exports.setDebugMode = setDebugMode; exports.isDebugEnabled = isDebugEnabled; exports.debugLog = debugLog; exports.telemetryStats = telemetryStats; exports.CountingExporter = CountingExporter; exports.registerFlushTarget = registerFlushTarget; exports.unregisterFlushTargets = unregisterFlushTargets; exports.installAutterAutoFlush = installAutterAutoFlush; exports.sharedSlot = sharedSlot; exports.SDK_IDENTITY = SDK_IDENTITY; exports.compatRequest = compatRequest; exports.fetchIngesterCompat = fetchIngesterCompat; exports.configureCompatCheck = configureCompatCheck; exports.ensureCompatConfigured = ensureCompatConfigured; exports.noteCompatFeature = noteCompatFeature; exports.observeIngesterResponse = observeIngesterResponse; exports.noteRelayRejection = noteRelayRejection;
@@ -5,7 +5,7 @@
5
5
 
6
6
 
7
7
 
8
- var _chunkARN26OK6cjs = require('./chunk-ARN26OK6.cjs');
8
+ var _chunkGYTQLITWcjs = require('./chunk-GYTQLITW.cjs');
9
9
 
10
10
  // src/doctor.ts
11
11
  var _fs = require('fs');
@@ -30,19 +30,19 @@ function installedSdks(cwd) {
30
30
  }
31
31
  if (_path.dirname.call(void 0, dir) === dir) break;
32
32
  }
33
- if (!found.has(_chunkARN26OK6cjs.SDK_IDENTITY.name)) found.set(_chunkARN26OK6cjs.SDK_IDENTITY.name, _chunkARN26OK6cjs.SDK_IDENTITY.version);
33
+ if (!found.has(_chunkGYTQLITWcjs.SDK_IDENTITY.name)) found.set(_chunkGYTQLITWcjs.SDK_IDENTITY.name, _chunkGYTQLITWcjs.SDK_IDENTITY.version);
34
34
  return [...found].map(([name, version]) => ({ name, version }));
35
35
  }
36
36
  function featuresInUse(sdks) {
37
- return _chunkARN26OK6cjs.COMPAT_MANIFEST.features.filter(
37
+ return _chunkGYTQLITWcjs.COMPAT_MANIFEST.features.filter(
38
38
  (feature) => sdks.some((sdk) => {
39
39
  const min = feature.sdks[sdk.name];
40
- return min !== void 0 && _chunkARN26OK6cjs.versionAtLeast.call(void 0, sdk.version, min) === true;
40
+ return min !== void 0 && _chunkGYTQLITWcjs.versionAtLeast.call(void 0, sdk.version, min) === true;
41
41
  })
42
42
  ).map((feature) => feature.id);
43
43
  }
44
44
  async function checkKey(endpoint, key, timeoutMs) {
45
- const res = await _chunkARN26OK6cjs.compatRequest.call(void 0, "POST", `${endpoint}/v1/traces`, {
45
+ const res = await _chunkGYTQLITWcjs.compatRequest.call(void 0, "POST", `${endpoint}/v1/traces`, {
46
46
  body: JSON.stringify({ resourceSpans: [] }),
47
47
  headers: { authorization: `Bearer ${key}` },
48
48
  unref: false,
@@ -62,10 +62,10 @@ async function runDoctor(options = {}) {
62
62
  const features = _optionalChain([options, 'access', _ => _.features, 'optionalAccess', _2 => _2.length]) ? options.features : featuresInUse(sdks);
63
63
  const routes = [
64
64
  ...new Set(
65
- features.map((id) => _optionalChain([_chunkARN26OK6cjs.COMPAT_MANIFEST, 'access', _3 => _3.features, 'access', _4 => _4.find, 'call', _5 => _5((f) => f.id === id), 'optionalAccess', _6 => _6.route])).filter((route) => Boolean(route))
65
+ features.map((id) => _optionalChain([_chunkGYTQLITWcjs.COMPAT_MANIFEST, 'access', _3 => _3.features, 'access', _4 => _4.find, 'call', _5 => _5((f) => f.id === id), 'optionalAccess', _6 => _6.route])).filter((route) => Boolean(route))
66
66
  )
67
67
  ];
68
- const { info, error } = await _chunkARN26OK6cjs.fetchIngesterCompat.call(void 0, endpoint, { probeRoutes: routes, unref: false, timeoutMs });
68
+ const { info, error } = await _chunkGYTQLITWcjs.fetchIngesterCompat.call(void 0, endpoint, { probeRoutes: routes, unref: false, timeoutMs });
69
69
  const issues = [];
70
70
  const seen = /* @__PURE__ */ new Set();
71
71
  const add = (issue) => {
@@ -75,15 +75,15 @@ async function runDoctor(options = {}) {
75
75
  issues.push(issue);
76
76
  }
77
77
  };
78
- _chunkARN26OK6cjs.evaluateCompat.call(void 0, { features, ingester: info }).forEach(add);
78
+ _chunkGYTQLITWcjs.evaluateCompat.call(void 0, { features, ingester: info }).forEach(add);
79
79
  for (const sdk of sdks) {
80
80
  if (_optionalChain([options, 'access', _7 => _7.features, 'optionalAccess', _8 => _8.length])) {
81
- _chunkARN26OK6cjs.evaluateCompat.call(void 0, { features: options.features, ingester: null, sdk }).forEach(add);
81
+ _chunkGYTQLITWcjs.evaluateCompat.call(void 0, { features: options.features, ingester: null, sdk }).forEach(add);
82
82
  }
83
83
  }
84
84
  const key = options.key ? await checkKey(endpoint, options.key, timeoutMs) : void 0;
85
85
  const featureRows = features.flatMap((id) => {
86
- const feature = _chunkARN26OK6cjs.COMPAT_MANIFEST.features.find((f) => f.id === id);
86
+ const feature = _chunkGYTQLITWcjs.COMPAT_MANIFEST.features.find((f) => f.id === id);
87
87
  if (!feature) return [];
88
88
  const bad = issues.some((issue) => issue.feature === id);
89
89
  const known = _optionalChain([info, 'optionalAccess', _9 => _9.version]) != null || feature.route !== void 0 && _optionalChain([info, 'optionalAccess', _10 => _10.routes, 'optionalAccess', _11 => _11[feature.route]]) !== void 0;
@@ -153,7 +153,7 @@ Checks that your Autter SDKs, ingester and ClickHouse schema versions match.
153
153
  Options:
154
154
  --endpoint <url> Ingester base URL (default: $AUTTER_ENDPOINT, $OTEL_EXPORTER_OTLP_ENDPOINT, or ${DEFAULT_ENDPOINT})
155
155
  --key <key> Also verify an ingest key (default: $AUTTER_RUNTIME_KEY)
156
- --features <a,b> Only check these features (${_chunkARN26OK6cjs.COMPAT_MANIFEST.features.map((f) => f.id).join(", ")})
156
+ --features <a,b> Only check these features (${_chunkGYTQLITWcjs.COMPAT_MANIFEST.features.map((f) => f.id).join(", ")})
157
157
  --json Machine-readable output
158
158
  --timeout <ms> Per-request timeout (default 5000)
159
159
 
@@ -1,7 +1,8 @@
1
1
  import {
2
2
  noteCompatFeature,
3
- observeIngesterResponse
4
- } from "./chunk-OY2HTUWY.js";
3
+ observeIngesterResponse,
4
+ sharedSlot
5
+ } from "./chunk-LG52CW3V.js";
5
6
 
6
7
  // ../runtime-core/dist/index.js
7
8
  var SENSITIVE_KEY_PATTERNS = [
@@ -993,7 +994,7 @@ import {
993
994
  unlinkSync
994
995
  } from "fs";
995
996
  import { join, resolve } from "path";
996
- var sinkStats = { dropped: 0 };
997
+ var sinkStats = sharedSlot("sinks", () => ({ dropped: 0 }));
997
998
  function otlpSink(options = {}) {
998
999
  let context = null;
999
1000
  let queue = [];
@@ -1362,18 +1363,22 @@ function memorySink() {
1362
1363
  }
1363
1364
 
1364
1365
  // src/logger.ts
1365
- var local = new AsyncLocalStorage();
1366
- var config = null;
1367
- var sinks = [];
1368
- var extraSinks = /* @__PURE__ */ new Set();
1366
+ var shared = sharedSlot("logger", () => ({
1367
+ local: new AsyncLocalStorage(),
1368
+ config: null,
1369
+ sinks: [],
1370
+ extraSinks: /* @__PURE__ */ new Set()
1371
+ }));
1372
+ var local = shared.local;
1373
+ var extraSinks = shared.extraSinks;
1369
1374
  var fallbackConsole = null;
1370
1375
  function redact(attributes) {
1371
- return config?.redact(attributes) ?? redactAttributes(attributes);
1376
+ return shared.config?.redact(attributes) ?? redactAttributes(attributes);
1372
1377
  }
1373
1378
  var safe = (attributes = {}) => boundContext(attributes, redact);
1374
1379
  var merge = (current, next) => mergeContext(current, next, redact);
1375
1380
  function activeSinks() {
1376
- if (config) return extraSinks.size ? [...sinks, ...extraSinks] : sinks;
1381
+ if (shared.config) return extraSinks.size ? [...shared.sinks, ...extraSinks] : shared.sinks;
1377
1382
  if (extraSinks.size) return [...extraSinks];
1378
1383
  fallbackConsole ??= consoleSink();
1379
1384
  return [fallbackConsole];
@@ -1410,9 +1415,9 @@ function emit(level, message, attributes, extra = {}, options = {}) {
1410
1415
  if (state?.sealed) return;
1411
1416
  const summary = extra["autter.event.type"] === "operation";
1412
1417
  if (!summary) recordLogBreadcrumb(level, message, attributes, state);
1413
- if (!summary && LEVEL_SEVERITY[level] < LEVEL_SEVERITY[config?.options?.minLevel ?? "debug"])
1418
+ if (!summary && LEVEL_SEVERITY[level] < LEVEL_SEVERITY[shared.config?.options?.minLevel ?? "debug"])
1414
1419
  return;
1415
- if (!summary && state && config?.options?.inline !== false) {
1420
+ if (!summary && state && shared.config?.options?.inline !== false) {
1416
1421
  const { "exception.stacktrace": _stack, ...inlineAttrs } = userContext(
1417
1422
  attributes,
1418
1423
  redact
@@ -1432,7 +1437,7 @@ function emit(level, message, attributes, extra = {}, options = {}) {
1432
1437
  ...operationAttributes(state),
1433
1438
  ...user
1434
1439
  };
1435
- const enrichers = config?.options?.enrich;
1440
+ const enrichers = shared.config?.options?.enrich;
1436
1441
  if (summary && state && enrichers?.length) {
1437
1442
  const event2 = { level, message, attributes: raw };
1438
1443
  for (const enrich of enrichers) {
@@ -1460,9 +1465,9 @@ function emit(level, message, attributes, extra = {}, options = {}) {
1460
1465
  }
1461
1466
  }
1462
1467
  function configureRuntimeLogger(next) {
1463
- config = next;
1464
- sinks = defaultSinks(next);
1465
- for (const sink of sinks) {
1468
+ shared.config = next;
1469
+ shared.sinks = defaultSinks(next);
1470
+ for (const sink of shared.sinks) {
1466
1471
  try {
1467
1472
  sink.start?.({
1468
1473
  endpoint: next.endpoint,
@@ -1477,14 +1482,14 @@ function configureRuntimeLogger(next) {
1477
1482
  }
1478
1483
  }
1479
1484
  function loggerConfigured() {
1480
- return config;
1485
+ return shared.config;
1481
1486
  }
1482
1487
  function addRuntimeSink(sink) {
1483
1488
  extraSinks.add(sink);
1484
1489
  return () => extraSinks.delete(sink);
1485
1490
  }
1486
1491
  function notifyException(error, attributes = {}) {
1487
- if (!extraSinks.size && !sinks.some((sink) => sink.exception)) return;
1492
+ if (!extraSinks.size && !shared.sinks.some((sink) => sink.exception)) return;
1488
1493
  const state = local.getStore();
1489
1494
  const record = {
1490
1495
  time: Date.now(),
@@ -1495,7 +1500,7 @@ function notifyException(error, attributes = {}) {
1495
1500
  ...state ? { operationId: state.id } : {},
1496
1501
  ...state?.requestId ? { requestId: state.requestId } : {}
1497
1502
  };
1498
- for (const sink of [...sinks, ...extraSinks]) {
1503
+ for (const sink of [...shared.sinks, ...extraSinks]) {
1499
1504
  try {
1500
1505
  sink.exception?.(record);
1501
1506
  } catch {
@@ -1504,25 +1509,25 @@ function notifyException(error, attributes = {}) {
1504
1509
  }
1505
1510
  function runtimeLogStats() {
1506
1511
  return {
1507
- buffered: sinks.reduce((sum, sink) => sum + (sink.buffered?.() ?? 0), 0),
1512
+ buffered: shared.sinks.reduce((sum, sink) => sum + (sink.buffered?.() ?? 0), 0),
1508
1513
  dropped: sinkStats.dropped
1509
1514
  };
1510
1515
  }
1511
1516
  async function flushRuntimeLogs() {
1512
- if (!config) return;
1517
+ if (!shared.config) return;
1513
1518
  const results = await Promise.allSettled(
1514
- sinks.map((sink) => Promise.resolve().then(() => sink.flush?.()))
1519
+ shared.sinks.map((sink) => Promise.resolve().then(() => sink.flush?.()))
1515
1520
  );
1516
1521
  const failed = results.find((result) => result.status === "rejected");
1517
1522
  if (failed) throw failed.reason;
1518
1523
  }
1519
1524
  async function shutdownRuntimeLogger() {
1520
- const active = sinks;
1525
+ const active = shared.sinks;
1521
1526
  const results = await Promise.allSettled(
1522
1527
  active.map((sink) => Promise.resolve().then(() => sink.shutdown?.()))
1523
1528
  );
1524
- config = null;
1525
- sinks = [];
1529
+ shared.config = null;
1530
+ shared.sinks = [];
1526
1531
  const failed = results.find((result) => result.status === "rejected");
1527
1532
  if (failed) throw failed.reason;
1528
1533
  }
@@ -1674,7 +1679,7 @@ function emitSummary(state, options = {}) {
1674
1679
  }
1675
1680
  function reportExplicitFailure(state) {
1676
1681
  if (state.outcome === "failed" && state.explicitOutcome && !state.errorThrown)
1677
- config?.reportOutcome(state.name, state.message ?? "Operation failed", {
1682
+ shared.config?.reportOutcome(state.name, state.message ?? "Operation failed", {
1678
1683
  ...traceAttributes(state),
1679
1684
  "autter.outcome.stack": state.reportingStack ?? ""
1680
1685
  });
@@ -1708,7 +1713,7 @@ function startOperation(name, fn, attributes, options, internal = {}) {
1708
1713
  thrown = error;
1709
1714
  noteOperationError(error, true, state);
1710
1715
  rememberThrownBreadcrumbs(error, state);
1711
- config?.captureThrown?.(error);
1716
+ shared.config?.captureThrown?.(error);
1712
1717
  throw error;
1713
1718
  } finally {
1714
1719
  trace.getActiveSpan()?.setAttributes({
@@ -1717,7 +1722,7 @@ function startOperation(name, fn, attributes, options, internal = {}) {
1717
1722
  "autter.operation.kind": state.kind
1718
1723
  });
1719
1724
  if (state.outcome === "failed" && !thrown)
1720
- config?.reportOutcome(
1725
+ shared.config?.reportOutcome(
1721
1726
  state.name,
1722
1727
  state.message ?? "Operation failed",
1723
1728
  {
@@ -1738,7 +1743,7 @@ function startOperation(name, fn, attributes, options, internal = {}) {
1738
1743
  }
1739
1744
  }
1740
1745
  ] : [];
1741
- const promise = config?.run ? config.run(state.name, run, traceAttributes(state), links) : run();
1746
+ const promise = shared.config?.run ? shared.config.run(state.name, run, traceAttributes(state), links) : run();
1742
1747
  if (!options.waitUntil) return promise;
1743
1748
  const waitUntil = options.waitUntil;
1744
1749
  return promise.finally(() => {
@@ -1815,9 +1820,14 @@ var runtimeContext = {
1815
1820
  // src/breadcrumbs.ts
1816
1821
  var INVALID_TRACE_ID = "00000000000000000000000000000000";
1817
1822
  var MAX_TRACE_TRAILS = 200;
1818
- var byTrace = /* @__PURE__ */ new Map();
1819
- var processTrail = new BreadcrumbTrail();
1820
- var thrownTrails = /* @__PURE__ */ new WeakMap();
1823
+ var trails = sharedSlot("breadcrumbs", () => ({
1824
+ byTrace: /* @__PURE__ */ new Map(),
1825
+ process: new BreadcrumbTrail(),
1826
+ thrown: /* @__PURE__ */ new WeakMap()
1827
+ }));
1828
+ var byTrace = trails.byTrace;
1829
+ var processTrail = trails.process;
1830
+ var thrownTrails = trails.thrown;
1821
1831
  function redact2(attributes) {
1822
1832
  return loggerConfigured()?.redact(attributes) ?? redactAttributes(attributes);
1823
1833
  }