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.
- package/README.md +50 -0
- package/dist/auto.cjs +5 -3
- package/dist/auto.cjs.map +1 -1
- package/dist/auto.js +3 -3
- package/dist/auto.js.map +1 -1
- package/dist/chunk-C_NdSu1c.cjs +34 -0
- package/dist/correlation-id.cjs +1 -1
- package/dist/correlation-id.d.cts.map +1 -1
- package/dist/correlation-id.d.ts.map +1 -1
- package/dist/correlation-id.js +1 -1
- package/dist/decorators.cjs +1 -1
- package/dist/decorators.js +1 -1
- package/dist/diagnostics.cjs +279 -0
- package/dist/diagnostics.cjs.map +1 -0
- package/dist/diagnostics.d.cts +83 -0
- package/dist/diagnostics.d.cts.map +1 -0
- package/dist/diagnostics.d.ts +83 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +274 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/{event-ByBTV9M2.js → event-531asIM6.js} +4 -4
- package/dist/{event-ByBTV9M2.js.map → event-531asIM6.js.map} +1 -1
- package/dist/{event-BhHREDJk.cjs → event-CcZYwp50.cjs} +4 -4
- package/dist/{event-BhHREDJk.cjs.map → event-CcZYwp50.cjs.map} +1 -1
- package/dist/event.cjs +1 -1
- package/dist/event.js +1 -1
- package/dist/{functional-zpzNLhky.cjs → functional-C8B0Qa7o.cjs} +10 -7
- package/dist/functional-C8B0Qa7o.cjs.map +1 -0
- package/dist/{functional-DtI0u4vx.js → functional-r-AUIRy_.js} +9 -9
- package/dist/functional-r-AUIRy_.js.map +1 -0
- package/dist/functional.cjs +1 -1
- package/dist/functional.js +1 -1
- package/dist/http.cjs +1 -1
- package/dist/http.js +1 -1
- package/dist/index.cjs +15 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -14
- package/dist/index.js.map +1 -1
- package/dist/{init-D-jnNMix.js → init-BS2JVkrL.js} +2 -2
- package/dist/{init-D-jnNMix.js.map → init-BS2JVkrL.js.map} +1 -1
- package/dist/{init-BX7AmFRl.cjs → init-BXiuPK6j.cjs} +3 -3
- package/dist/{init-BX7AmFRl.cjs.map → init-BXiuPK6j.cjs.map} +1 -1
- package/dist/instrumentation.cjs +2 -2
- package/dist/instrumentation.js +2 -2
- package/dist/logger.cjs +236 -8
- package/dist/logger.cjs.map +1 -0
- package/dist/messaging.cjs +1 -1
- package/dist/messaging.js +1 -1
- package/dist/{node-require-DF5QBX6z.cjs → node-require-CZ_PU448.cjs} +6 -4
- package/dist/node-require-CZ_PU448.cjs.map +1 -0
- package/dist/{node-require-Db1oDpLj.js → node-require-vROmTeJ8.js} +5 -5
- package/dist/node-require-vROmTeJ8.js.map +1 -0
- package/dist/{operation-context-C-2hmmtP.js → operation-context-CKBoA4Qy.js} +3 -3
- package/dist/operation-context-CKBoA4Qy.js.map +1 -0
- package/dist/{operation-context-n4_obUwq.cjs → operation-context-D6LDf4W_.cjs} +3 -1
- package/dist/operation-context-D6LDf4W_.cjs.map +1 -0
- package/dist/register.cjs +3 -1
- package/dist/register.cjs.map +1 -1
- package/dist/register.js +2 -2
- package/dist/register.js.map +1 -1
- package/dist/semantic-helpers.cjs +1 -1
- package/dist/semantic-helpers.js +1 -1
- package/dist/{stable-hash-Cg5cT34Q.js → stable-hash-ChFBIhNt.js} +3 -3
- package/dist/stable-hash-ChFBIhNt.js.map +1 -0
- package/dist/{stable-hash-BNTMrmdB.cjs → stable-hash-brKISGf1.cjs} +4 -2
- package/dist/stable-hash-brKISGf1.cjs.map +1 -0
- package/dist/trace-context-Cijqoi6e.d.cts.map +1 -1
- package/dist/trace-context-Cijqoi6e.d.ts.map +1 -1
- package/dist/trace-helpers.cjs +1 -1
- package/dist/trace-helpers.js +1 -1
- package/dist/{track-wc0HafS_.js → track-COUuU48p.js} +5 -5
- package/dist/track-COUuU48p.js.map +1 -0
- package/dist/{track-D59FfpL0.cjs → track-Cb3Q4QmS.cjs} +4 -2
- package/dist/track-Cb3Q4QmS.cjs.map +1 -0
- package/dist/validate.cjs +1 -1
- package/dist/validate.js +1 -1
- package/dist/webhook.cjs +1 -1
- package/dist/webhook.js +1 -1
- package/dist/workflow-distributed.cjs +1 -1
- package/dist/workflow-distributed.js +1 -1
- package/dist/workflow.cjs +3 -1
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts.map +1 -1
- package/dist/workflow.d.ts.map +1 -1
- package/dist/workflow.js +3 -3
- package/dist/workflow.js.map +1 -1
- package/dist/yaml-config.cjs +233 -4
- package/dist/yaml-config.cjs.map +1 -0
- package/dist/yaml-config.d.cts.map +1 -1
- package/dist/yaml-config.d.ts.map +1 -1
- package/dist/yaml-config.js +8 -7
- package/dist/yaml-config.js.map +1 -1
- package/package.json +6 -1
- package/skills/review-otel-patterns/SKILL.md +11 -8
- package/skills/review-otel-patterns/references/code-review.md +4 -3
- package/dist/functional-DtI0u4vx.js.map +0 -1
- package/dist/functional-zpzNLhky.cjs.map +0 -1
- package/dist/logger-thMPLpOG.cjs +0 -487
- package/dist/logger-thMPLpOG.cjs.map +0 -1
- package/dist/node-require-DF5QBX6z.cjs.map +0 -1
- package/dist/node-require-Db1oDpLj.js.map +0 -1
- package/dist/operation-context-C-2hmmtP.js.map +0 -1
- package/dist/operation-context-n4_obUwq.cjs.map +0 -1
- package/dist/stable-hash-BNTMrmdB.cjs.map +0 -1
- package/dist/stable-hash-Cg5cT34Q.js.map +0 -1
- package/dist/track-D59FfpL0.cjs.map +0 -1
- package/dist/track-wc0HafS_.js.map +0 -1
- package/dist/yaml-config-Ck2uB0Dp.cjs +0 -273
- 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
|
|
2
|
-
const require_init = require('./init-
|
|
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
|
-
|
|
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
|
|
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-
|
|
1
|
+
import { s as init } from "./init-BS2JVkrL.js";
|
|
2
2
|
import { loadYamlConfig } from "./yaml-config.js";
|
|
3
|
-
import
|
|
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
|
|
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
|
+
});
|
package/dist/correlation-id.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
-
const require_track = require('./track-
|
|
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":";;;
|
|
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":";;;
|
|
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"}
|
package/dist/correlation-id.js
CHANGED
|
@@ -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-
|
|
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 };
|
package/dist/decorators.cjs
CHANGED
|
@@ -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-
|
|
3
|
+
const require_track = require('./track-Cb3Q4QmS.cjs');
|
|
4
4
|
let _opentelemetry_api = require("@opentelemetry/api");
|
|
5
5
|
|
|
6
6
|
//#region src/decorators.ts
|
package/dist/decorators.js
CHANGED
|
@@ -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"}
|