@vymalo/opencode-otel 0.12.0 → 0.14.1

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.
@@ -3,20 +3,22 @@ import { type EnvSource } from "./config.js";
3
3
  import { type Logger } from "./logging.js";
4
4
  import { type ExporterFactories } from "./providers.js";
5
5
  export interface OtelPluginFactoryOptions {
6
- logger?: Logger;
7
- /** Injected environment; defaults to `process.env`. */
8
- env?: EnvSource;
9
- /** Substitute exporters (tests use in-memory ones). */
10
- exporters?: ExporterFactories;
11
- /** Injectable clock; defaults to `Date.now`. */
12
- now?: () => number;
13
- /** Skip `beforeExit`/`SIGINT`/`SIGTERM` registration (tests). */
14
- registerProcessHandlers?: boolean;
15
- /** Override the resolved host metadata (hostname, version) for tests. */
16
- hostInfo?: {
17
- hostname?: string;
18
- version?: string;
19
- };
6
+ logger?: Logger;
7
+ /** Injected environment; defaults to `process.env`. */
8
+ env?: EnvSource;
9
+ /** Substitute exporters (tests use in-memory ones). */
10
+ exporters?: ExporterFactories;
11
+ /** Injectable clock; defaults to `Date.now`. */
12
+ now?: () => number;
13
+ /** Skip `beforeExit`/`SIGINT`/`SIGTERM` registration (tests). */
14
+ registerProcessHandlers?: boolean;
15
+ /** Override the resolved host metadata (hostname, version) for tests. */
16
+ hostInfo?: {
17
+ hostname?: string;
18
+ version?: string;
19
+ };
20
+ /** How long a deferred resource attribute waits for its event. */
21
+ deferredTimeoutMs?: number;
20
22
  }
21
23
  export declare function createOtelPlugin(factoryOptions?: OtelPluginFactoryOptions): Plugin;
22
24
  export declare const OpencodeOtelPlugin: Plugin;
package/dist/opencode.js CHANGED
@@ -1,141 +1,184 @@
1
1
  import { hostname } from "node:os";
2
2
  import { resolveOtelConfig } from "./config.js";
3
+ import { deferredAttribute } from "./deferred.js";
3
4
  import { createJsonConsoleLogger, DEFAULT_LOG_LEVEL, fromOpenCodeLogLevel, LOG_LEVEL_PRIORITY } from "./logging.js";
4
5
  import { installTracePropagation } from "./propagation.js";
5
6
  import { buildResource, createProviders, describeError } from "./providers.js";
6
7
  import { TelemetryRecorder } from "./recorder.js";
8
+ import { readVcsInfo } from "./vcs.js";
7
9
  const PLUGIN_SERVICE_NAME = "opencode-otel-plugin";
8
10
  /**
9
- * Pipe plugin logs through OpenCode's `client.app.log` so they show up in the
10
- * host's structured log stream, with the JSON console as a reliable fallback.
11
- * Mirrors the rest of the suite.
12
- *
13
- * Note this is the plugin's *own* diagnostic logging — entirely separate from
14
- * the OTLP logs signal it exports.
15
- */
11
+ * Pipe plugin logs through OpenCode's `client.app.log` so they show up in the
12
+ * host's structured log stream, with the JSON console as a reliable fallback.
13
+ * Mirrors the rest of the suite.
14
+ *
15
+ * Note this is the plugin's *own* diagnostic logging — entirely separate from
16
+ * the OTLP logs signal it exports.
17
+ */
16
18
  function createOpenCodeLogger(client, getMinLevel) {
17
- const fallback = createJsonConsoleLogger("debug");
18
- const consoleAll = /^(1|true|yes|on)$/i.test(process.env.VYMALO_PLUGIN_CONSOLE_LOG ?? "");
19
- const write = (level, event, fields) => {
20
- if (LOG_LEVEL_PRIORITY[level] < LOG_LEVEL_PRIORITY[getMinLevel()]) {
21
- return;
22
- }
23
- if (consoleAll || level === "warn" || level === "error") {
24
- fallback[level](event, fields);
25
- }
26
- const hostLevel = level === "trace" ? "debug" : level;
27
- void client.app
28
- .log({
29
- body: { service: PLUGIN_SERVICE_NAME, level: hostLevel, message: event, extra: fields }
30
- })
31
- .catch(() => {
32
- /* best-effort */
33
- });
34
- };
35
- return {
36
- trace: (event, fields) => write("trace", event, fields),
37
- debug: (event, fields) => write("debug", event, fields),
38
- info: (event, fields) => write("info", event, fields),
39
- warn: (event, fields) => write("warn", event, fields),
40
- error: (event, fields) => write("error", event, fields)
41
- };
19
+ const fallback = createJsonConsoleLogger("debug");
20
+ const consoleAll = /^(1|true|yes|on)$/i.test(process.env.VYMALO_PLUGIN_CONSOLE_LOG ?? "");
21
+ const write = (level, event, fields) => {
22
+ if (LOG_LEVEL_PRIORITY[level] < LOG_LEVEL_PRIORITY[getMinLevel()]) {
23
+ return;
24
+ }
25
+ if (consoleAll || level === "warn" || level === "error") {
26
+ fallback[level](event, fields);
27
+ }
28
+ const hostLevel = level === "trace" ? "debug" : level;
29
+ void client.app.log({ body: {
30
+ service: PLUGIN_SERVICE_NAME,
31
+ level: hostLevel,
32
+ message: event,
33
+ extra: fields
34
+ } }).catch(() => {
35
+ /* best-effort */
36
+ });
37
+ };
38
+ return {
39
+ trace: (event, fields) => write("trace", event, fields),
40
+ debug: (event, fields) => write("debug", event, fields),
41
+ info: (event, fields) => write("info", event, fields),
42
+ warn: (event, fields) => write("warn", event, fields),
43
+ error: (event, fields) => write("error", event, fields)
44
+ };
42
45
  }
43
46
  /**
44
- * Drain buffered telemetry on process exit. The plugin API has no dispose hook,
45
- * so without this a short CLI invocation loses everything still in a batch
46
- * processor. Handlers are registered once and never keep the loop alive.
47
- */
48
- function registerExitHandlers(providers, logger) {
49
- let done = false;
50
- const drain = () => {
51
- if (done) {
52
- return;
53
- }
54
- done = true;
55
- void providers.shutdown().catch((error) => {
56
- logger.warn("otel_shutdown_failed", { error: describeError(error) });
57
- });
58
- };
59
- process.once("beforeExit", drain);
60
- process.once("SIGINT", drain);
61
- process.once("SIGTERM", drain);
47
+ * Drain buffered telemetry on process exit. The plugin API has no dispose hook,
48
+ * so without this a short CLI invocation loses everything still in a batch
49
+ * processor. Handlers are registered once and never keep the loop alive.
50
+ */
51
+ function registerExitHandlers(providers, logger, deferred) {
52
+ let done = false;
53
+ const drain = () => {
54
+ if (done) {
55
+ return;
56
+ }
57
+ done = true;
58
+ // Settle any still-pending resource attribute first. Exporters await those
59
+ // promises, and their timers are `unref`'d — so on `beforeExit` the timer
60
+ // may never fire and the shutdown would hang, losing everything buffered.
61
+ for (const attribute of deferred) {
62
+ attribute.abandon();
63
+ }
64
+ void providers.shutdown().catch((error) => {
65
+ logger.warn("otel_shutdown_failed", { error: describeError(error) });
66
+ });
67
+ };
68
+ process.once("beforeExit", drain);
69
+ process.once("SIGINT", drain);
70
+ process.once("SIGTERM", drain);
62
71
  }
63
72
  export function createOtelPlugin(factoryOptions = {}) {
64
- return async (input, pluginOptions) => {
65
- let currentLogLevel = DEFAULT_LOG_LEVEL;
66
- const logger = factoryOptions.logger ?? createOpenCodeLogger(input.client, () => currentLogLevel);
67
- const config = resolveOtelConfig(pluginOptions, factoryOptions.env ?? process.env);
68
- if (!config.active) {
69
- // No endpoint and no explicit exporter means the plugin was installed but
70
- // never configured — cost nothing rather than half-initializing.
71
- logger.info("otel_plugin_inactive", {
72
- enabled: config.enabled,
73
- reason: config.enabled ? "no_exporter_configured" : "disabled"
74
- });
75
- return {
76
- config: async (hostConfig) => {
77
- currentLogLevel = fromOpenCodeLogLevel(hostConfig.logLevel) ?? DEFAULT_LOG_LEVEL;
78
- }
79
- };
80
- }
81
- const resource = buildResource(config, {
82
- version: factoryOptions.hostInfo?.version,
83
- hostname: factoryOptions.hostInfo?.hostname ?? safeHostname(),
84
- projectName: input.project?.id,
85
- directory: input.directory,
86
- worktree: input.worktree
87
- });
88
- const providers = createProviders(config, resource, logger, factoryOptions.exporters);
89
- const recorder = new TelemetryRecorder({
90
- providers,
91
- config,
92
- logger,
93
- now: factoryOptions.now
94
- });
95
- if (factoryOptions.registerProcessHandlers !== false) {
96
- registerExitHandlers(providers, logger);
97
- }
98
- logger.info("otel_plugin_enabled", {
99
- serviceName: config.serviceName,
100
- exporters: config.exporters,
101
- endpoint: config.endpoint,
102
- includeSessionId: config.includeSessionId,
103
- filteredTools: [...config.filteredTools]
104
- });
105
- return {
106
- config: async (hostConfig) => {
107
- currentLogLevel = fromOpenCodeLogLevel(hostConfig.logLevel) ?? DEFAULT_LOG_LEVEL;
108
- if (config.propagateTraceContext) {
109
- const wrapped = installTracePropagation(hostConfig, {
110
- getContext: () => recorder.currentChatContext(),
111
- logger
112
- });
113
- logger.debug("otel_trace_propagation_ready", { providerCount: wrapped });
114
- }
115
- },
116
- event: async ({ event }) => {
117
- recorder.onEvent(event);
118
- },
119
- "chat.message": async (chatInput, chatOutput) => {
120
- recorder.onChatMessage(chatInput, chatOutput);
121
- },
122
- "tool.execute.before": async (toolInput) => {
123
- recorder.onToolBefore(toolInput);
124
- },
125
- "tool.execute.after": async (toolInput, toolOutput) => {
126
- recorder.onToolAfter(toolInput, toolOutput);
127
- }
128
- };
129
- };
73
+ return async (input, pluginOptions) => {
74
+ let currentLogLevel = DEFAULT_LOG_LEVEL;
75
+ const logger = factoryOptions.logger ?? createOpenCodeLogger(input.client, () => currentLogLevel);
76
+ const config = resolveOtelConfig(pluginOptions, factoryOptions.env ?? process.env);
77
+ if (!config.active) {
78
+ // No endpoint and no explicit exporter means the plugin was installed but
79
+ // never configured — cost nothing rather than half-initializing.
80
+ logger.info("otel_plugin_inactive", {
81
+ enabled: config.enabled,
82
+ reason: config.enabled ? "no_exporter_configured" : "disabled"
83
+ });
84
+ return { config: async (hostConfig) => {
85
+ currentLogLevel = fromOpenCodeLogLevel(hostConfig.logLevel) ?? DEFAULT_LOG_LEVEL;
86
+ } };
87
+ }
88
+ // `service.version` and the git branch only ever reach a plugin as events
89
+ // (`installation.updated` / `vcs.branch.updated`), which arrive after the
90
+ // resource is built. Deferred attributes bridge that, with a bounded wait
91
+ // so a host that never emits them cannot stall the first export.
92
+ const version = deferredAttribute(factoryOptions.deferredTimeoutMs);
93
+ const branch = deferredAttribute(factoryOptions.deferredTimeoutMs);
94
+ if (factoryOptions.hostInfo?.version) {
95
+ version.settle(factoryOptions.hostInfo.version);
96
+ }
97
+ // Read the checkout straight off disk. This is both richer and more
98
+ // reliable than waiting for `vcs.branch.updated`: it arrives before the
99
+ // first export instead of racing the deferral window, and it carries the
100
+ // remote and revision, which no event reports at all. The event stays as a
101
+ // fallback — `settle` keeps the first value, so whichever lands first wins.
102
+ const vcs = config.collectVcs ? await readVcsInfo(input.worktree ?? input.directory).catch(() => ({})) : {};
103
+ if (vcs.ref) {
104
+ branch.settle(vcs.ref);
105
+ }
106
+ const resource = buildResource(config, {
107
+ version: version.value,
108
+ hostname: factoryOptions.hostInfo?.hostname ?? safeHostname(),
109
+ projectName: input.project?.id,
110
+ directory: input.directory,
111
+ worktree: input.worktree,
112
+ branch: branch.value,
113
+ vcs
114
+ });
115
+ const providers = createProviders(config, resource, logger, factoryOptions.exporters);
116
+ const recorder = new TelemetryRecorder({
117
+ providers,
118
+ config,
119
+ logger,
120
+ now: factoryOptions.now,
121
+ resourceSinks: {
122
+ version: (value) => version.settle(value),
123
+ branch: (value) => branch.settle(value)
124
+ }
125
+ });
126
+ if (factoryOptions.registerProcessHandlers !== false) {
127
+ registerExitHandlers(providers, logger, [version, branch]);
128
+ }
129
+ logger.info("otel_plugin_enabled", {
130
+ serviceName: config.serviceName,
131
+ exporters: config.exporters,
132
+ endpoint: config.endpoint,
133
+ includeSessionId: config.includeSessionId,
134
+ filteredTools: [...config.filteredTools]
135
+ });
136
+ return {
137
+ config: async (hostConfig) => {
138
+ currentLogLevel = fromOpenCodeLogLevel(hostConfig.logLevel) ?? DEFAULT_LOG_LEVEL;
139
+ if (config.propagateTraceContext) {
140
+ const wrapped = installTracePropagation(hostConfig, {
141
+ getContext: () => recorder.currentChatContext(),
142
+ logger
143
+ });
144
+ logger.debug("otel_trace_propagation_ready", { providerCount: wrapped });
145
+ }
146
+ },
147
+ event: async ({ event }) => {
148
+ recorder.onEvent(event);
149
+ },
150
+ "chat.message": async (chatInput, chatOutput) => {
151
+ recorder.onChatMessage(chatInput, chatOutput);
152
+ },
153
+ "tool.execute.before": async (toolInput) => {
154
+ recorder.onToolBefore(toolInput);
155
+ },
156
+ "tool.execute.after": async (toolInput, toolOutput) => {
157
+ recorder.onToolAfter(toolInput, toolOutput);
158
+ },
159
+ "chat.params": async (paramsInput, paramsOutput) => {
160
+ recorder.onChatParams(paramsInput, paramsOutput);
161
+ },
162
+ "permission.ask": async (permissionInput, permissionOutput) => {
163
+ recorder.onPermissionAsk(permissionInput, permissionOutput);
164
+ },
165
+ "experimental.text.complete": async (textInput, textOutput) => {
166
+ recorder.onTextComplete(textInput, textOutput);
167
+ },
168
+ "experimental.compaction.autocontinue": async (compactionInput, compactionOutput) => {
169
+ recorder.onCompactionAutocontinue(compactionInput, compactionOutput);
170
+ }
171
+ };
172
+ };
130
173
  }
131
174
  function safeHostname() {
132
- try {
133
- return hostname() || undefined;
134
- }
135
- catch {
136
- return undefined;
137
- }
175
+ try {
176
+ return hostname() || undefined;
177
+ } catch {
178
+ return undefined;
179
+ }
138
180
  }
139
181
  export const OpencodeOtelPlugin = createOtelPlugin();
140
182
  export default OpencodeOtelPlugin;
183
+
141
184
  //# sourceMappingURL=opencode.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"opencode.js","sourceRoot":"","sources":["../src/opencode.ts"],"names":[],"mappings":"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"}
1
+ {"mappings":"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","names":[],"sources":["../src/opencode.ts"],"version":3,"file":"opencode.js","sourceRoot":""}
@@ -1,22 +1,22 @@
1
1
  import type { Context } from "@opentelemetry/api";
2
2
  import type { Logger } from "./logging.js";
3
3
  export interface ProviderConfigLike {
4
- options?: Record<string, unknown>;
4
+ options?: Record<string, unknown>;
5
5
  }
6
6
  export interface PropagationConfigInput {
7
- provider?: Record<string, ProviderConfigLike | undefined>;
7
+ provider?: Record<string, ProviderConfigLike | undefined>;
8
8
  }
9
9
  /**
10
- * Wrap every provider's `options.fetch` so outgoing model requests carry W3C
11
- * trace context, joining an OpenCode session and its gateway-side spans into
12
- * one trace.
13
- *
14
- * This is the same interception seam `@vymalo/opencode-ratelimit` uses — and it
15
- * composes the same way: the existing fetch is captured at install time and
16
- * delegated to, so stacking the two plugins in either order works.
17
- */
10
+ * Wrap every provider's `options.fetch` so outgoing model requests carry W3C
11
+ * trace context, joining an OpenCode session and its gateway-side spans into
12
+ * one trace.
13
+ *
14
+ * This is the same interception seam `@vymalo/opencode-ratelimit` uses — and it
15
+ * composes the same way: the existing fetch is captured at install time and
16
+ * delegated to, so stacking the two plugins in either order works.
17
+ */
18
18
  export declare function installTracePropagation(input: PropagationConfigInput, deps: {
19
- getContext: () => Context | undefined;
20
- logger: Logger;
21
- fetchImpl?: typeof fetch;
19
+ getContext: () => Context | undefined;
20
+ logger: Logger;
21
+ fetchImpl?: typeof fetch;
22
22
  }): number;
@@ -2,54 +2,56 @@ import { defaultTextMapSetter } from "@opentelemetry/api";
2
2
  import { W3CTraceContextPropagator } from "@opentelemetry/core";
3
3
  const propagator = new W3CTraceContextPropagator();
4
4
  /**
5
- * Wrap every provider's `options.fetch` so outgoing model requests carry W3C
6
- * trace context, joining an OpenCode session and its gateway-side spans into
7
- * one trace.
8
- *
9
- * This is the same interception seam `@vymalo/opencode-ratelimit` uses — and it
10
- * composes the same way: the existing fetch is captured at install time and
11
- * delegated to, so stacking the two plugins in either order works.
12
- */
5
+ * Wrap every provider's `options.fetch` so outgoing model requests carry W3C
6
+ * trace context, joining an OpenCode session and its gateway-side spans into
7
+ * one trace.
8
+ *
9
+ * This is the same interception seam `@vymalo/opencode-ratelimit` uses — and it
10
+ * composes the same way: the existing fetch is captured at install time and
11
+ * delegated to, so stacking the two plugins in either order works.
12
+ */
13
13
  export function installTracePropagation(input, deps) {
14
- const providers = input.provider;
15
- if (!providers) {
16
- return 0;
17
- }
18
- let wrapped = 0;
19
- for (const [providerId, providerConfig] of Object.entries(providers)) {
20
- if (!providerConfig) {
21
- continue;
22
- }
23
- const options = (providerConfig.options ??= {});
24
- const delegate = typeof options.fetch === "function"
25
- ? options.fetch
26
- : (deps.fetchImpl ?? globalThis.fetch);
27
- if (typeof delegate !== "function") {
28
- continue;
29
- }
30
- options.fetch = async (input_, init) => {
31
- const context = deps.getContext();
32
- if (!context) {
33
- return delegate(input_, init);
34
- }
35
- const carrier = {};
36
- propagator.inject(context, carrier, defaultTextMapSetter);
37
- if (Object.keys(carrier).length === 0) {
38
- return delegate(input_, init);
39
- }
40
- const headers = new Headers(init?.headers ?? {});
41
- // Never clobber an upstream traceparent — if something already set one,
42
- // it knows more about the request than we do.
43
- for (const [key, value] of Object.entries(carrier)) {
44
- if (!headers.has(key)) {
45
- headers.set(key, value);
46
- }
47
- }
48
- return delegate(input_, { ...init, headers });
49
- };
50
- wrapped += 1;
51
- deps.logger.trace("otel_trace_propagation_installed", { providerId });
52
- }
53
- return wrapped;
14
+ const providers = input.provider;
15
+ if (!providers) {
16
+ return 0;
17
+ }
18
+ let wrapped = 0;
19
+ for (const [providerId, providerConfig] of Object.entries(providers)) {
20
+ if (!providerConfig) {
21
+ continue;
22
+ }
23
+ const options = providerConfig.options ??= {};
24
+ const delegate = typeof options.fetch === "function" ? options.fetch : deps.fetchImpl ?? globalThis.fetch;
25
+ if (typeof delegate !== "function") {
26
+ continue;
27
+ }
28
+ options.fetch = async (input_, init) => {
29
+ const context = deps.getContext();
30
+ if (!context) {
31
+ return delegate(input_, init);
32
+ }
33
+ const carrier = {};
34
+ propagator.inject(context, carrier, defaultTextMapSetter);
35
+ if (Object.keys(carrier).length === 0) {
36
+ return delegate(input_, init);
37
+ }
38
+ const headers = new Headers(init?.headers ?? {});
39
+ // Never clobber an upstream traceparent — if something already set one,
40
+ // it knows more about the request than we do.
41
+ for (const [key, value] of Object.entries(carrier)) {
42
+ if (!headers.has(key)) {
43
+ headers.set(key, value);
44
+ }
45
+ }
46
+ return delegate(input_, {
47
+ ...init,
48
+ headers
49
+ });
50
+ };
51
+ wrapped += 1;
52
+ deps.logger.trace("otel_trace_propagation_installed", { providerId });
53
+ }
54
+ return wrapped;
54
55
  }
56
+
55
57
  //# sourceMappingURL=propagation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"propagation.js","sourceRoot":"","sources":["../src/propagation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAYhE,MAAM,UAAU,GAAG,IAAI,yBAAyB,EAAE,CAAC;AAEnD;;;;;;;;GAQG;AACH,MAAM,UAAU,uBAAuB,CACrC,KAA6B,EAC7B,IAAyF;IAEzF,MAAM,SAAS,GAAG,KAAK,CAAC,QAAQ,CAAC;IACjC,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,CAAC,CAAC;IACX,CAAC;IAED,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,CAAC,UAAU,EAAE,cAAc,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACrE,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,SAAS;QACX,CAAC;QACD,MAAM,OAAO,GAAG,CAAC,cAAc,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC;QAChD,MAAM,QAAQ,GACZ,OAAO,OAAO,CAAC,KAAK,KAAK,UAAU;YACjC,CAAC,CAAE,OAAO,CAAC,KAAsB;YACjC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;QAC3C,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE,CAAC;YACnC,SAAS;QACX,CAAC;QAED,OAAO,CAAC,KAAK,GAAG,KAAK,EACnB,MAAmC,EACnC,IAAkC,EACf,EAAE;YACrB,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;YAClC,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YAChC,CAAC;YACD,MAAM,OAAO,GAA2B,EAAE,CAAC;YAC3C,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,oBAAoB,CAAC,CAAC;YAC1D,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACtC,OAAO,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YAChC,CAAC;YACD,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC;YACjD,wEAAwE;YACxE,8CAA8C;YAC9C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;gBACnD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;oBACtB,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;gBAC1B,CAAC;YACH,CAAC;YACD,OAAO,QAAQ,CAAC,MAAM,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QAChD,CAAC,CAAC;QACF,OAAO,IAAI,CAAC,CAAC;QACb,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,kCAAkC,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;IACxE,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
1
+ {"mappings":"AACA,SAAS,4BAA4B;AACrC,SAAS,iCAAiC;AAY1C,MAAM,aAAa,IAAI,0BAA0B;;;;;;;;;;AAWjD,OAAO,SAAS,wBACd,OACA,MACQ;CACR,MAAM,YAAY,MAAM;CACxB,IAAI,CAAC,WAAW;EACd,OAAO;CACT;CAEA,IAAI,UAAU;CACd,KAAK,MAAM,CAAC,YAAY,mBAAmB,OAAO,QAAQ,SAAS,GAAG;EACpE,IAAI,CAAC,gBAAgB;GACnB;EACF;EACA,MAAM,UAAW,eAAe,YAAY,CAAC;EAC7C,MAAM,WACJ,OAAO,QAAQ,UAAU,aACpB,QAAQ,QACR,KAAK,aAAa,WAAW;EACpC,IAAI,OAAO,aAAa,YAAY;GAClC;EACF;EAEA,QAAQ,QAAQ,OACd,QACA,SACsB;GACtB,MAAM,UAAU,KAAK,WAAW;GAChC,IAAI,CAAC,SAAS;IACZ,OAAO,SAAS,QAAQ,IAAI;GAC9B;GACA,MAAM,UAAkC,CAAC;GACzC,WAAW,OAAO,SAAS,SAAS,oBAAoB;GACxD,IAAI,OAAO,KAAK,OAAO,CAAC,CAAC,WAAW,GAAG;IACrC,OAAO,SAAS,QAAQ,IAAI;GAC9B;GACA,MAAM,UAAU,IAAI,QAAQ,MAAM,WAAW,CAAC,CAAC;;;GAG/C,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,GAAG;IAClD,IAAI,CAAC,QAAQ,IAAI,GAAG,GAAG;KACrB,QAAQ,IAAI,KAAK,KAAK;IACxB;GACF;GACA,OAAO,SAAS,QAAQ;IAAE,GAAG;IAAM;GAAQ,CAAC;EAC9C;EACA,WAAW;EACX,KAAK,OAAO,MAAM,oCAAoC,EAAE,WAAW,CAAC;CACtE;CAEA,OAAO;AACT","names":[],"sources":["../src/propagation.ts"],"version":3,"file":"propagation.js","sourceRoot":""}