@probelabs/visor 0.1.104 → 0.1.106

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.
@@ -0,0 +1,331 @@
1
+ import {
2
+ __esm,
3
+ __export,
4
+ __require,
5
+ __toCommonJS
6
+ } from "./chunk-WMJKH4XE.mjs";
7
+
8
+ // src/telemetry/fallback-ndjson.ts
9
+ var fallback_ndjson_exports = {};
10
+ __export(fallback_ndjson_exports, {
11
+ emitNdjsonFallback: () => emitNdjsonFallback,
12
+ emitNdjsonSpanWithEvents: () => emitNdjsonSpanWithEvents,
13
+ flushNdjson: () => flushNdjson
14
+ });
15
+ import * as fs from "fs";
16
+ import * as path from "path";
17
+ function resolveTargetPath(outDir) {
18
+ if (process.env.VISOR_FALLBACK_TRACE_FILE) {
19
+ CURRENT_FILE = process.env.VISOR_FALLBACK_TRACE_FILE;
20
+ return CURRENT_FILE;
21
+ }
22
+ if (CURRENT_FILE) return CURRENT_FILE;
23
+ const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
24
+ CURRENT_FILE = path.join(outDir, `${ts}.ndjson`);
25
+ return CURRENT_FILE;
26
+ }
27
+ function isEnabled() {
28
+ if (process.env.VISOR_FALLBACK_TRACE_FILE) return true;
29
+ return process.env.VISOR_TELEMETRY_ENABLED === "true" && (process.env.VISOR_TELEMETRY_SINK || "file") === "file";
30
+ }
31
+ function appendAsync(outDir, line) {
32
+ writeChain = writeChain.then(async () => {
33
+ if (!dirReady) {
34
+ try {
35
+ await fs.promises.mkdir(outDir, { recursive: true });
36
+ } catch {
37
+ }
38
+ dirReady = true;
39
+ }
40
+ const target = resolveTargetPath(outDir);
41
+ await fs.promises.appendFile(target, line, "utf8");
42
+ }).catch(() => {
43
+ });
44
+ }
45
+ async function flushNdjson() {
46
+ try {
47
+ await writeChain;
48
+ } catch {
49
+ }
50
+ }
51
+ function emitNdjsonFallback(name, attrs) {
52
+ try {
53
+ if (!isEnabled()) return;
54
+ const outDir = process.env.VISOR_TRACE_DIR || path.join(process.cwd(), "output", "traces");
55
+ const line = JSON.stringify({ name, attributes: attrs }) + "\n";
56
+ appendAsync(outDir, line);
57
+ } catch {
58
+ }
59
+ }
60
+ function emitNdjsonSpanWithEvents(name, attrs, events) {
61
+ try {
62
+ if (!isEnabled()) return;
63
+ const outDir = process.env.VISOR_TRACE_DIR || path.join(process.cwd(), "output", "traces");
64
+ const line = JSON.stringify({ name, attributes: attrs, events }) + "\n";
65
+ appendAsync(outDir, line);
66
+ } catch {
67
+ }
68
+ }
69
+ var CURRENT_FILE, dirReady, writeChain;
70
+ var init_fallback_ndjson = __esm({
71
+ "src/telemetry/fallback-ndjson.ts"() {
72
+ "use strict";
73
+ CURRENT_FILE = null;
74
+ dirReady = false;
75
+ writeChain = Promise.resolve();
76
+ }
77
+ });
78
+
79
+ // src/telemetry/lazy-otel.ts
80
+ var otelApi = null;
81
+ var otelApiAttempted = false;
82
+ var OTEL_API_MODULE = "@opentelemetry/api";
83
+ function getOtelApi() {
84
+ if (otelApiAttempted) return otelApi;
85
+ otelApiAttempted = true;
86
+ try {
87
+ otelApi = (function(name) {
88
+ return __require(name);
89
+ })(OTEL_API_MODULE);
90
+ } catch {
91
+ otelApi = null;
92
+ }
93
+ return otelApi;
94
+ }
95
+ var trace = {
96
+ getTracer(name, version) {
97
+ const api = getOtelApi();
98
+ if (!api) return createNoOpTracer();
99
+ return api.trace.getTracer(name, version);
100
+ },
101
+ getSpan(context2) {
102
+ const api = getOtelApi();
103
+ if (!api) return void 0;
104
+ return api.trace.getSpan(context2);
105
+ },
106
+ getActiveSpan() {
107
+ const api = getOtelApi();
108
+ if (!api) return void 0;
109
+ return api.trace.getActiveSpan();
110
+ }
111
+ };
112
+ var context = {
113
+ active() {
114
+ const api = getOtelApi();
115
+ if (!api) return {};
116
+ return api.context.active();
117
+ },
118
+ with(context2, fn, thisArg, ...args) {
119
+ const api = getOtelApi();
120
+ if (!api) return fn.call(thisArg, ...args);
121
+ return api.context.with(context2, fn, thisArg, ...args);
122
+ }
123
+ };
124
+ var metrics = {
125
+ getMeter(name, version) {
126
+ const api = getOtelApi();
127
+ if (!api?.metrics) return createNoOpMeter();
128
+ return api.metrics.getMeter(name, version);
129
+ }
130
+ };
131
+ var SpanStatusCode = {
132
+ get UNSET() {
133
+ const api = getOtelApi();
134
+ return api?.SpanStatusCode?.UNSET ?? 0;
135
+ },
136
+ get OK() {
137
+ const api = getOtelApi();
138
+ return api?.SpanStatusCode?.OK ?? 1;
139
+ },
140
+ get ERROR() {
141
+ const api = getOtelApi();
142
+ return api?.SpanStatusCode?.ERROR ?? 2;
143
+ }
144
+ };
145
+ function createNoOpTracer() {
146
+ return {
147
+ startSpan: () => createNoOpSpan(),
148
+ // Support both OTel v1 and v2 overloads:
149
+ // - startActiveSpan(name, callback)
150
+ // - startActiveSpan(name, options, callback)
151
+ // - startActiveSpan(name, options, context, callback)
152
+ startActiveSpan: (name, arg2, arg3, arg4) => {
153
+ const span = createNoOpSpan();
154
+ let cb = void 0;
155
+ if (typeof arg2 === "function") cb = arg2;
156
+ else if (typeof arg3 === "function") cb = arg3;
157
+ else if (typeof arg4 === "function") cb = arg4;
158
+ if (typeof cb === "function") {
159
+ try {
160
+ return cb(span);
161
+ } catch {
162
+ return void 0;
163
+ }
164
+ }
165
+ return span;
166
+ }
167
+ };
168
+ }
169
+ function createNoOpSpan() {
170
+ return {
171
+ spanContext: () => ({ traceId: "", spanId: "", traceFlags: 0 }),
172
+ setAttribute: () => {
173
+ },
174
+ setAttributes: () => {
175
+ },
176
+ addEvent: () => {
177
+ },
178
+ setStatus: () => {
179
+ },
180
+ updateName: () => {
181
+ },
182
+ end: () => {
183
+ },
184
+ isRecording: () => false,
185
+ recordException: () => {
186
+ }
187
+ };
188
+ }
189
+ function createNoOpMeter() {
190
+ return {
191
+ createCounter: () => ({ add: () => {
192
+ } }),
193
+ createHistogram: () => ({ record: () => {
194
+ } }),
195
+ createUpDownCounter: () => ({ add: () => {
196
+ } }),
197
+ createObservableGauge: () => {
198
+ },
199
+ createObservableCounter: () => {
200
+ },
201
+ createObservableUpDownCounter: () => {
202
+ }
203
+ };
204
+ }
205
+
206
+ // src/telemetry/trace-helpers.ts
207
+ function getTracer() {
208
+ return trace.getTracer("visor");
209
+ }
210
+ async function withActiveSpan(name, attrs, fn) {
211
+ const tracer = getTracer();
212
+ return await new Promise((resolve, reject) => {
213
+ const callback = async (span) => {
214
+ try {
215
+ const res = await fn(span);
216
+ resolve(res);
217
+ } catch (err) {
218
+ try {
219
+ if (err instanceof Error) span.recordException(err);
220
+ span.setStatus({ code: SpanStatusCode.ERROR });
221
+ } catch {
222
+ }
223
+ reject(err);
224
+ } finally {
225
+ try {
226
+ span.end();
227
+ } catch {
228
+ }
229
+ }
230
+ };
231
+ const options = attrs ? { attributes: attrs } : {};
232
+ tracer.startActiveSpan(name, options, callback);
233
+ });
234
+ }
235
+ function addEvent(name, attrs) {
236
+ const span = trace.getSpan(context.active());
237
+ if (span) {
238
+ try {
239
+ span.addEvent(name, attrs);
240
+ } catch {
241
+ }
242
+ }
243
+ try {
244
+ const { emitNdjsonSpanWithEvents: emitNdjsonSpanWithEvents2 } = (init_fallback_ndjson(), __toCommonJS(fallback_ndjson_exports));
245
+ emitNdjsonSpanWithEvents2("visor.event", {}, [{ name, attrs }]);
246
+ if (name === "fail_if.triggered") {
247
+ emitNdjsonSpanWithEvents2("visor.event", {}, [
248
+ { name: "fail_if.evaluated", attrs },
249
+ { name: "fail_if.triggered", attrs }
250
+ ]);
251
+ }
252
+ } catch {
253
+ }
254
+ }
255
+
256
+ // src/telemetry/metrics.ts
257
+ var initialized = false;
258
+ var meter = metrics.getMeter("visor");
259
+ var TEST_ENABLED = process.env.VISOR_TEST_METRICS === "true";
260
+ var TEST_SNAPSHOT = { fail_if_triggered: 0 };
261
+ var checkDurationHist;
262
+ var providerDurationHist;
263
+ var foreachDurationHist;
264
+ var issuesCounter;
265
+ var activeChecks;
266
+ var failIfCounter;
267
+ var diagramBlocks;
268
+ function ensureInstruments() {
269
+ if (initialized) return;
270
+ try {
271
+ checkDurationHist = meter.createHistogram("visor.check.duration_ms", {
272
+ description: "Duration of a check execution in milliseconds",
273
+ unit: "ms"
274
+ });
275
+ providerDurationHist = meter.createHistogram("visor.provider.duration_ms", {
276
+ description: "Duration of provider execution in milliseconds",
277
+ unit: "ms"
278
+ });
279
+ foreachDurationHist = meter.createHistogram("visor.foreach.item.duration_ms", {
280
+ description: "Duration of a forEach item execution in milliseconds",
281
+ unit: "ms"
282
+ });
283
+ issuesCounter = meter.createCounter("visor.check.issues", {
284
+ description: "Number of issues produced by checks",
285
+ unit: "1"
286
+ });
287
+ activeChecks = meter.createUpDownCounter("visor.run.active_checks", {
288
+ description: "Number of checks actively running",
289
+ unit: "1"
290
+ });
291
+ failIfCounter = meter.createCounter("visor.fail_if.triggered", {
292
+ description: "Number of times fail_if condition triggered",
293
+ unit: "1"
294
+ });
295
+ diagramBlocks = meter.createCounter("visor.diagram.blocks", {
296
+ description: "Number of Mermaid diagram blocks emitted",
297
+ unit: "1"
298
+ });
299
+ initialized = true;
300
+ } catch {
301
+ }
302
+ }
303
+ function addFailIfTriggered(check, scope) {
304
+ ensureInstruments();
305
+ try {
306
+ failIfCounter?.add(1, { "visor.check.id": check, scope });
307
+ } catch {
308
+ }
309
+ if (TEST_ENABLED) TEST_SNAPSHOT.fail_if_triggered++;
310
+ }
311
+ function addDiagramBlock(origin) {
312
+ ensureInstruments();
313
+ try {
314
+ diagramBlocks?.add(1, { origin });
315
+ } catch {
316
+ }
317
+ }
318
+
319
+ export {
320
+ trace,
321
+ context,
322
+ emitNdjsonFallback,
323
+ emitNdjsonSpanWithEvents,
324
+ fallback_ndjson_exports,
325
+ init_fallback_ndjson,
326
+ withActiveSpan,
327
+ addEvent,
328
+ addFailIfTriggered,
329
+ addDiagramBlock
330
+ };
331
+ //# sourceMappingURL=chunk-U7X54EMV.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/telemetry/fallback-ndjson.ts","../../src/telemetry/lazy-otel.ts","../../src/telemetry/trace-helpers.ts","../../src/telemetry/metrics.ts"],"sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\n\nlet CURRENT_FILE: string | null = null;\nlet dirReady = false;\nlet writeChain: Promise<void> = Promise.resolve();\nfunction resolveTargetPath(outDir: string): string {\n if (process.env.VISOR_FALLBACK_TRACE_FILE) {\n CURRENT_FILE = process.env.VISOR_FALLBACK_TRACE_FILE;\n return CURRENT_FILE;\n }\n if (CURRENT_FILE) return CURRENT_FILE;\n const ts = new Date().toISOString().replace(/[:.]/g, '-');\n CURRENT_FILE = path.join(outDir, `${ts}.ndjson`);\n return CURRENT_FILE;\n}\n\nfunction isEnabled(): boolean {\n // Enable when CLI set a fallback file (serverless mode), or when explicit file sink is enabled\n if (process.env.VISOR_FALLBACK_TRACE_FILE) return true;\n return (\n process.env.VISOR_TELEMETRY_ENABLED === 'true' &&\n (process.env.VISOR_TELEMETRY_SINK || 'file') === 'file'\n );\n}\n\nfunction appendAsync(outDir: string, line: string): void {\n writeChain = writeChain\n .then(async () => {\n if (!dirReady) {\n try {\n await fs.promises.mkdir(outDir, { recursive: true });\n } catch {}\n dirReady = true;\n }\n const target = resolveTargetPath(outDir);\n await fs.promises.appendFile(target, line, 'utf8');\n })\n .catch(() => {});\n}\n\nexport async function flushNdjson(): Promise<void> {\n try {\n await writeChain;\n } catch {}\n}\n\nexport function emitNdjsonFallback(name: string, attrs: Record<string, unknown>): void {\n try {\n if (!isEnabled()) return;\n const outDir = process.env.VISOR_TRACE_DIR || path.join(process.cwd(), 'output', 'traces');\n const line = JSON.stringify({ name, attributes: attrs }) + '\\n';\n appendAsync(outDir, line);\n } catch {\n // ignore\n }\n}\n\nexport function emitNdjsonSpanWithEvents(\n name: string,\n attrs: Record<string, unknown>,\n events: Array<{ name: string; attrs?: Record<string, unknown> }>\n): void {\n try {\n if (!isEnabled()) return;\n const outDir = process.env.VISOR_TRACE_DIR || path.join(process.cwd(), 'output', 'traces');\n const line = JSON.stringify({ name, attributes: attrs, events }) + '\\n';\n appendAsync(outDir, line);\n } catch {\n // ignore\n }\n}\n","/**\n * Lazy-loading wrapper for OpenTelemetry API.\n * Returns no-op implementations if OpenTelemetry is not installed.\n * Uses hardcoded module name for security - no dynamic module loading.\n */\n\nlet otelApi: any = null;\nlet otelApiAttempted = false;\n\n// Hardcoded allowed module name to prevent module loading attacks\nconst OTEL_API_MODULE = '@opentelemetry/api';\n\nfunction getOtelApi() {\n if (otelApiAttempted) return otelApi;\n otelApiAttempted = true;\n\n try {\n // Security: Only load the specific @opentelemetry/api module\n // Use dynamic require to prevent bundlers from including this module\n\n otelApi = (function (name: string) {\n return require(name);\n })(OTEL_API_MODULE);\n } catch {\n // OpenTelemetry not installed - provide no-op implementations\n otelApi = null;\n }\n\n return otelApi;\n}\n\n// Export lazy-loaded trace API\nexport const trace = {\n getTracer(name: string, version?: string) {\n const api = getOtelApi();\n if (!api) return createNoOpTracer();\n return api.trace.getTracer(name, version);\n },\n getSpan(context: any) {\n const api = getOtelApi();\n if (!api) return undefined;\n return api.trace.getSpan(context);\n },\n getActiveSpan() {\n const api = getOtelApi();\n if (!api) return undefined;\n return api.trace.getActiveSpan();\n },\n};\n\n// Export lazy-loaded context API\nexport const context = {\n active() {\n const api = getOtelApi();\n if (!api) return {};\n return api.context.active();\n },\n with(context: any, fn: Function, thisArg?: any, ...args: any[]) {\n const api = getOtelApi();\n if (!api) return fn.call(thisArg, ...args);\n return api.context.with(context, fn, thisArg, ...args);\n },\n};\n\n// Export lazy-loaded metrics API\nexport const metrics = {\n getMeter(name: string, version?: string) {\n const api = getOtelApi();\n if (!api?.metrics) return createNoOpMeter();\n return api.metrics.getMeter(name, version);\n },\n};\n\n// Export types and enums\nexport const SpanStatusCode = {\n get UNSET() {\n const api = getOtelApi();\n return api?.SpanStatusCode?.UNSET ?? 0;\n },\n get OK() {\n const api = getOtelApi();\n return api?.SpanStatusCode?.OK ?? 1;\n },\n get ERROR() {\n const api = getOtelApi();\n return api?.SpanStatusCode?.ERROR ?? 2;\n },\n};\n\nexport const SpanKind = {\n get INTERNAL() {\n const api = getOtelApi();\n return api?.SpanKind?.INTERNAL ?? 0;\n },\n get SERVER() {\n const api = getOtelApi();\n return api?.SpanKind?.SERVER ?? 1;\n },\n get CLIENT() {\n const api = getOtelApi();\n return api?.SpanKind?.CLIENT ?? 2;\n },\n get PRODUCER() {\n const api = getOtelApi();\n return api?.SpanKind?.PRODUCER ?? 3;\n },\n get CONSUMER() {\n const api = getOtelApi();\n return api?.SpanKind?.CONSUMER ?? 4;\n },\n};\n\n// Export diag API\nexport const diag = {\n setLogger(logger: any, level?: any) {\n const api = getOtelApi();\n if (!api) return;\n return api.diag.setLogger(logger, level);\n },\n};\n\n// Lazy-loaded DiagConsoleLogger and DiagLogLevel for consistency\nexport const DiagConsoleLogger = {\n get() {\n const api = getOtelApi();\n return api?.DiagConsoleLogger;\n },\n};\n\nexport const DiagLogLevel = {\n get NONE() {\n const api = getOtelApi();\n return api?.DiagLogLevel?.NONE ?? 0;\n },\n get ERROR() {\n const api = getOtelApi();\n return api?.DiagLogLevel?.ERROR ?? 30;\n },\n get WARN() {\n const api = getOtelApi();\n return api?.DiagLogLevel?.WARN ?? 50;\n },\n get INFO() {\n const api = getOtelApi();\n return api?.DiagLogLevel?.INFO ?? 60;\n },\n get DEBUG() {\n const api = getOtelApi();\n return api?.DiagLogLevel?.DEBUG ?? 70;\n },\n get VERBOSE() {\n const api = getOtelApi();\n return api?.DiagLogLevel?.VERBOSE ?? 80;\n },\n get ALL() {\n const api = getOtelApi();\n return api?.DiagLogLevel?.ALL ?? 9999;\n },\n};\n\n// Type exports for TypeScript\nexport type Span = any;\nexport type Attributes = Record<string, any>;\nexport type HrTime = [number, number];\n\n// No-op implementations\nfunction createNoOpTracer() {\n return {\n startSpan: () => createNoOpSpan(),\n // Support both OTel v1 and v2 overloads:\n // - startActiveSpan(name, callback)\n // - startActiveSpan(name, options, callback)\n // - startActiveSpan(name, options, context, callback)\n startActiveSpan: (name: string, arg2?: any, arg3?: any, arg4?: any) => {\n const span = createNoOpSpan();\n let cb: any = undefined;\n if (typeof arg2 === 'function') cb = arg2;\n else if (typeof arg3 === 'function') cb = arg3;\n else if (typeof arg4 === 'function') cb = arg4;\n if (typeof cb === 'function') {\n try {\n return cb(span);\n } catch {\n // swallow errors in no-op implementation\n return undefined;\n }\n }\n // No callback supplied: return a no-op span like the real API would\n return span;\n },\n };\n}\n\nfunction createNoOpSpan() {\n return {\n spanContext: () => ({ traceId: '', spanId: '', traceFlags: 0 }),\n setAttribute: () => {},\n setAttributes: () => {},\n addEvent: () => {},\n setStatus: () => {},\n updateName: () => {},\n end: () => {},\n isRecording: () => false,\n recordException: () => {},\n };\n}\n\nfunction createNoOpMeter() {\n return {\n createCounter: () => ({ add: () => {} }),\n createHistogram: () => ({ record: () => {} }),\n createUpDownCounter: () => ({ add: () => {} }),\n createObservableGauge: () => {},\n createObservableCounter: () => {},\n createObservableUpDownCounter: () => {},\n };\n}\n","import { context as otContext, Span, SpanStatusCode, trace, Attributes } from './lazy-otel';\n\nexport function getTracer() {\n return trace.getTracer('visor');\n}\n\nexport async function withActiveSpan<T>(\n name: string,\n attrs: Record<string, unknown> | undefined,\n fn: (span: Span) => Promise<T>\n): Promise<T> {\n const tracer = getTracer();\n // Preserve parent context via tracer API; avoid logging parent IDs to stdout\n // Avoid noisy stdout logs that break JSON consumers\n return await new Promise<T>((resolve, reject) => {\n const callback = async (span: Span) => {\n // console.debug(`[trace] Span callback invoked for: [trace_id=${ctx.traceId} span_id=${ctx.spanId}] ${name} span: true`);\n try {\n const res = await fn(span);\n // console.debug('[trace] Span execution completed for:', name);\n resolve(res);\n } catch (err) {\n // console.debug('[trace] Span execution errored for:', name, err);\n try {\n if (err instanceof Error) span.recordException(err);\n span.setStatus({ code: SpanStatusCode.ERROR });\n } catch {}\n reject(err);\n } finally {\n try {\n // console.debug('[trace] Ending span:', name);\n span.end();\n } catch {}\n }\n };\n // startActiveSpan should use the current active context to set parent automatically\n const options = attrs ? { attributes: attrs as Attributes } : {};\n tracer.startActiveSpan(name, options, callback);\n });\n}\n\nexport function addEvent(name: string, attrs?: Record<string, unknown>): void {\n const span = trace.getSpan(otContext.active());\n if (span) {\n try {\n span.addEvent(name, attrs as Attributes);\n } catch {\n // ignore\n }\n }\n // Fallback NDJSON emission for serverless/file sink when SDK may be inactive\n try {\n const { emitNdjsonSpanWithEvents } = require('./fallback-ndjson');\n emitNdjsonSpanWithEvents('visor.event', {}, [{ name, attrs }]);\n if (name === 'fail_if.triggered') {\n emitNdjsonSpanWithEvents('visor.event', {}, [\n { name: 'fail_if.evaluated', attrs },\n { name: 'fail_if.triggered', attrs },\n ]);\n }\n } catch {}\n}\n\nexport function setSpanAttributes(attrs: Record<string, unknown>): void {\n const span = trace.getSpan(otContext.active());\n if (!span) return;\n try {\n for (const [k, v] of Object.entries(attrs)) span.setAttribute(k, v as never);\n } catch {\n // ignore\n }\n}\n\nexport function setSpanError(err: unknown): void {\n const span = trace.getSpan(otContext.active());\n if (!span) return;\n try {\n if (err instanceof Error) span.recordException(err);\n span.setStatus({ code: SpanStatusCode.ERROR });\n } catch {\n // ignore\n }\n}\n\n// Internal helper for tests: write a minimal run marker to NDJSON when using file sink\nlet __ndjsonPath: string | null = null;\nexport function __getOrCreateNdjsonPath(): string | null {\n try {\n // If sink is explicitly set to non-file, skip. If unset, still allow when a trace dir/file is configured.\n if (process.env.VISOR_TELEMETRY_SINK && process.env.VISOR_TELEMETRY_SINK !== 'file')\n return null;\n const path = require('path');\n const fs = require('fs');\n // Prefer explicit fallback file path if set by the CLI\n if (process.env.VISOR_FALLBACK_TRACE_FILE) {\n __ndjsonPath = process.env.VISOR_FALLBACK_TRACE_FILE;\n const dir = path.dirname(__ndjsonPath);\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });\n return __ndjsonPath;\n }\n const outDir = process.env.VISOR_TRACE_DIR || path.join(process.cwd(), 'output', 'traces');\n if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });\n if (!__ndjsonPath) {\n const ts = new Date().toISOString().replace(/[:.]/g, '-');\n __ndjsonPath = path.join(outDir, `${ts}.ndjson`);\n }\n return __ndjsonPath;\n } catch {\n return null;\n }\n}\nexport function _appendRunMarker(): void {\n try {\n const fs = require('fs');\n const p = __getOrCreateNdjsonPath();\n if (!p) return;\n const line = { name: 'visor.run', attributes: { started: true } };\n fs.appendFileSync(p, JSON.stringify(line) + '\\n', 'utf8');\n } catch {}\n}\n","import { metrics } from './lazy-otel';\n\nlet initialized = false;\nconst meter = metrics.getMeter('visor');\n\n// Test helpers (enabled with VISOR_TEST_METRICS=true)\nconst TEST_ENABLED = process.env.VISOR_TEST_METRICS === 'true';\nconst TEST_SNAPSHOT: { [k: string]: number } = { fail_if_triggered: 0 };\n\n// Instruments (lazily created when first used)\nlet checkDurationHist: ReturnType<typeof meter.createHistogram> | undefined;\nlet providerDurationHist: ReturnType<typeof meter.createHistogram> | undefined;\nlet foreachDurationHist: ReturnType<typeof meter.createHistogram> | undefined;\nlet issuesCounter: ReturnType<typeof meter.createCounter> | undefined;\nlet activeChecks: ReturnType<typeof meter.createUpDownCounter> | undefined;\nlet failIfCounter: ReturnType<typeof meter.createCounter> | undefined;\nlet diagramBlocks: ReturnType<typeof meter.createCounter> | undefined;\n\nfunction ensureInstruments() {\n if (initialized) return;\n try {\n checkDurationHist = meter.createHistogram('visor.check.duration_ms', {\n description: 'Duration of a check execution in milliseconds',\n unit: 'ms',\n });\n providerDurationHist = meter.createHistogram('visor.provider.duration_ms', {\n description: 'Duration of provider execution in milliseconds',\n unit: 'ms',\n });\n foreachDurationHist = meter.createHistogram('visor.foreach.item.duration_ms', {\n description: 'Duration of a forEach item execution in milliseconds',\n unit: 'ms',\n });\n issuesCounter = meter.createCounter('visor.check.issues', {\n description: 'Number of issues produced by checks',\n unit: '1',\n });\n activeChecks = meter.createUpDownCounter('visor.run.active_checks', {\n description: 'Number of checks actively running',\n unit: '1',\n });\n failIfCounter = meter.createCounter('visor.fail_if.triggered', {\n description: 'Number of times fail_if condition triggered',\n unit: '1',\n });\n diagramBlocks = meter.createCounter('visor.diagram.blocks', {\n description: 'Number of Mermaid diagram blocks emitted',\n unit: '1',\n });\n initialized = true;\n } catch {\n // Metrics may be unavailable if SDK not initialized; ignore gracefully\n }\n}\n\nexport function recordCheckDuration(check: string, durationMs: number, group?: string) {\n ensureInstruments();\n try {\n checkDurationHist?.record(durationMs, {\n 'visor.check.id': check,\n 'visor.check.group': group || 'default',\n });\n } catch {}\n}\n\nexport function recordProviderDuration(check: string, providerType: string, durationMs: number) {\n ensureInstruments();\n try {\n providerDurationHist?.record(durationMs, {\n 'visor.check.id': check,\n 'visor.provider.type': providerType,\n });\n } catch {}\n}\n\nexport function recordForEachDuration(\n check: string,\n index: number,\n total: number,\n durationMs: number\n) {\n ensureInstruments();\n try {\n foreachDurationHist?.record(durationMs, {\n 'visor.check.id': check,\n 'visor.foreach.index': index,\n 'visor.foreach.total': total,\n });\n } catch {}\n}\n\nexport function addIssues(check: string, severity: string, count = 1) {\n ensureInstruments();\n try {\n issuesCounter?.add(count, {\n 'visor.check.id': check,\n severity,\n });\n } catch {}\n}\n\nexport function incActiveCheck(check: string) {\n ensureInstruments();\n try {\n activeChecks?.add(1, { 'visor.check.id': check });\n } catch {}\n}\n\nexport function decActiveCheck(check: string) {\n ensureInstruments();\n try {\n activeChecks?.add(-1, { 'visor.check.id': check });\n } catch {}\n}\n\nexport function addFailIfTriggered(check: string, scope: 'global' | 'check') {\n ensureInstruments();\n try {\n failIfCounter?.add(1, { 'visor.check.id': check, scope });\n } catch {}\n if (TEST_ENABLED) TEST_SNAPSHOT.fail_if_triggered++;\n}\n\nexport function addDiagramBlock(origin: 'content' | 'issue') {\n ensureInstruments();\n try {\n diagramBlocks?.add(1, { origin });\n } catch {}\n}\n\nexport function getTestMetricsSnapshot() {\n return { ...TEST_SNAPSHOT };\n}\n\nexport function resetTestMetricsSnapshot() {\n Object.keys(TEST_SNAPSHOT).forEach(k => (TEST_SNAPSHOT[k] = 0));\n}\n"],"mappings":"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","names":["context","emitNdjsonSpanWithEvents"]}
@@ -0,0 +1,61 @@
1
+ import {
2
+ addDiagramBlock,
3
+ addEvent
4
+ } from "./chunk-U7X54EMV.mjs";
5
+ import "./chunk-WMJKH4XE.mjs";
6
+
7
+ // src/utils/mermaid-telemetry.ts
8
+ import * as fs from "fs";
9
+ import * as path from "path";
10
+ var MERMAID_RE = /```mermaid\s*\n([\s\S]*?)\n```/gi;
11
+ function emitMermaidFromMarkdown(checkName, markdown, origin) {
12
+ if (!markdown || typeof markdown !== "string") return 0;
13
+ let m;
14
+ let count = 0;
15
+ MERMAID_RE.lastIndex = 0;
16
+ while ((m = MERMAID_RE.exec(markdown)) != null) {
17
+ const code = (m[1] || "").trim();
18
+ if (code) {
19
+ try {
20
+ addEvent("diagram.block", { check: checkName, origin, code });
21
+ addDiagramBlock(origin);
22
+ if (process.env.VISOR_TRACE_REPORT === "true") {
23
+ const outDir = process.env.VISOR_TRACE_DIR || path.join(process.cwd(), "output", "traces");
24
+ try {
25
+ if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
26
+ const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
27
+ const jsonPath = path.join(outDir, `${ts}.trace.json`);
28
+ const htmlPath = path.join(outDir, `${ts}.report.html`);
29
+ let data = { spans: [] };
30
+ if (fs.existsSync(jsonPath)) {
31
+ try {
32
+ data = JSON.parse(fs.readFileSync(jsonPath, "utf8"));
33
+ } catch {
34
+ data = { spans: [] };
35
+ }
36
+ }
37
+ data.spans.push({
38
+ events: [{ name: "diagram.block", attrs: { check: checkName, origin, code } }]
39
+ });
40
+ fs.writeFileSync(jsonPath, JSON.stringify(data, null, 2), "utf8");
41
+ if (!fs.existsSync(htmlPath)) {
42
+ fs.writeFileSync(
43
+ htmlPath,
44
+ '<!doctype html><html><head><meta charset="utf-8"/><title>Visor Trace Report</title></head><body><h2>Visor Trace Report</h2></body></html>',
45
+ "utf8"
46
+ );
47
+ }
48
+ } catch {
49
+ }
50
+ }
51
+ count++;
52
+ } catch {
53
+ }
54
+ }
55
+ }
56
+ return count;
57
+ }
58
+ export {
59
+ emitMermaidFromMarkdown
60
+ };
61
+ //# sourceMappingURL=mermaid-telemetry-SN6A2TKW.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/mermaid-telemetry.ts"],"sourcesContent":["import { addEvent } from '../telemetry/trace-helpers';\nimport { addDiagramBlock } from '../telemetry/metrics';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\nconst MERMAID_RE = /```mermaid\\s*\\n([\\s\\S]*?)\\n```/gi;\n\nexport type DiagramOrigin = 'content' | 'issue';\n\nexport function emitMermaidFromMarkdown(\n checkName: string,\n markdown: string,\n origin: DiagramOrigin\n): number {\n if (!markdown || typeof markdown !== 'string') return 0;\n let m: RegExpExecArray | null;\n let count = 0;\n MERMAID_RE.lastIndex = 0;\n while ((m = MERMAID_RE.exec(markdown)) != null) {\n const code = (m[1] || '').trim();\n if (code) {\n try {\n addEvent('diagram.block', { check: checkName, origin, code });\n addDiagramBlock(origin);\n // Fallback writer for environments where OTel SDK isn't active\n if (process.env.VISOR_TRACE_REPORT === 'true') {\n const outDir =\n process.env.VISOR_TRACE_DIR || path.join(process.cwd(), 'output', 'traces');\n try {\n if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });\n const ts = new Date().toISOString().replace(/[:.]/g, '-');\n const jsonPath = path.join(outDir, `${ts}.trace.json`);\n const htmlPath = path.join(outDir, `${ts}.report.html`);\n // Append or create minimal trace JSON\n let data: {\n spans: Array<{ events: Array<{ name: string; attrs: Record<string, unknown> }> }>;\n } = { spans: [] };\n if (fs.existsSync(jsonPath)) {\n try {\n data = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));\n } catch {\n data = { spans: [] };\n }\n }\n data.spans.push({\n events: [{ name: 'diagram.block', attrs: { check: checkName, origin, code } }],\n });\n fs.writeFileSync(jsonPath, JSON.stringify(data, null, 2), 'utf8');\n // Ensure minimal HTML report exists\n if (!fs.existsSync(htmlPath)) {\n fs.writeFileSync(\n htmlPath,\n '<!doctype html><html><head><meta charset=\"utf-8\"/><title>Visor Trace Report</title></head><body><h2>Visor Trace Report</h2></body></html>',\n 'utf8'\n );\n }\n } catch {}\n }\n count++;\n } catch {\n // ignore telemetry failures\n }\n }\n }\n return count;\n}\n"],"mappings":";;;;;;;AAEA,YAAY,QAAQ;AACpB,YAAY,UAAU;AAEtB,IAAM,aAAa;AAIZ,SAAS,wBACd,WACA,UACA,QACQ;AACR,MAAI,CAAC,YAAY,OAAO,aAAa,SAAU,QAAO;AACtD,MAAI;AACJ,MAAI,QAAQ;AACZ,aAAW,YAAY;AACvB,UAAQ,IAAI,WAAW,KAAK,QAAQ,MAAM,MAAM;AAC9C,UAAM,QAAQ,EAAE,CAAC,KAAK,IAAI,KAAK;AAC/B,QAAI,MAAM;AACR,UAAI;AACF,iBAAS,iBAAiB,EAAE,OAAO,WAAW,QAAQ,KAAK,CAAC;AAC5D,wBAAgB,MAAM;AAEtB,YAAI,QAAQ,IAAI,uBAAuB,QAAQ;AAC7C,gBAAM,SACJ,QAAQ,IAAI,mBAAwB,UAAK,QAAQ,IAAI,GAAG,UAAU,QAAQ;AAC5E,cAAI;AACF,gBAAI,CAAI,cAAW,MAAM,EAAG,CAAG,aAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;AACpE,kBAAM,MAAK,oBAAI,KAAK,GAAE,YAAY,EAAE,QAAQ,SAAS,GAAG;AACxD,kBAAM,WAAgB,UAAK,QAAQ,GAAG,EAAE,aAAa;AACrD,kBAAM,WAAgB,UAAK,QAAQ,GAAG,EAAE,cAAc;AAEtD,gBAAI,OAEA,EAAE,OAAO,CAAC,EAAE;AAChB,gBAAO,cAAW,QAAQ,GAAG;AAC3B,kBAAI;AACF,uBAAO,KAAK,MAAS,gBAAa,UAAU,MAAM,CAAC;AAAA,cACrD,QAAQ;AACN,uBAAO,EAAE,OAAO,CAAC,EAAE;AAAA,cACrB;AAAA,YACF;AACA,iBAAK,MAAM,KAAK;AAAA,cACd,QAAQ,CAAC,EAAE,MAAM,iBAAiB,OAAO,EAAE,OAAO,WAAW,QAAQ,KAAK,EAAE,CAAC;AAAA,YAC/E,CAAC;AACD,YAAG,iBAAc,UAAU,KAAK,UAAU,MAAM,MAAM,CAAC,GAAG,MAAM;AAEhE,gBAAI,CAAI,cAAW,QAAQ,GAAG;AAC5B,cAAG;AAAA,gBACD;AAAA,gBACA;AAAA,gBACA;AAAA,cACF;AAAA,YACF;AAAA,UACF,QAAQ;AAAA,UAAC;AAAA,QACX;AACA;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
package/dist/sdk/sdk.js CHANGED
@@ -3490,6 +3490,33 @@ var init_env_resolver = __esm({
3490
3490
  }
3491
3491
  return missing;
3492
3492
  }
3493
+ /**
3494
+ * Resolves environment variables in HTTP headers
3495
+ * Each header value is processed through resolveValue to replace env var references
3496
+ */
3497
+ static resolveHeaders(headers) {
3498
+ const resolved = {};
3499
+ for (const [key, value] of Object.entries(headers)) {
3500
+ resolved[key] = String(this.resolveValue(value));
3501
+ }
3502
+ return resolved;
3503
+ }
3504
+ /**
3505
+ * Sanitizes headers for logging/telemetry by redacting sensitive values
3506
+ * Headers like Authorization, API keys, and cookies are replaced with [REDACTED]
3507
+ */
3508
+ static sanitizeHeaders(headers) {
3509
+ const sensitiveHeaders = ["authorization", "x-api-key", "cookie", "set-cookie"];
3510
+ const sanitized = {};
3511
+ for (const [key, value] of Object.entries(headers)) {
3512
+ if (sensitiveHeaders.includes(key.toLowerCase())) {
3513
+ sanitized[key] = "[REDACTED]";
3514
+ } else {
3515
+ sanitized[key] = value;
3516
+ }
3517
+ }
3518
+ return sanitized;
3519
+ }
3493
3520
  };
3494
3521
  }
3495
3522
  });
@@ -4264,9 +4291,24 @@ function getOtelApi() {
4264
4291
  function createNoOpTracer() {
4265
4292
  return {
4266
4293
  startSpan: () => createNoOpSpan(),
4267
- startActiveSpan: (name, fn) => {
4268
- if (typeof fn === "function") return fn(createNoOpSpan());
4269
- return createNoOpSpan();
4294
+ // Support both OTel v1 and v2 overloads:
4295
+ // - startActiveSpan(name, callback)
4296
+ // - startActiveSpan(name, options, callback)
4297
+ // - startActiveSpan(name, options, context, callback)
4298
+ startActiveSpan: (name, arg2, arg3, arg4) => {
4299
+ const span = createNoOpSpan();
4300
+ let cb = void 0;
4301
+ if (typeof arg2 === "function") cb = arg2;
4302
+ else if (typeof arg3 === "function") cb = arg3;
4303
+ else if (typeof arg4 === "function") cb = arg4;
4304
+ if (typeof cb === "function") {
4305
+ try {
4306
+ return cb(span);
4307
+ } catch {
4308
+ return void 0;
4309
+ }
4310
+ }
4311
+ return span;
4270
4312
  }
4271
4313
  };
4272
4314
  }
@@ -5098,6 +5140,7 @@ var init_http_check_provider = __esm({
5098
5140
  init_liquid_extensions();
5099
5141
  init_lazy_otel();
5100
5142
  init_state_capture();
5143
+ init_env_resolver();
5101
5144
  HttpCheckProvider = class extends CheckProvider {
5102
5145
  liquid;
5103
5146
  constructor() {
@@ -5183,7 +5226,14 @@ var init_http_check_provider = __esm({
5183
5226
  );
5184
5227
  }
5185
5228
  try {
5186
- const response = await this.sendWebhookRequest(url, method, headers, payload, timeout);
5229
+ const resolvedHeaders = EnvironmentResolver.resolveHeaders(headers);
5230
+ const response = await this.sendWebhookRequest(
5231
+ url,
5232
+ method,
5233
+ resolvedHeaders,
5234
+ payload,
5235
+ timeout
5236
+ );
5187
5237
  const result = this.parseWebhookResponse(response, url);
5188
5238
  const suppressionEnabled = config.suppressionEnabled !== false;
5189
5239
  const issueFilter = new IssueFilter(suppressionEnabled);
@@ -5195,12 +5245,14 @@ var init_http_check_provider = __esm({
5195
5245
  try {
5196
5246
  const span = trace.getSpan(context.active());
5197
5247
  if (span) {
5248
+ const sanitizedHeaders = EnvironmentResolver.sanitizeHeaders(resolvedHeaders);
5198
5249
  captureProviderCall(
5199
5250
  span,
5200
5251
  "http",
5201
5252
  {
5202
5253
  url,
5203
5254
  method,
5255
+ headers: sanitizedHeaders,
5204
5256
  body: JSON.stringify(payload).substring(0, 500)
5205
5257
  },
5206
5258
  {
@@ -5451,6 +5503,7 @@ var init_http_client_provider = __esm({
5451
5503
  "use strict";
5452
5504
  init_check_provider_interface();
5453
5505
  init_liquid_extensions();
5506
+ init_env_resolver();
5454
5507
  HttpClientProvider = class extends CheckProvider {
5455
5508
  liquid;
5456
5509
  constructor() {
@@ -5512,7 +5565,8 @@ var init_http_client_provider = __esm({
5512
5565
  const renderedBody = await this.liquid.parseAndRender(bodyTemplate, templateContext);
5513
5566
  requestBody = renderedBody;
5514
5567
  }
5515
- const data = await this.fetchData(renderedUrl, method, headers, requestBody, timeout);
5568
+ const resolvedHeaders = EnvironmentResolver.resolveHeaders(headers);
5569
+ const data = await this.fetchData(renderedUrl, method, resolvedHeaders, requestBody, timeout);
5516
5570
  let processedData = data;
5517
5571
  if (transform) {
5518
5572
  try {
@@ -8434,6 +8488,7 @@ var init_mcp_check_provider = __esm({
8434
8488
  import_sse = require("@modelcontextprotocol/sdk/client/sse.js");
8435
8489
  import_streamableHttp = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
8436
8490
  init_sandbox();
8491
+ init_env_resolver();
8437
8492
  McpCheckProvider = class extends CheckProvider {
8438
8493
  liquid;
8439
8494
  sandbox;
@@ -8734,7 +8789,7 @@ var init_mcp_check_provider = __esm({
8734
8789
  async executeSseMethod(config, methodArgs, timeout) {
8735
8790
  const requestInit = {};
8736
8791
  if (config.headers) {
8737
- requestInit.headers = config.headers;
8792
+ requestInit.headers = EnvironmentResolver.resolveHeaders(config.headers);
8738
8793
  }
8739
8794
  const transport = new import_sse.SSEClientTransport(new URL(config.url), {
8740
8795
  requestInit
@@ -8747,7 +8802,7 @@ var init_mcp_check_provider = __esm({
8747
8802
  async executeHttpMethod(config, methodArgs, timeout) {
8748
8803
  const requestInit = {};
8749
8804
  if (config.headers) {
8750
- requestInit.headers = config.headers;
8805
+ requestInit.headers = EnvironmentResolver.resolveHeaders(config.headers);
8751
8806
  }
8752
8807
  const transport = new import_streamableHttp.StreamableHTTPClientTransport(new URL(config.url), {
8753
8808
  requestInit,