autotel 4.2.0 → 4.2.2

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.
Files changed (111) hide show
  1. package/README.md +50 -0
  2. package/dist/auto.cjs +5 -3
  3. package/dist/auto.cjs.map +1 -1
  4. package/dist/auto.js +3 -3
  5. package/dist/auto.js.map +1 -1
  6. package/dist/chunk-C_NdSu1c.cjs +34 -0
  7. package/dist/correlation-id.cjs +1 -1
  8. package/dist/correlation-id.d.cts.map +1 -1
  9. package/dist/correlation-id.d.ts.map +1 -1
  10. package/dist/correlation-id.js +1 -1
  11. package/dist/decorators.cjs +1 -1
  12. package/dist/decorators.js +1 -1
  13. package/dist/diagnostics.cjs +279 -0
  14. package/dist/diagnostics.cjs.map +1 -0
  15. package/dist/diagnostics.d.cts +83 -0
  16. package/dist/diagnostics.d.cts.map +1 -0
  17. package/dist/diagnostics.d.ts +83 -0
  18. package/dist/diagnostics.d.ts.map +1 -0
  19. package/dist/diagnostics.js +274 -0
  20. package/dist/diagnostics.js.map +1 -0
  21. package/dist/{event-ByBTV9M2.js → event-531asIM6.js} +4 -4
  22. package/dist/{event-ByBTV9M2.js.map → event-531asIM6.js.map} +1 -1
  23. package/dist/{event-BhHREDJk.cjs → event-CcZYwp50.cjs} +4 -4
  24. package/dist/{event-BhHREDJk.cjs.map → event-CcZYwp50.cjs.map} +1 -1
  25. package/dist/event.cjs +1 -1
  26. package/dist/event.js +1 -1
  27. package/dist/{functional-zpzNLhky.cjs → functional-C8B0Qa7o.cjs} +10 -7
  28. package/dist/functional-C8B0Qa7o.cjs.map +1 -0
  29. package/dist/{functional-DtI0u4vx.js → functional-r-AUIRy_.js} +9 -9
  30. package/dist/functional-r-AUIRy_.js.map +1 -0
  31. package/dist/functional.cjs +1 -1
  32. package/dist/functional.js +1 -1
  33. package/dist/http.cjs +1 -1
  34. package/dist/http.js +1 -1
  35. package/dist/index.cjs +15 -13
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.d.cts.map +1 -1
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +14 -14
  40. package/dist/index.js.map +1 -1
  41. package/dist/{init-D-jnNMix.js → init-BS2JVkrL.js} +2 -2
  42. package/dist/{init-D-jnNMix.js.map → init-BS2JVkrL.js.map} +1 -1
  43. package/dist/{init-BX7AmFRl.cjs → init-BXiuPK6j.cjs} +3 -3
  44. package/dist/{init-BX7AmFRl.cjs.map → init-BXiuPK6j.cjs.map} +1 -1
  45. package/dist/instrumentation.cjs +2 -2
  46. package/dist/instrumentation.js +2 -2
  47. package/dist/logger.cjs +236 -8
  48. package/dist/logger.cjs.map +1 -0
  49. package/dist/messaging.cjs +1 -1
  50. package/dist/messaging.js +1 -1
  51. package/dist/{node-require-DF5QBX6z.cjs → node-require-CZ_PU448.cjs} +6 -4
  52. package/dist/node-require-CZ_PU448.cjs.map +1 -0
  53. package/dist/{node-require-Db1oDpLj.js → node-require-vROmTeJ8.js} +5 -5
  54. package/dist/node-require-vROmTeJ8.js.map +1 -0
  55. package/dist/{operation-context-C-2hmmtP.js → operation-context-CKBoA4Qy.js} +3 -3
  56. package/dist/operation-context-CKBoA4Qy.js.map +1 -0
  57. package/dist/{operation-context-n4_obUwq.cjs → operation-context-D6LDf4W_.cjs} +3 -1
  58. package/dist/operation-context-D6LDf4W_.cjs.map +1 -0
  59. package/dist/register.cjs +3 -1
  60. package/dist/register.cjs.map +1 -1
  61. package/dist/register.js +2 -2
  62. package/dist/register.js.map +1 -1
  63. package/dist/semantic-helpers.cjs +1 -1
  64. package/dist/semantic-helpers.js +1 -1
  65. package/dist/{stable-hash-Cg5cT34Q.js → stable-hash-ChFBIhNt.js} +3 -3
  66. package/dist/stable-hash-ChFBIhNt.js.map +1 -0
  67. package/dist/{stable-hash-BNTMrmdB.cjs → stable-hash-brKISGf1.cjs} +4 -2
  68. package/dist/stable-hash-brKISGf1.cjs.map +1 -0
  69. package/dist/trace-context-Cijqoi6e.d.cts.map +1 -1
  70. package/dist/trace-context-Cijqoi6e.d.ts.map +1 -1
  71. package/dist/trace-helpers.cjs +1 -1
  72. package/dist/trace-helpers.js +1 -1
  73. package/dist/{track-wc0HafS_.js → track-COUuU48p.js} +5 -5
  74. package/dist/track-COUuU48p.js.map +1 -0
  75. package/dist/{track-D59FfpL0.cjs → track-Cb3Q4QmS.cjs} +4 -2
  76. package/dist/track-Cb3Q4QmS.cjs.map +1 -0
  77. package/dist/validate.cjs +1 -1
  78. package/dist/validate.js +1 -1
  79. package/dist/webhook.cjs +1 -1
  80. package/dist/webhook.js +1 -1
  81. package/dist/workflow-distributed.cjs +1 -1
  82. package/dist/workflow-distributed.js +1 -1
  83. package/dist/workflow.cjs +3 -1
  84. package/dist/workflow.cjs.map +1 -1
  85. package/dist/workflow.d.cts.map +1 -1
  86. package/dist/workflow.d.ts.map +1 -1
  87. package/dist/workflow.js +3 -3
  88. package/dist/workflow.js.map +1 -1
  89. package/dist/yaml-config.cjs +233 -4
  90. package/dist/yaml-config.cjs.map +1 -0
  91. package/dist/yaml-config.d.cts.map +1 -1
  92. package/dist/yaml-config.d.ts.map +1 -1
  93. package/dist/yaml-config.js +8 -7
  94. package/dist/yaml-config.js.map +1 -1
  95. package/package.json +6 -1
  96. package/skills/review-otel-patterns/SKILL.md +11 -8
  97. package/skills/review-otel-patterns/references/code-review.md +4 -3
  98. package/dist/functional-DtI0u4vx.js.map +0 -1
  99. package/dist/functional-zpzNLhky.cjs.map +0 -1
  100. package/dist/logger-thMPLpOG.cjs +0 -487
  101. package/dist/logger-thMPLpOG.cjs.map +0 -1
  102. package/dist/node-require-DF5QBX6z.cjs.map +0 -1
  103. package/dist/node-require-Db1oDpLj.js.map +0 -1
  104. package/dist/operation-context-C-2hmmtP.js.map +0 -1
  105. package/dist/operation-context-n4_obUwq.cjs.map +0 -1
  106. package/dist/stable-hash-BNTMrmdB.cjs.map +0 -1
  107. package/dist/stable-hash-Cg5cT34Q.js.map +0 -1
  108. package/dist/track-D59FfpL0.cjs.map +0 -1
  109. package/dist/track-wc0HafS_.js.map +0 -1
  110. package/dist/yaml-config-Ck2uB0Dp.cjs +0 -273
  111. package/dist/yaml-config-Ck2uB0Dp.cjs.map +0 -1
package/README.md CHANGED
@@ -1880,6 +1880,56 @@ try {
1880
1880
  }
1881
1881
  ```
1882
1882
 
1883
+ ## Diagnostics Channels
1884
+
1885
+ `autotel/diagnostics` bridges Node's built-in
1886
+ [`diagnostics_channel`](https://nodejs.org/api/diagnostics_channel.html) into
1887
+ autotel spans and events — no monkey-patching, no `import-in-the-middle`. Every
1888
+ entry point is opt-in and degrades to a no-op on runtimes (edge, old Node) that
1889
+ lack the underlying channels.
1890
+
1891
+ **Capture `console.*` as correlated wide events** (the patch-free way to get
1892
+ `console.log` into your traces):
1893
+
1894
+ ```ts
1895
+ import { captureConsole } from 'autotel/diagnostics';
1896
+
1897
+ const stop = captureConsole(); // every console.* → an OTel log record,
1898
+ // correlated to the active span by trace context
1899
+ ```
1900
+
1901
+ Each call becomes a log record (severity mapped from the method, printf-formatted
1902
+ body, `log.source`/`log.method` attributes). Pass `{ target: 'span-event' }` to
1903
+ add events to the active span instead, or `{ target: 'both' }`. Nothing patches
1904
+ the global `console`, so there's no load-order fragility.
1905
+
1906
+ **Lightweight HTTP spans + W3C propagation** without `import-in-the-middle`:
1907
+
1908
+ ```ts
1909
+ import { instrumentHttp } from 'autotel/diagnostics';
1910
+
1911
+ const stop = instrumentHttp(); // SERVER span per inbound request (parented to the
1912
+ // incoming traceparent) + CLIENT span per outbound
1913
+ // request (injects traceparent downstream)
1914
+ ```
1915
+
1916
+ This is an opt-in alternative to `@opentelemetry/instrumentation-http` for span
1917
+ coverage and propagation. Limitation: a plain channel can't wrap the request
1918
+ handler, so it does **not** set an ambient context for the handler's duration —
1919
+ application spans created inside a handler won't auto-nest under the SERVER span.
1920
+ Use `@opentelemetry/instrumentation-http` if you need that nesting.
1921
+
1922
+ **Bridge any channel** with the shared primitive (also used by
1923
+ `autotel-genai`'s `ai:telemetry` subscriber):
1924
+
1925
+ ```ts
1926
+ import { subscribeChannel, subscribeTracingChannel } from 'autotel/diagnostics';
1927
+
1928
+ const off = subscribeChannel('my-lib:event', (message) => {
1929
+ /* turn message into a span/event */
1930
+ });
1931
+ ```
1932
+
1883
1933
  ## Auto Instrumentation & Advanced Configuration
1884
1934
 
1885
1935
  - `autoInstrumentations` : Enable OpenTelemetry auto-instrumentations (HTTP, Express, Fastify, Prisma, Pino…). Requires `@opentelemetry/auto-instrumentations-node`.
package/dist/auto.cjs CHANGED
@@ -1,6 +1,8 @@
1
- const require_yaml_config = require('./yaml-config-Ck2uB0Dp.cjs');
2
- const require_init = require('./init-BX7AmFRl.cjs');
1
+ const require_chunk = require('./chunk-C_NdSu1c.cjs');
2
+ const require_init = require('./init-BXiuPK6j.cjs');
3
+ const require_yaml_config = require('./yaml-config.cjs');
3
4
  let node_module = require("node:module");
5
+ node_module = require_chunk.__toESM(node_module, 1);
4
6
  let import_in_the_middle = require("import-in-the-middle");
5
7
 
6
8
  //#region src/auto.ts
@@ -39,7 +41,7 @@ let import_in_the_middle = require("import-in-the-middle");
39
41
  * @requires Node.js 20.6.0 or later
40
42
  */
41
43
  const { registerOptions } = (0, import_in_the_middle.createAddHookMessageChannel)();
42
- (0, node_module.register)("import-in-the-middle/hook.mjs", require("url").pathToFileURL(__filename).href, registerOptions);
44
+ node_module.register("import-in-the-middle/hook.mjs", require("url").pathToFileURL(__filename).href, registerOptions);
43
45
  const yamlConfig = require_yaml_config.loadYamlConfig();
44
46
  const autoInstrumentationsEnv = process.env.AUTOTEL_INTEGRATIONS;
45
47
  const autoInstrumentations = autoInstrumentationsEnv === "true" ? true : autoInstrumentationsEnv ? autoInstrumentationsEnv.split(",").map((s) => s.trim()) : yamlConfig?.autoInstrumentations ?? ["http", "express"];
package/dist/auto.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"auto.cjs","names":["loadYamlConfig","init"],"sources":["../src/auto.ts"],"sourcesContent":["/**\n * Zero-config ESM instrumentation with auto-init from YAML/environment variables\n *\n * This module provides the simplest possible setup for OpenTelemetry instrumentation.\n * Just import it and everything is configured from autotel.yaml or environment variables.\n *\n * Usage with YAML config (recommended):\n * ```bash\n * # Create autotel.yaml in project root, then:\n * tsx --import autotel/auto src/index.ts\n * ```\n *\n * Usage with environment variables:\n * ```bash\n * OTEL_SERVICE_NAME=my-app tsx --import autotel/auto src/index.ts\n * ```\n *\n * No instrumentation.ts file needed!\n *\n * Configuration Priority (highest to lowest):\n * 1. YAML file (autotel.yaml or AUTOTEL_CONFIG_FILE)\n * 2. Environment variables (OTEL_*, AUTOTEL_*)\n * 3. Built-in defaults\n *\n * Environment Variables:\n * - OTEL_SERVICE_NAME: Service name (required for meaningful traces)\n * - OTEL_EXPORTER_OTLP_ENDPOINT: OTLP collector endpoint (e.g., http://localhost:4318)\n * - OTEL_EXPORTER_OTLP_HEADERS: Auth headers (e.g., x-honeycomb-team=YOUR_KEY)\n * - AUTOTEL_INTEGRATIONS: Comma-separated list or 'true' for all (default: http,express)\n * - AUTOTEL_DEBUG: Set to 'true' to enable console span output\n * - AUTOTEL_CONFIG_FILE: Path to YAML config file (overrides autotel.yaml discovery)\n *\n * @requires Node.js 20.6.0 or later\n */\n\nimport { register } from 'node:module';\nimport { createAddHookMessageChannel } from 'import-in-the-middle';\nimport { init } from './init';\nimport { loadYamlConfig } from './yaml-config';\n\n// Register ESM hooks first (must happen before any instrumented modules load)\nconst { registerOptions } = createAddHookMessageChannel();\nregister('import-in-the-middle/hook.mjs', import.meta.url, registerOptions);\n\n// Load YAML config if present (init.ts will also load it, but we need values here)\nconst yamlConfig = loadYamlConfig();\n\n// Parse auto-instrumentations from environment variable (fallback if not in YAML)\nconst autoInstrumentationsEnv = process.env.AUTOTEL_INTEGRATIONS;\nconst autoInstrumentations:\n | string[]\n | boolean\n | Record<string, { enabled?: boolean }> =\n autoInstrumentationsEnv === 'true'\n ? true // Enable all auto-instrumentations\n : autoInstrumentationsEnv\n ? autoInstrumentationsEnv.split(',').map((s) => s.trim())\n : (yamlConfig?.autoInstrumentations ?? ['http', 'express']); // YAML > default\n\n// Auto-initialize with YAML config merged with env var defaults\n// init() will load YAML again and merge properly, but we pass overrides here\ninit({\n service:\n yamlConfig?.service ?? process.env.OTEL_SERVICE_NAME ?? 'unknown-service',\n debug: yamlConfig?.debug ?? process.env.AUTOTEL_DEBUG === 'true',\n autoInstrumentations,\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCA,MAAM,EAAE,0EAAgD;0BAC/C,gFAAkD,eAAe;AAG1E,MAAM,aAAaA,mCAAe;AAGlC,MAAM,0BAA0B,QAAQ,IAAI;AAC5C,MAAM,uBAIJ,4BAA4B,SACxB,OACA,0BACE,wBAAwB,MAAM,GAAG,CAAC,CAAC,KAAK,MAAM,EAAE,KAAK,CAAC,IACrD,YAAY,wBAAwB,CAAC,QAAQ,SAAS;AAI/DC,kBAAK;CACH,SACE,YAAY,WAAW,QAAQ,IAAI,qBAAqB;CAC1D,OAAO,YAAY,SAAS,QAAQ,IAAI,kBAAkB;CAC1D;AACF,CAAC"}
1
+ {"version":3,"file":"auto.cjs","names":["nodeModule","loadYamlConfig","init"],"sources":["../src/auto.ts"],"sourcesContent":["/**\n * Zero-config ESM instrumentation with auto-init from YAML/environment variables\n *\n * This module provides the simplest possible setup for OpenTelemetry instrumentation.\n * Just import it and everything is configured from autotel.yaml or environment variables.\n *\n * Usage with YAML config (recommended):\n * ```bash\n * # Create autotel.yaml in project root, then:\n * tsx --import autotel/auto src/index.ts\n * ```\n *\n * Usage with environment variables:\n * ```bash\n * OTEL_SERVICE_NAME=my-app tsx --import autotel/auto src/index.ts\n * ```\n *\n * No instrumentation.ts file needed!\n *\n * Configuration Priority (highest to lowest):\n * 1. YAML file (autotel.yaml or AUTOTEL_CONFIG_FILE)\n * 2. Environment variables (OTEL_*, AUTOTEL_*)\n * 3. Built-in defaults\n *\n * Environment Variables:\n * - OTEL_SERVICE_NAME: Service name (required for meaningful traces)\n * - OTEL_EXPORTER_OTLP_ENDPOINT: OTLP collector endpoint (e.g., http://localhost:4318)\n * - OTEL_EXPORTER_OTLP_HEADERS: Auth headers (e.g., x-honeycomb-team=YOUR_KEY)\n * - AUTOTEL_INTEGRATIONS: Comma-separated list or 'true' for all (default: http,express)\n * - AUTOTEL_DEBUG: Set to 'true' to enable console span output\n * - AUTOTEL_CONFIG_FILE: Path to YAML config file (overrides autotel.yaml discovery)\n *\n * @requires Node.js 20.6.0 or later\n */\n\n// namespace import for browser-bundler compat — see node-require.ts\nimport * as nodeModule from 'node:module';\nimport { createAddHookMessageChannel } from 'import-in-the-middle';\nimport { init } from './init';\nimport { loadYamlConfig } from './yaml-config';\n\n// Register ESM hooks first (must happen before any instrumented modules load)\nconst { registerOptions } = createAddHookMessageChannel();\nnodeModule.register(\n 'import-in-the-middle/hook.mjs',\n import.meta.url,\n registerOptions,\n);\n\n// Load YAML config if present (init.ts will also load it, but we need values here)\nconst yamlConfig = loadYamlConfig();\n\n// Parse auto-instrumentations from environment variable (fallback if not in YAML)\nconst autoInstrumentationsEnv = process.env.AUTOTEL_INTEGRATIONS;\nconst autoInstrumentations:\n | string[]\n | boolean\n | Record<string, { enabled?: boolean }> =\n autoInstrumentationsEnv === 'true'\n ? true // Enable all auto-instrumentations\n : autoInstrumentationsEnv\n ? autoInstrumentationsEnv.split(',').map((s) => s.trim())\n : (yamlConfig?.autoInstrumentations ?? ['http', 'express']); // YAML > default\n\n// Auto-initialize with YAML config merged with env var defaults\n// init() will load YAML again and merge properly, but we pass overrides here\ninit({\n service:\n yamlConfig?.service ?? process.env.OTEL_SERVICE_NAME ?? 'unknown-service',\n debug: yamlConfig?.debug ?? process.env.AUTOTEL_DEBUG === 'true',\n autoInstrumentations,\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,MAAM,EAAE,0EAAgD;AACxDA,YAAW,SACT,gFAEA,eACF;AAGA,MAAM,aAAaC,mCAAe;AAGlC,MAAM,0BAA0B,QAAQ,IAAI;AAC5C,MAAM,uBAIJ,4BAA4B,SACxB,OACA,0BACE,wBAAwB,MAAM,GAAG,CAAC,CAAC,KAAK,MAAM,EAAE,KAAK,CAAC,IACrD,YAAY,wBAAwB,CAAC,QAAQ,SAAS;AAI/DC,kBAAK;CACH,SACE,YAAY,WAAW,QAAQ,IAAI,qBAAqB;CAC1D,OAAO,YAAY,SAAS,QAAQ,IAAI,kBAAkB;CAC1D;AACF,CAAC"}
package/dist/auto.js CHANGED
@@ -1,6 +1,6 @@
1
- import { s as init } from "./init-D-jnNMix.js";
1
+ import { s as init } from "./init-BS2JVkrL.js";
2
2
  import { loadYamlConfig } from "./yaml-config.js";
3
- import { register } from "node:module";
3
+ import * as nodeModule from "node:module";
4
4
  import { createAddHookMessageChannel } from "import-in-the-middle";
5
5
 
6
6
  //#region src/auto.ts
@@ -39,7 +39,7 @@ import { createAddHookMessageChannel } from "import-in-the-middle";
39
39
  * @requires Node.js 20.6.0 or later
40
40
  */
41
41
  const { registerOptions } = createAddHookMessageChannel();
42
- register("import-in-the-middle/hook.mjs", import.meta.url, registerOptions);
42
+ nodeModule.register("import-in-the-middle/hook.mjs", import.meta.url, registerOptions);
43
43
  const yamlConfig = loadYamlConfig();
44
44
  const autoInstrumentationsEnv = process.env.AUTOTEL_INTEGRATIONS;
45
45
  const autoInstrumentations = autoInstrumentationsEnv === "true" ? true : autoInstrumentationsEnv ? autoInstrumentationsEnv.split(",").map((s) => s.trim()) : yamlConfig?.autoInstrumentations ?? ["http", "express"];
package/dist/auto.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"auto.js","names":[],"sources":["../src/auto.ts"],"sourcesContent":["/**\n * Zero-config ESM instrumentation with auto-init from YAML/environment variables\n *\n * This module provides the simplest possible setup for OpenTelemetry instrumentation.\n * Just import it and everything is configured from autotel.yaml or environment variables.\n *\n * Usage with YAML config (recommended):\n * ```bash\n * # Create autotel.yaml in project root, then:\n * tsx --import autotel/auto src/index.ts\n * ```\n *\n * Usage with environment variables:\n * ```bash\n * OTEL_SERVICE_NAME=my-app tsx --import autotel/auto src/index.ts\n * ```\n *\n * No instrumentation.ts file needed!\n *\n * Configuration Priority (highest to lowest):\n * 1. YAML file (autotel.yaml or AUTOTEL_CONFIG_FILE)\n * 2. Environment variables (OTEL_*, AUTOTEL_*)\n * 3. Built-in defaults\n *\n * Environment Variables:\n * - OTEL_SERVICE_NAME: Service name (required for meaningful traces)\n * - OTEL_EXPORTER_OTLP_ENDPOINT: OTLP collector endpoint (e.g., http://localhost:4318)\n * - OTEL_EXPORTER_OTLP_HEADERS: Auth headers (e.g., x-honeycomb-team=YOUR_KEY)\n * - AUTOTEL_INTEGRATIONS: Comma-separated list or 'true' for all (default: http,express)\n * - AUTOTEL_DEBUG: Set to 'true' to enable console span output\n * - AUTOTEL_CONFIG_FILE: Path to YAML config file (overrides autotel.yaml discovery)\n *\n * @requires Node.js 20.6.0 or later\n */\n\nimport { register } from 'node:module';\nimport { createAddHookMessageChannel } from 'import-in-the-middle';\nimport { init } from './init';\nimport { loadYamlConfig } from './yaml-config';\n\n// Register ESM hooks first (must happen before any instrumented modules load)\nconst { registerOptions } = createAddHookMessageChannel();\nregister('import-in-the-middle/hook.mjs', import.meta.url, registerOptions);\n\n// Load YAML config if present (init.ts will also load it, but we need values here)\nconst yamlConfig = loadYamlConfig();\n\n// Parse auto-instrumentations from environment variable (fallback if not in YAML)\nconst autoInstrumentationsEnv = process.env.AUTOTEL_INTEGRATIONS;\nconst autoInstrumentations:\n | string[]\n | boolean\n | Record<string, { enabled?: boolean }> =\n autoInstrumentationsEnv === 'true'\n ? true // Enable all auto-instrumentations\n : autoInstrumentationsEnv\n ? autoInstrumentationsEnv.split(',').map((s) => s.trim())\n : (yamlConfig?.autoInstrumentations ?? ['http', 'express']); // YAML > default\n\n// Auto-initialize with YAML config merged with env var defaults\n// init() will load YAML again and merge properly, but we pass overrides here\ninit({\n service:\n yamlConfig?.service ?? process.env.OTEL_SERVICE_NAME ?? 'unknown-service',\n debug: yamlConfig?.debug ?? process.env.AUTOTEL_DEBUG === 'true',\n autoInstrumentations,\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCA,MAAM,EAAE,oBAAoB,4BAA4B;AACxD,SAAS,iCAAiC,OAAO,KAAK,KAAK,eAAe;AAG1E,MAAM,aAAa,eAAe;AAGlC,MAAM,0BAA0B,QAAQ,IAAI;AAC5C,MAAM,uBAIJ,4BAA4B,SACxB,OACA,0BACE,wBAAwB,MAAM,GAAG,CAAC,CAAC,KAAK,MAAM,EAAE,KAAK,CAAC,IACrD,YAAY,wBAAwB,CAAC,QAAQ,SAAS;AAI/D,KAAK;CACH,SACE,YAAY,WAAW,QAAQ,IAAI,qBAAqB;CAC1D,OAAO,YAAY,SAAS,QAAQ,IAAI,kBAAkB;CAC1D;AACF,CAAC"}
1
+ {"version":3,"file":"auto.js","names":[],"sources":["../src/auto.ts"],"sourcesContent":["/**\n * Zero-config ESM instrumentation with auto-init from YAML/environment variables\n *\n * This module provides the simplest possible setup for OpenTelemetry instrumentation.\n * Just import it and everything is configured from autotel.yaml or environment variables.\n *\n * Usage with YAML config (recommended):\n * ```bash\n * # Create autotel.yaml in project root, then:\n * tsx --import autotel/auto src/index.ts\n * ```\n *\n * Usage with environment variables:\n * ```bash\n * OTEL_SERVICE_NAME=my-app tsx --import autotel/auto src/index.ts\n * ```\n *\n * No instrumentation.ts file needed!\n *\n * Configuration Priority (highest to lowest):\n * 1. YAML file (autotel.yaml or AUTOTEL_CONFIG_FILE)\n * 2. Environment variables (OTEL_*, AUTOTEL_*)\n * 3. Built-in defaults\n *\n * Environment Variables:\n * - OTEL_SERVICE_NAME: Service name (required for meaningful traces)\n * - OTEL_EXPORTER_OTLP_ENDPOINT: OTLP collector endpoint (e.g., http://localhost:4318)\n * - OTEL_EXPORTER_OTLP_HEADERS: Auth headers (e.g., x-honeycomb-team=YOUR_KEY)\n * - AUTOTEL_INTEGRATIONS: Comma-separated list or 'true' for all (default: http,express)\n * - AUTOTEL_DEBUG: Set to 'true' to enable console span output\n * - AUTOTEL_CONFIG_FILE: Path to YAML config file (overrides autotel.yaml discovery)\n *\n * @requires Node.js 20.6.0 or later\n */\n\n// namespace import for browser-bundler compat — see node-require.ts\nimport * as nodeModule from 'node:module';\nimport { createAddHookMessageChannel } from 'import-in-the-middle';\nimport { init } from './init';\nimport { loadYamlConfig } from './yaml-config';\n\n// Register ESM hooks first (must happen before any instrumented modules load)\nconst { registerOptions } = createAddHookMessageChannel();\nnodeModule.register(\n 'import-in-the-middle/hook.mjs',\n import.meta.url,\n registerOptions,\n);\n\n// Load YAML config if present (init.ts will also load it, but we need values here)\nconst yamlConfig = loadYamlConfig();\n\n// Parse auto-instrumentations from environment variable (fallback if not in YAML)\nconst autoInstrumentationsEnv = process.env.AUTOTEL_INTEGRATIONS;\nconst autoInstrumentations:\n | string[]\n | boolean\n | Record<string, { enabled?: boolean }> =\n autoInstrumentationsEnv === 'true'\n ? true // Enable all auto-instrumentations\n : autoInstrumentationsEnv\n ? autoInstrumentationsEnv.split(',').map((s) => s.trim())\n : (yamlConfig?.autoInstrumentations ?? ['http', 'express']); // YAML > default\n\n// Auto-initialize with YAML config merged with env var defaults\n// init() will load YAML again and merge properly, but we pass overrides here\ninit({\n service:\n yamlConfig?.service ?? process.env.OTEL_SERVICE_NAME ?? 'unknown-service',\n debug: yamlConfig?.debug ?? process.env.AUTOTEL_DEBUG === 'true',\n autoInstrumentations,\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,MAAM,EAAE,oBAAoB,4BAA4B;AACxD,WAAW,SACT,iCACA,OAAO,KAAK,KACZ,eACF;AAGA,MAAM,aAAa,eAAe;AAGlC,MAAM,0BAA0B,QAAQ,IAAI;AAC5C,MAAM,uBAIJ,4BAA4B,SACxB,OACA,0BACE,wBAAwB,MAAM,GAAG,CAAC,CAAC,KAAK,MAAM,EAAE,KAAK,CAAC,IACrD,YAAY,wBAAwB,CAAC,QAAQ,SAAS;AAI/D,KAAK;CACH,SACE,YAAY,WAAW,QAAQ,IAAI,qBAAqB;CAC1D,OAAO,YAAY,SAAS,QAAQ,IAAI,kBAAkB;CAC1D;AACF,CAAC"}
@@ -0,0 +1,34 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) {
13
+ __defProp(to, key, {
14
+ get: ((k) => from[k]).bind(null, key),
15
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
16
+ });
17
+ }
18
+ }
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
23
+ value: mod,
24
+ enumerable: true
25
+ }) : target, mod));
26
+
27
+ //#endregion
28
+
29
+ Object.defineProperty(exports, '__toESM', {
30
+ enumerable: true,
31
+ get: function () {
32
+ return __toESM;
33
+ }
34
+ });
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- const require_track = require('./track-D59FfpL0.cjs');
2
+ const require_track = require('./track-Cb3Q4QmS.cjs');
3
3
 
4
4
  exports.CORRELATION_ID_BAGGAGE_KEY = require_track.CORRELATION_ID_BAGGAGE_KEY;
5
5
  exports.generateCorrelationId = require_track.generateCorrelationId;
@@ -1 +1 @@
1
- {"version":3,"file":"correlation-id.d.cts","names":[],"sources":["../src/correlation-id.ts"],"mappings":";;;KA4BK,gBAAA;EACH,KAAK;AAAA;AA+FiC;AAsBxC;;AAtBwC,cAnF3B,0BAAA;;;;;;;;AAyGiE;AAkB9E;;;;AAAsD;iBA5GtC,qBAAA;;;;AAsIuB;AAavC;;;;AAA2E;;;;;;;;;;;iBAvH3D,gBAAA;;;;;;;;;;;;;;;iBAwCA,wBAAA;;;;;;;;;;;;;;;;;;;iBAsBA,oBAAA,IAAwB,aAAA,UAAuB,EAAA,QAAU,CAAA,GAAI,CAAC;;;;;;;;;;;;;;;iBAkB9D,gBAAA,CAAiB,aAAqB;;;;;;;;;;;;;;;;;;;;;iBAwBtC,yBAAA,CACd,aAAA,wCAC8B,OAAO;;;;iBAavB,qBAAA,IAAyB,iBAAiB,CAAC,gBAAA"}
1
+ {"version":3,"file":"correlation-id.d.cts","names":[],"sources":["../src/correlation-id.ts"],"mappings":";;;KA8BK,gBAAA;EACH,KAAK;AAAA;AAgGiC;AAsBxC;;AAtBwC,cAnF3B,0BAAA;;;;;;;;AAyGiE;AAkB9E;;;;AAAsD;iBA5GtC,qBAAA;;;;AAsIuB;AAavC;;;;AAA2E;;;;;;;;;;;iBAvH3D,gBAAA;;;;;;;;;;;;;;;iBAwCA,wBAAA;;;;;;;;;;;;;;;;;;;iBAsBA,oBAAA,IAAwB,aAAA,UAAuB,EAAA,QAAU,CAAA,GAAI,CAAC;;;;;;;;;;;;;;;iBAkB9D,gBAAA,CAAiB,aAAqB;;;;;;;;;;;;;;;;;;;;;iBAwBtC,yBAAA,CACd,aAAA,wCAC8B,OAAO;;;;iBAavB,qBAAA,IAAyB,iBAAiB,CAAC,gBAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"correlation-id.d.ts","names":[],"sources":["../src/correlation-id.ts"],"mappings":";;;KA4BK,gBAAA;EACH,KAAK;AAAA;AA+FiC;AAsBxC;;AAtBwC,cAnF3B,0BAAA;;;;;;;;AAyGiE;AAkB9E;;;;AAAsD;iBA5GtC,qBAAA;;;;AAsIuB;AAavC;;;;AAA2E;;;;;;;;;;;iBAvH3D,gBAAA;;;;;;;;;;;;;;;iBAwCA,wBAAA;;;;;;;;;;;;;;;;;;;iBAsBA,oBAAA,IAAwB,aAAA,UAAuB,EAAA,QAAU,CAAA,GAAI,CAAC;;;;;;;;;;;;;;;iBAkB9D,gBAAA,CAAiB,aAAqB;;;;;;;;;;;;;;;;;;;;;iBAwBtC,yBAAA,CACd,aAAA,wCAC8B,OAAO;;;;iBAavB,qBAAA,IAAyB,iBAAiB,CAAC,gBAAA"}
1
+ {"version":3,"file":"correlation-id.d.ts","names":[],"sources":["../src/correlation-id.ts"],"mappings":";;;KA8BK,gBAAA;EACH,KAAK;AAAA;AAgGiC;AAsBxC;;AAtBwC,cAnF3B,0BAAA;;;;;;;;AAyGiE;AAkB9E;;;;AAAsD;iBA5GtC,qBAAA;;;;AAsIuB;AAavC;;;;AAA2E;;;;;;;;;;;iBAvH3D,gBAAA;;;;;;;;;;;;;;;iBAwCA,wBAAA;;;;;;;;;;;;;;;;;;;iBAsBA,oBAAA,IAAwB,aAAA,UAAuB,EAAA,QAAU,CAAA,GAAI,CAAC;;;;;;;;;;;;;;;iBAkB9D,gBAAA,CAAiB,aAAqB;;;;;;;;;;;;;;;;;;;;;iBAwBtC,yBAAA,CACd,aAAA,wCAC8B,OAAO;;;;iBAavB,qBAAA,IAAyB,iBAAiB,CAAC,gBAAA"}
@@ -1,3 +1,3 @@
1
- import { a as CORRELATION_ID_BAGGAGE_KEY, c as getCorrelationStorage, d as setCorrelationId, f as setCorrelationIdInBaggage, l as getOrCreateCorrelationId, o as generateCorrelationId, s as getCorrelationId, u as runWithCorrelationId } from "./track-wc0HafS_.js";
1
+ import { a as CORRELATION_ID_BAGGAGE_KEY, c as getCorrelationStorage, d as setCorrelationId, f as setCorrelationIdInBaggage, l as getOrCreateCorrelationId, o as generateCorrelationId, s as getCorrelationId, u as runWithCorrelationId } from "./track-COUuU48p.js";
2
2
 
3
3
  export { CORRELATION_ID_BAGGAGE_KEY, generateCorrelationId, getCorrelationId, getCorrelationStorage, getOrCreateCorrelationId, runWithCorrelationId, setCorrelationId, setCorrelationIdInBaggage };
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_config = require('./config.cjs');
3
- const require_track = require('./track-D59FfpL0.cjs');
3
+ const require_track = require('./track-Cb3Q4QmS.cjs');
4
4
  let _opentelemetry_api = require("@opentelemetry/api");
5
5
 
6
6
  //#region src/decorators.ts
@@ -1,5 +1,5 @@
1
1
  import { getConfig } from "./config.js";
2
- import { p as createTraceContext } from "./track-wc0HafS_.js";
2
+ import { p as createTraceContext } from "./track-COUuU48p.js";
3
3
  import { SpanStatusCode } from "@opentelemetry/api";
4
4
 
5
5
  //#region src/decorators.ts
@@ -0,0 +1,279 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_node_require = require('./node-require-CZ_PU448.cjs');
3
+ let _opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conventions");
4
+ let _opentelemetry_api = require("@opentelemetry/api");
5
+ let _opentelemetry_api_logs = require("@opentelemetry/api-logs");
6
+
7
+ //#region src/diagnostics/channel.ts
8
+ /**
9
+ * Edge-safe wrappers over Node's `diagnostics_channel`.
10
+ *
11
+ * The module is loaded lazily through {@link safeRequire} — never a static
12
+ * `node:` import — so merely importing this file is side-effect-free and bundles
13
+ * cleanly for browser/edge targets, where every subscribe call degrades to a
14
+ * no-op (returning an unsubscribe that does nothing). This is the shared
15
+ * primitive behind autotel's diagnostics-channel integrations (console capture,
16
+ * HTTP spans) and any app- or library-specific channel you want to bridge into
17
+ * a span/event.
18
+ *
19
+ * `diagnostics_channel.subscribe` (Node 18.7+) and `tracingChannel` (Node 19+)
20
+ * are used; autotel targets Node 22+, but on any runtime that lacks them the
21
+ * loader returns `undefined` and the helpers no-op.
22
+ */
23
+ let cached;
24
+ function loadDiagnosticsChannel() {
25
+ if (cached !== void 0) return cached ?? void 0;
26
+ cached = require_node_require.safeRequire("node:diagnostics_channel") ?? null;
27
+ return cached ?? void 0;
28
+ }
29
+ /** Whether Node's `diagnostics_channel` is available in this runtime. */
30
+ function diagnosticsChannelAvailable() {
31
+ return loadDiagnosticsChannel() !== void 0;
32
+ }
33
+ /**
34
+ * Subscribe to a named diagnostics channel. Returns an idempotent unsubscribe
35
+ * function; a no-op (that still returns a disposer) on unsupported runtimes.
36
+ */
37
+ function subscribeChannel(name, handler) {
38
+ const dc = loadDiagnosticsChannel();
39
+ if (!dc?.subscribe) return () => {};
40
+ dc.subscribe(name, handler);
41
+ let active = true;
42
+ return () => {
43
+ if (!active) return;
44
+ active = false;
45
+ dc.unsubscribe?.(name, handler);
46
+ };
47
+ }
48
+ /**
49
+ * Subscribe to a `tracingChannel` (the `tracing:${name}:{start,end,…}` set).
50
+ * Returns an idempotent unsubscribe; a no-op on runtimes without
51
+ * `tracingChannel` support.
52
+ */
53
+ function subscribeTracingChannel(name, handlers) {
54
+ const channel = loadDiagnosticsChannel()?.tracingChannel?.(name);
55
+ if (!channel) return () => {};
56
+ channel.subscribe(handlers);
57
+ let active = true;
58
+ return () => {
59
+ if (!active) return;
60
+ active = false;
61
+ channel.unsubscribe(handlers);
62
+ };
63
+ }
64
+
65
+ //#endregion
66
+ //#region src/diagnostics/console.ts
67
+ /**
68
+ * Capture `console.*` calls as wide events — without monkey-patching `console`.
69
+ *
70
+ * Node publishes every `console.log` / `info` / `debug` / `warn` / `error` call
71
+ * on a built-in diagnostics channel. {@link captureConsole} subscribes to those
72
+ * channels and turns each call into an OpenTelemetry **log record** (correlated
73
+ * to the active span via trace context by the logs SDK) and/or a **span event**
74
+ * on the active span. Nothing patches the global `console`, so there is no
75
+ * load-order fragility and no interference with other tooling.
76
+ *
77
+ * Opt-in. Call once after `init()` and keep the returned disposer to stop:
78
+ *
79
+ * ```ts
80
+ * import { captureConsole } from 'autotel/diagnostics';
81
+ *
82
+ * const stop = captureConsole(); // every console.* → correlated log record
83
+ * // …later: stop();
84
+ * ```
85
+ *
86
+ * The built-in `console.*` channels are a Stability-1 (experimental) Node API;
87
+ * this module degrades to a no-op where they are unavailable.
88
+ */
89
+ const ALL_LEVELS = [
90
+ "log",
91
+ "info",
92
+ "debug",
93
+ "warn",
94
+ "error"
95
+ ];
96
+ const SEVERITY = {
97
+ debug: _opentelemetry_api_logs.SeverityNumber.DEBUG,
98
+ log: _opentelemetry_api_logs.SeverityNumber.INFO,
99
+ info: _opentelemetry_api_logs.SeverityNumber.INFO,
100
+ warn: _opentelemetry_api_logs.SeverityNumber.WARN,
101
+ error: _opentelemetry_api_logs.SeverityNumber.ERROR
102
+ };
103
+ const nodeUtil = require_node_require.safeRequire("node:util");
104
+ /** Format console arguments the way `console` itself would (printf + inspect). */
105
+ function formatArgs(args) {
106
+ if (nodeUtil?.format) return nodeUtil.format(...args);
107
+ return args.map((a) => typeof a === "string" ? a : safeStringify(a)).join(" ");
108
+ }
109
+ function safeStringify(value) {
110
+ try {
111
+ return JSON.stringify(value) ?? String(value);
112
+ } catch {
113
+ return String(value);
114
+ }
115
+ }
116
+ /**
117
+ * Start capturing `console.*` calls as wide events. Returns a disposer that
118
+ * stops capture. Safe to call on runtimes without the console channels (no-op).
119
+ */
120
+ function captureConsole(options = {}) {
121
+ const levels = options.levels ?? ALL_LEVELS;
122
+ const target = options.target ?? "log";
123
+ const toLog = target === "log" || target === "both";
124
+ const toSpan = target === "span-event" || target === "both";
125
+ const logger = _opentelemetry_api_logs.logs.getLogger(options.loggerName ?? "autotel.console");
126
+ let recording = false;
127
+ const disposers = levels.map((level) => subscribeChannel(`console.${level}`, (message) => {
128
+ if (recording) return;
129
+ const body = formatArgs(message?.args ?? []);
130
+ recording = true;
131
+ try {
132
+ const attributes = {
133
+ "log.source": "console",
134
+ "log.method": level,
135
+ ...options.attributes
136
+ };
137
+ if (toLog) logger.emit({
138
+ severityNumber: SEVERITY[level],
139
+ severityText: level.toUpperCase(),
140
+ body,
141
+ attributes
142
+ });
143
+ if (toSpan) _opentelemetry_api.trace.getActiveSpan()?.addEvent("log", {
144
+ "log.message": body,
145
+ ...attributes
146
+ });
147
+ } finally {
148
+ recording = false;
149
+ }
150
+ }));
151
+ let active = true;
152
+ return () => {
153
+ if (!active) return;
154
+ active = false;
155
+ for (const dispose of disposers) dispose();
156
+ };
157
+ }
158
+
159
+ //#endregion
160
+ //#region src/diagnostics/http.ts
161
+ const SERVER_SPANS = /* @__PURE__ */ new WeakMap();
162
+ const CLIENT_SPANS = /* @__PURE__ */ new WeakMap();
163
+ function firstHeader(value) {
164
+ return Array.isArray(value) ? value[0] : value;
165
+ }
166
+ function splitHostPort(host) {
167
+ if (!host) return {};
168
+ const idx = host.lastIndexOf(":");
169
+ if (idx === -1) return { address: host };
170
+ const port = Number(host.slice(idx + 1));
171
+ return {
172
+ address: host.slice(0, idx),
173
+ port: Number.isFinite(port) ? port : void 0
174
+ };
175
+ }
176
+ /**
177
+ * Start emitting HTTP server/client spans from Node's HTTP diagnostics
178
+ * channels. Returns a disposer; a no-op on runtimes without the channels.
179
+ */
180
+ function instrumentHttp(options = {}) {
181
+ const tracer = options.tracer ?? _opentelemetry_api.trace.getTracer("autotel.http-diagnostics");
182
+ const disposers = [];
183
+ if (options.server !== false) disposers.push(subscribeChannel("http.server.request.start", (message) => {
184
+ const request = message?.request;
185
+ if (!request) return;
186
+ const method = request.method ?? "HTTP";
187
+ const { address, port } = splitHostPort(firstHeader(request.headers.host));
188
+ const path = (request.url ?? "/").split("?", 1)[0];
189
+ const attributes = {
190
+ [_opentelemetry_semantic_conventions.ATTR_HTTP_REQUEST_METHOD]: method,
191
+ [_opentelemetry_semantic_conventions.ATTR_URL_PATH]: path,
192
+ [_opentelemetry_semantic_conventions.ATTR_URL_SCHEME]: "http",
193
+ [_opentelemetry_semantic_conventions.ATTR_NETWORK_PROTOCOL_VERSION]: request.httpVersion,
194
+ [_opentelemetry_semantic_conventions.ATTR_USER_AGENT_ORIGINAL]: firstHeader(request.headers["user-agent"]),
195
+ [_opentelemetry_semantic_conventions.ATTR_SERVER_ADDRESS]: address,
196
+ [_opentelemetry_semantic_conventions.ATTR_SERVER_PORT]: port
197
+ };
198
+ const parent = _opentelemetry_api.propagation.extract(_opentelemetry_api.context.active(), request.headers, _opentelemetry_api.defaultTextMapGetter);
199
+ const span = tracer.startSpan(method, {
200
+ kind: _opentelemetry_api.SpanKind.SERVER,
201
+ attributes
202
+ }, parent);
203
+ SERVER_SPANS.set(request, span);
204
+ }), subscribeChannel("http.server.response.finish", (message) => {
205
+ const { request, response } = message ?? {};
206
+ if (!request) return;
207
+ const span = SERVER_SPANS.get(request);
208
+ if (!span) return;
209
+ SERVER_SPANS.delete(request);
210
+ finishHttpSpan(span, response?.statusCode, 500);
211
+ }));
212
+ if (options.client !== false) disposers.push(subscribeChannel("http.client.request.start", (message) => {
213
+ const request = message?.request;
214
+ if (!request) return;
215
+ const method = request.method ?? "HTTP";
216
+ const req = request;
217
+ const { address, port } = splitHostPort(req.host);
218
+ const scheme = (req.protocol ?? "http:").replace(":", "");
219
+ const attributes = {
220
+ [_opentelemetry_semantic_conventions.ATTR_HTTP_REQUEST_METHOD]: method,
221
+ [_opentelemetry_semantic_conventions.ATTR_SERVER_ADDRESS]: address,
222
+ [_opentelemetry_semantic_conventions.ATTR_SERVER_PORT]: port,
223
+ [_opentelemetry_semantic_conventions.ATTR_URL_FULL]: address && req.path ? `${scheme}://${req.host}${req.path}` : void 0
224
+ };
225
+ const span = tracer.startSpan(method, {
226
+ kind: _opentelemetry_api.SpanKind.CLIENT,
227
+ attributes
228
+ });
229
+ CLIENT_SPANS.set(request, span);
230
+ if (!request.headersSent) {
231
+ const carrier = {};
232
+ _opentelemetry_api.propagation.inject(_opentelemetry_api.trace.setSpan(_opentelemetry_api.context.active(), span), carrier, _opentelemetry_api.defaultTextMapSetter);
233
+ for (const [key, value] of Object.entries(carrier)) try {
234
+ request.setHeader(key, value);
235
+ } catch {}
236
+ }
237
+ }), subscribeChannel("http.client.response.finish", (message) => {
238
+ const { request, response } = message ?? {};
239
+ if (!request) return;
240
+ const span = CLIENT_SPANS.get(request);
241
+ if (!span) return;
242
+ CLIENT_SPANS.delete(request);
243
+ finishHttpSpan(span, response?.statusCode, 400);
244
+ }), subscribeChannel("http.client.request.error", (message) => {
245
+ const { request, error } = message ?? {};
246
+ if (!request) return;
247
+ const span = CLIENT_SPANS.get(request);
248
+ if (!span) return;
249
+ CLIENT_SPANS.delete(request);
250
+ if (error instanceof Error) span.recordException(error);
251
+ span.setStatus({
252
+ code: _opentelemetry_api.SpanStatusCode.ERROR,
253
+ message: error instanceof Error ? error.message : void 0
254
+ });
255
+ span.end();
256
+ }));
257
+ let active = true;
258
+ return () => {
259
+ if (!active) return;
260
+ active = false;
261
+ for (const dispose of disposers) dispose();
262
+ };
263
+ }
264
+ /** Set status code + error status (when `>= errorAt`) and end the span. */
265
+ function finishHttpSpan(span, statusCode, errorAt) {
266
+ if (statusCode !== void 0) {
267
+ span.setAttribute(_opentelemetry_semantic_conventions.ATTR_HTTP_RESPONSE_STATUS_CODE, statusCode);
268
+ if (statusCode >= errorAt) span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
269
+ }
270
+ span.end();
271
+ }
272
+
273
+ //#endregion
274
+ exports.captureConsole = captureConsole;
275
+ exports.diagnosticsChannelAvailable = diagnosticsChannelAvailable;
276
+ exports.instrumentHttp = instrumentHttp;
277
+ exports.subscribeChannel = subscribeChannel;
278
+ exports.subscribeTracingChannel = subscribeTracingChannel;
279
+ //# sourceMappingURL=diagnostics.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics.cjs","names":["safeRequire","SeverityNumber","safeRequire","logs","trace","ATTR_HTTP_REQUEST_METHOD","ATTR_URL_PATH","ATTR_URL_SCHEME","ATTR_NETWORK_PROTOCOL_VERSION","ATTR_USER_AGENT_ORIGINAL","ATTR_SERVER_ADDRESS","ATTR_SERVER_PORT","propagation","otelContext","defaultTextMapGetter","SpanKind","ATTR_URL_FULL","defaultTextMapSetter","SpanStatusCode","ATTR_HTTP_RESPONSE_STATUS_CODE"],"sources":["../src/diagnostics/channel.ts","../src/diagnostics/console.ts","../src/diagnostics/http.ts"],"sourcesContent":["/**\n * Edge-safe wrappers over Node's `diagnostics_channel`.\n *\n * The module is loaded lazily through {@link safeRequire} — never a static\n * `node:` import — so merely importing this file is side-effect-free and bundles\n * cleanly for browser/edge targets, where every subscribe call degrades to a\n * no-op (returning an unsubscribe that does nothing). This is the shared\n * primitive behind autotel's diagnostics-channel integrations (console capture,\n * HTTP spans) and any app- or library-specific channel you want to bridge into\n * a span/event.\n *\n * `diagnostics_channel.subscribe` (Node 18.7+) and `tracingChannel` (Node 19+)\n * are used; autotel targets Node 22+, but on any runtime that lacks them the\n * loader returns `undefined` and the helpers no-op.\n */\n\nimport { safeRequire } from '../node-require.js';\n\ntype DiagnosticsChannelModule = typeof import('node:diagnostics_channel');\n\nlet cached: DiagnosticsChannelModule | null | undefined;\n\nfunction loadDiagnosticsChannel(): DiagnosticsChannelModule | undefined {\n if (cached !== undefined) return cached ?? undefined;\n cached =\n safeRequire<DiagnosticsChannelModule>('node:diagnostics_channel') ?? null;\n return cached ?? undefined;\n}\n\n/** Whether Node's `diagnostics_channel` is available in this runtime. */\nexport function diagnosticsChannelAvailable(): boolean {\n return loadDiagnosticsChannel() !== undefined;\n}\n\n/** Handler for a plain named channel. */\nexport type ChannelMessageHandler = (\n message: unknown,\n name: string | symbol,\n) => void;\n\n/**\n * Subscribe to a named diagnostics channel. Returns an idempotent unsubscribe\n * function; a no-op (that still returns a disposer) on unsupported runtimes.\n */\nexport function subscribeChannel(\n name: string,\n handler: ChannelMessageHandler,\n): () => void {\n const dc = loadDiagnosticsChannel();\n if (!dc?.subscribe) return () => {};\n dc.subscribe(name, handler);\n let active = true;\n return () => {\n if (!active) return;\n active = false;\n dc.unsubscribe?.(name, handler);\n };\n}\n\n/** Subscriber set for a {@link https://nodejs.org/api/diagnostics_channel.html#class-tracingchannel TracingChannel}. */\nexport interface TracingChannelHandlers {\n start?(message: unknown): void;\n end?(message: unknown): void;\n asyncStart?(message: unknown): void;\n asyncEnd?(message: unknown): void;\n error?(message: unknown): void;\n}\n\n/**\n * Subscribe to a `tracingChannel` (the `tracing:${name}:{start,end,…}` set).\n * Returns an idempotent unsubscribe; a no-op on runtimes without\n * `tracingChannel` support.\n */\nexport function subscribeTracingChannel(\n name: string,\n handlers: TracingChannelHandlers,\n): () => void {\n const dc = loadDiagnosticsChannel();\n const channel = dc?.tracingChannel?.(name);\n if (!channel) return () => {};\n // Node's typings want all five handlers; we pass the subset provided.\n channel.subscribe(handlers as Parameters<typeof channel.subscribe>[0]);\n let active = true;\n return () => {\n if (!active) return;\n active = false;\n channel.unsubscribe(handlers as Parameters<typeof channel.unsubscribe>[0]);\n };\n}\n","/**\n * Capture `console.*` calls as wide events — without monkey-patching `console`.\n *\n * Node publishes every `console.log` / `info` / `debug` / `warn` / `error` call\n * on a built-in diagnostics channel. {@link captureConsole} subscribes to those\n * channels and turns each call into an OpenTelemetry **log record** (correlated\n * to the active span via trace context by the logs SDK) and/or a **span event**\n * on the active span. Nothing patches the global `console`, so there is no\n * load-order fragility and no interference with other tooling.\n *\n * Opt-in. Call once after `init()` and keep the returned disposer to stop:\n *\n * ```ts\n * import { captureConsole } from 'autotel/diagnostics';\n *\n * const stop = captureConsole(); // every console.* → correlated log record\n * // …later: stop();\n * ```\n *\n * The built-in `console.*` channels are a Stability-1 (experimental) Node API;\n * this module degrades to a no-op where they are unavailable.\n */\n\nimport { trace, type Attributes } from '@opentelemetry/api';\nimport { logs, SeverityNumber, type Logger } from '@opentelemetry/api-logs';\nimport { safeRequire } from '../node-require.js';\nimport { subscribeChannel } from './channel.js';\n\n/** Console methods that publish a diagnostics channel. */\nexport type ConsoleLevel = 'log' | 'info' | 'debug' | 'warn' | 'error';\n\nconst ALL_LEVELS: readonly ConsoleLevel[] = [\n 'log',\n 'info',\n 'debug',\n 'warn',\n 'error',\n];\n\nconst SEVERITY: Record<ConsoleLevel, SeverityNumber> = {\n debug: SeverityNumber.DEBUG,\n log: SeverityNumber.INFO,\n info: SeverityNumber.INFO,\n warn: SeverityNumber.WARN,\n error: SeverityNumber.ERROR,\n};\n\nexport interface CaptureConsoleOptions {\n /** Which console methods to capture. Defaults to all five. */\n levels?: readonly ConsoleLevel[];\n /**\n * Where to record captured output:\n * - `'log'` (default): emit an OpenTelemetry log record;\n * - `'span-event'`: add an event to the active span (nothing if no active span);\n * - `'both'`.\n */\n target?: 'log' | 'span-event' | 'both';\n /** Logger name for emitted records. Defaults to `'autotel.console'`. */\n loggerName?: string;\n /** Static attributes merged onto every captured record/event. */\n attributes?: Attributes;\n}\n\ntype ConsoleMessage = { args?: unknown[] };\n\nconst nodeUtil = safeRequire<typeof import('node:util')>('node:util');\n\n/** Format console arguments the way `console` itself would (printf + inspect). */\nfunction formatArgs(args: unknown[]): string {\n if (nodeUtil?.format) return nodeUtil.format(...args);\n return args\n .map((a) => (typeof a === 'string' ? a : safeStringify(a)))\n .join(' ');\n}\n\nfunction safeStringify(value: unknown): string {\n try {\n return JSON.stringify(value) ?? String(value);\n } catch {\n return String(value);\n }\n}\n\n/**\n * Start capturing `console.*` calls as wide events. Returns a disposer that\n * stops capture. Safe to call on runtimes without the console channels (no-op).\n */\nexport function captureConsole(\n options: CaptureConsoleOptions = {},\n): () => void {\n const levels = options.levels ?? ALL_LEVELS;\n const target = options.target ?? 'log';\n const toLog = target === 'log' || target === 'both';\n const toSpan = target === 'span-event' || target === 'both';\n const logger: Logger = logs.getLogger(\n options.loggerName ?? 'autotel.console',\n );\n\n // Guard against re-entrancy: if recording a captured call itself triggers a\n // `console.*` (e.g. an exporter logging a warning), don't capture that.\n let recording = false;\n\n const disposers = levels.map((level) =>\n subscribeChannel(`console.${level}`, (message) => {\n if (recording) return;\n const args = (message as ConsoleMessage)?.args ?? [];\n const body = formatArgs(args as unknown[]);\n recording = true;\n try {\n const attributes: Attributes = {\n 'log.source': 'console',\n 'log.method': level,\n ...options.attributes,\n };\n if (toLog) {\n logger.emit({\n severityNumber: SEVERITY[level],\n severityText: level.toUpperCase(),\n body,\n attributes,\n });\n }\n if (toSpan) {\n trace\n .getActiveSpan()\n ?.addEvent('log', { 'log.message': body, ...attributes });\n }\n } finally {\n recording = false;\n }\n }),\n );\n\n let active = true;\n return () => {\n if (!active) return;\n active = false;\n for (const dispose of disposers) dispose();\n };\n}\n","/**\n * Lightweight HTTP spans via Node's built-in `diagnostics_channel` — no\n * monkey-patching, no `import-in-the-middle`.\n *\n * Node publishes `http.server.request.start` / `http.server.response.finish`\n * and `http.client.request.start` / `http.client.response.finish` /\n * `http.client.request.error`. {@link instrumentHttp} subscribes to those and\n * emits a `SERVER` span per inbound request (parented to an incoming W3C\n * `traceparent`) and a `CLIENT` span per outbound request (whose context it\n * injects into the outgoing headers for downstream propagation).\n *\n * ```ts\n * import { instrumentHttp } from 'autotel/diagnostics';\n *\n * const stop = instrumentHttp();\n * ```\n *\n * Scope & limitation. This is an opt-in, low-overhead alternative to\n * `@opentelemetry/instrumentation-http` for HTTP span coverage + W3C\n * propagation. Client-side propagation works (the `traceparent` is injected on\n * the `ClientRequest` object directly). What it does **not** do is establish an\n * *ambient* OpenTelemetry context for the duration of a server request handler,\n * so application spans created inside a handler will not become children of the\n * `SERVER` span.\n *\n * This is structural, not a \"wait for a newer Node\" gap. Node publishes the\n * `http.*` channels with a plain `channel.publish()` — not `runStores` /\n * `tracingChannel` — so a subscriber has no scope to bind a store to. The only\n * ways to get handler nesting both defeat the purpose of using a channel:\n * 1. `AsyncLocalStorage.enterWith()` in the start handler — no scoped exit, so\n * context leaks across requests sharing an event-loop tick / keep-alive\n * connection and misattributes spans. Strictly worse than no nesting.\n * 2. Patching `http.Server.prototype.emit` to wrap the `'request'` listener in\n * `context.with()` — monkey-patching, i.e. reimplementing\n * `@opentelemetry/instrumentation-http`.\n * If you need handler nesting, use `@opentelemetry/instrumentation-http`.\n *\n * The `http.*` channels are a Stability-1 (experimental) Node API; this module\n * degrades to a no-op where they are unavailable.\n */\n\nimport type { ClientRequest, IncomingMessage, ServerResponse } from 'node:http';\nimport {\n context as otelContext,\n defaultTextMapGetter,\n defaultTextMapSetter,\n propagation,\n SpanKind,\n SpanStatusCode,\n trace,\n type Attributes,\n type Span,\n type Tracer,\n} from '@opentelemetry/api';\nimport {\n ATTR_HTTP_REQUEST_METHOD,\n ATTR_HTTP_RESPONSE_STATUS_CODE,\n ATTR_NETWORK_PROTOCOL_VERSION,\n ATTR_SERVER_ADDRESS,\n ATTR_SERVER_PORT,\n ATTR_URL_FULL,\n ATTR_URL_PATH,\n ATTR_URL_SCHEME,\n ATTR_USER_AGENT_ORIGINAL,\n} from '@opentelemetry/semantic-conventions';\nimport { subscribeChannel } from './channel.js';\n\nexport interface InstrumentHttpOptions {\n /** Instrument inbound (server) requests. Default `true`. */\n server?: boolean;\n /** Instrument outbound (client) requests. Default `true`. */\n client?: boolean;\n /** Tracer to use. Defaults to `trace.getTracer('autotel.http-diagnostics')`. */\n tracer?: Tracer;\n}\n\ninterface ServerStartMessage {\n request?: IncomingMessage;\n response?: ServerResponse;\n}\ninterface ServerFinishMessage {\n request?: IncomingMessage;\n response?: ServerResponse;\n}\ninterface ClientStartMessage {\n request?: ClientRequest;\n}\ninterface ClientFinishMessage {\n request?: ClientRequest;\n response?: IncomingMessage;\n}\ninterface ClientErrorMessage {\n request?: ClientRequest;\n error?: unknown;\n}\n\nconst SERVER_SPANS = new WeakMap<object, Span>();\nconst CLIENT_SPANS = new WeakMap<object, Span>();\n\nfunction firstHeader(value: string | string[] | undefined): string | undefined {\n return Array.isArray(value) ? value[0] : value;\n}\n\nfunction splitHostPort(host: string | undefined): {\n address?: string;\n port?: number;\n} {\n if (!host) return {};\n const idx = host.lastIndexOf(':');\n if (idx === -1) return { address: host };\n const port = Number(host.slice(idx + 1));\n return {\n address: host.slice(0, idx),\n port: Number.isFinite(port) ? port : undefined,\n };\n}\n\n/**\n * Start emitting HTTP server/client spans from Node's HTTP diagnostics\n * channels. Returns a disposer; a no-op on runtimes without the channels.\n */\nexport function instrumentHttp(\n options: InstrumentHttpOptions = {},\n): () => void {\n const tracer = options.tracer ?? trace.getTracer('autotel.http-diagnostics');\n const disposers: Array<() => void> = [];\n\n if (options.server !== false) {\n disposers.push(\n subscribeChannel('http.server.request.start', (message) => {\n const request = (message as ServerStartMessage)?.request;\n if (!request) return;\n const method = request.method ?? 'HTTP';\n const host = firstHeader(request.headers.host);\n const { address, port } = splitHostPort(host);\n const path = (request.url ?? '/').split('?', 1)[0];\n const attributes: Attributes = {\n [ATTR_HTTP_REQUEST_METHOD]: method,\n [ATTR_URL_PATH]: path,\n [ATTR_URL_SCHEME]: 'http',\n [ATTR_NETWORK_PROTOCOL_VERSION]: request.httpVersion,\n [ATTR_USER_AGENT_ORIGINAL]: firstHeader(\n request.headers['user-agent'],\n ),\n [ATTR_SERVER_ADDRESS]: address,\n [ATTR_SERVER_PORT]: port,\n };\n const parent = propagation.extract(\n otelContext.active(),\n request.headers,\n defaultTextMapGetter,\n );\n const span = tracer.startSpan(\n method,\n { kind: SpanKind.SERVER, attributes },\n parent,\n );\n SERVER_SPANS.set(request, span);\n }),\n subscribeChannel('http.server.response.finish', (message) => {\n const { request, response } = (message as ServerFinishMessage) ?? {};\n if (!request) return;\n const span = SERVER_SPANS.get(request);\n if (!span) return;\n SERVER_SPANS.delete(request);\n finishHttpSpan(span, response?.statusCode, 500);\n }),\n );\n }\n\n if (options.client !== false) {\n disposers.push(\n subscribeChannel('http.client.request.start', (message) => {\n const request = (message as ClientStartMessage)?.request;\n if (!request) return;\n const method = request.method ?? 'HTTP';\n // `ClientRequest` exposes host/protocol/path on the public surface.\n const req = request as ClientRequest & {\n host?: string;\n protocol?: string;\n path?: string;\n };\n const { address, port } = splitHostPort(req.host);\n const scheme = (req.protocol ?? 'http:').replace(':', '');\n const attributes: Attributes = {\n [ATTR_HTTP_REQUEST_METHOD]: method,\n [ATTR_SERVER_ADDRESS]: address,\n [ATTR_SERVER_PORT]: port,\n [ATTR_URL_FULL]:\n address && req.path\n ? `${scheme}://${req.host}${req.path}`\n : undefined,\n };\n const span = tracer.startSpan(method, {\n kind: SpanKind.CLIENT,\n attributes,\n });\n CLIENT_SPANS.set(request, span);\n\n // Inject this span's context into the outbound headers so the\n // downstream service continues the trace.\n if (!request.headersSent) {\n const carrier: Record<string, string> = {};\n propagation.inject(\n trace.setSpan(otelContext.active(), span),\n carrier,\n defaultTextMapSetter,\n );\n for (const [key, value] of Object.entries(carrier)) {\n try {\n request.setHeader(key, value);\n } catch {\n // Headers already sent / immutable — propagation best-effort.\n }\n }\n }\n }),\n subscribeChannel('http.client.response.finish', (message) => {\n const { request, response } = (message as ClientFinishMessage) ?? {};\n if (!request) return;\n const span = CLIENT_SPANS.get(request);\n if (!span) return;\n CLIENT_SPANS.delete(request);\n finishHttpSpan(span, response?.statusCode, 400);\n }),\n subscribeChannel('http.client.request.error', (message) => {\n const { request, error } = (message as ClientErrorMessage) ?? {};\n if (!request) return;\n const span = CLIENT_SPANS.get(request);\n if (!span) return;\n CLIENT_SPANS.delete(request);\n if (error instanceof Error) span.recordException(error);\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: error instanceof Error ? error.message : undefined,\n });\n span.end();\n }),\n );\n }\n\n let active = true;\n return () => {\n if (!active) return;\n active = false;\n for (const dispose of disposers) dispose();\n };\n}\n\n/** Set status code + error status (when `>= errorAt`) and end the span. */\nfunction finishHttpSpan(\n span: Span,\n statusCode: number | undefined,\n errorAt: number,\n): void {\n if (statusCode !== undefined) {\n span.setAttribute(ATTR_HTTP_RESPONSE_STATUS_CODE, statusCode);\n if (statusCode >= errorAt) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n }\n }\n span.end();\n}\n"],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ import { Attributes, Tracer } from "@opentelemetry/api";
2
+
3
+ //#region src/diagnostics/channel.d.ts
4
+ /**
5
+ * Edge-safe wrappers over Node's `diagnostics_channel`.
6
+ *
7
+ * The module is loaded lazily through {@link safeRequire} — never a static
8
+ * `node:` import — so merely importing this file is side-effect-free and bundles
9
+ * cleanly for browser/edge targets, where every subscribe call degrades to a
10
+ * no-op (returning an unsubscribe that does nothing). This is the shared
11
+ * primitive behind autotel's diagnostics-channel integrations (console capture,
12
+ * HTTP spans) and any app- or library-specific channel you want to bridge into
13
+ * a span/event.
14
+ *
15
+ * `diagnostics_channel.subscribe` (Node 18.7+) and `tracingChannel` (Node 19+)
16
+ * are used; autotel targets Node 22+, but on any runtime that lacks them the
17
+ * loader returns `undefined` and the helpers no-op.
18
+ */
19
+ /** Whether Node's `diagnostics_channel` is available in this runtime. */
20
+ declare function diagnosticsChannelAvailable(): boolean;
21
+ /** Handler for a plain named channel. */
22
+ type ChannelMessageHandler = (message: unknown, name: string | symbol) => void;
23
+ /**
24
+ * Subscribe to a named diagnostics channel. Returns an idempotent unsubscribe
25
+ * function; a no-op (that still returns a disposer) on unsupported runtimes.
26
+ */
27
+ declare function subscribeChannel(name: string, handler: ChannelMessageHandler): () => void;
28
+ /** Subscriber set for a {@link https://nodejs.org/api/diagnostics_channel.html#class-tracingchannel TracingChannel}. */
29
+ interface TracingChannelHandlers {
30
+ start?(message: unknown): void;
31
+ end?(message: unknown): void;
32
+ asyncStart?(message: unknown): void;
33
+ asyncEnd?(message: unknown): void;
34
+ error?(message: unknown): void;
35
+ }
36
+ /**
37
+ * Subscribe to a `tracingChannel` (the `tracing:${name}:{start,end,…}` set).
38
+ * Returns an idempotent unsubscribe; a no-op on runtimes without
39
+ * `tracingChannel` support.
40
+ */
41
+ declare function subscribeTracingChannel(name: string, handlers: TracingChannelHandlers): () => void;
42
+ //#endregion
43
+ //#region src/diagnostics/console.d.ts
44
+ /** Console methods that publish a diagnostics channel. */
45
+ type ConsoleLevel = 'log' | 'info' | 'debug' | 'warn' | 'error';
46
+ interface CaptureConsoleOptions {
47
+ /** Which console methods to capture. Defaults to all five. */
48
+ levels?: readonly ConsoleLevel[];
49
+ /**
50
+ * Where to record captured output:
51
+ * - `'log'` (default): emit an OpenTelemetry log record;
52
+ * - `'span-event'`: add an event to the active span (nothing if no active span);
53
+ * - `'both'`.
54
+ */
55
+ target?: 'log' | 'span-event' | 'both';
56
+ /** Logger name for emitted records. Defaults to `'autotel.console'`. */
57
+ loggerName?: string;
58
+ /** Static attributes merged onto every captured record/event. */
59
+ attributes?: Attributes;
60
+ }
61
+ /**
62
+ * Start capturing `console.*` calls as wide events. Returns a disposer that
63
+ * stops capture. Safe to call on runtimes without the console channels (no-op).
64
+ */
65
+ declare function captureConsole(options?: CaptureConsoleOptions): () => void;
66
+ //#endregion
67
+ //#region src/diagnostics/http.d.ts
68
+ interface InstrumentHttpOptions {
69
+ /** Instrument inbound (server) requests. Default `true`. */
70
+ server?: boolean;
71
+ /** Instrument outbound (client) requests. Default `true`. */
72
+ client?: boolean;
73
+ /** Tracer to use. Defaults to `trace.getTracer('autotel.http-diagnostics')`. */
74
+ tracer?: Tracer;
75
+ }
76
+ /**
77
+ * Start emitting HTTP server/client spans from Node's HTTP diagnostics
78
+ * channels. Returns a disposer; a no-op on runtimes without the channels.
79
+ */
80
+ declare function instrumentHttp(options?: InstrumentHttpOptions): () => void;
81
+ //#endregion
82
+ export { type CaptureConsoleOptions, type ChannelMessageHandler, type ConsoleLevel, type InstrumentHttpOptions, type TracingChannelHandlers, captureConsole, diagnosticsChannelAvailable, instrumentHttp, subscribeChannel, subscribeTracingChannel };
83
+ //# sourceMappingURL=diagnostics.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics.d.cts","names":[],"sources":["../src/diagnostics/channel.ts","../src/diagnostics/console.ts","../src/diagnostics/http.ts"],"mappings":";;;;;;AA8BA;;;;AAA2C;AAK3C;;;;AAEuB;AAOvB;;;iBAdgB,2BAAA;;KAKJ,qBAAA,IACV,OAAA,WACA,IAAqB;;;AASS;AAchC;iBAhBgB,gBAAA,CACd,IAAA,UACA,OAAA,EAAS,qBAAqB;;UAcf,sBAAA;EACf,KAAA,EAAO,OAAA;EACP,GAAA,EAAK,OAAA;EACL,UAAA,EAAY,OAAA;EACZ,QAAA,EAAU,OAAA;EACV,KAAA,EAAO,OAAA;AAAA;;;;;;iBAQO,uBAAA,CACd,IAAA,UACA,QAAA,EAAU,sBAAsB;;;;KC9CtB,YAAA;AAAA,UAkBK,qBAAA;EDcf;ECZA,MAAA,YAAkB,YAAA;EDalB;;;;;;ECNA,MAAA;EDSO;ECPP,UAAA;EDOuB;ECLvB,UAAA,GAAa,UAAU;AAAA;;;;;iBA2BT,cAAA,CACd,OAAmC,GAA1B,qBAA0B;;;UCrBpB,qBAAA;EFOf;EELA,MAAA;EFMA;EEJA,MAAA;EFIgC;EEFhC,MAAA,GAAS,MAAM;AAAA;;AD5CjB;;;iBC4FgB,cAAA,CACd,OAAmC,GAA1B,qBAA0B"}