@thesight/sdk 0.3.5 → 0.3.7

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/dist/index.cjs CHANGED
@@ -77,12 +77,12 @@ var SightSpanExporter = class {
77
77
  fetchImpl;
78
78
  maxBatchSize;
79
79
  shuttingDown = false;
80
- constructor(config) {
81
- const parsed = parseDsn(config.dsn);
80
+ constructor(config2) {
81
+ const parsed = parseDsn(config2.dsn);
82
82
  this.apiKey = parsed.apiKey;
83
83
  this.ingestUrl = parsed.ingestUrl;
84
- this.fetchImpl = config.fetchImpl ?? globalThis.fetch;
85
- this.maxBatchSize = Math.min(100, config.maxBatchSize ?? 100);
84
+ this.fetchImpl = config2.fetchImpl ?? globalThis.fetch;
85
+ this.maxBatchSize = Math.min(100, config2.maxBatchSize ?? 100);
86
86
  if (!this.fetchImpl) {
87
87
  throw new Error(
88
88
  "SightSpanExporter: globalThis.fetch is not available. Node >= 18 ships with fetch built in, or pass `fetchImpl` explicitly."
@@ -137,8 +137,8 @@ var SightSpanExporter = class {
137
137
  }
138
138
  convertSpan(span) {
139
139
  const attr = span.attributes;
140
- const resource = span.resource.attributes;
141
- const serviceName = typeof resource["service.name"] === "string" ? resource["service.name"] : void 0;
140
+ const resource2 = span.resource.attributes;
141
+ const serviceName = typeof resource2["service.name"] === "string" ? resource2["service.name"] : void 0;
142
142
  const startTimeMs = (0, import_core.hrTimeToMilliseconds)(span.startTime);
143
143
  const endTimeMs = (0, import_core.hrTimeToMilliseconds)(span.endTime);
144
144
  const durationMs = Math.max(0, endTimeMs - startTimeMs);
@@ -205,15 +205,18 @@ function initSight(config) {
205
205
  if (!config.serviceName) {
206
206
  throw new Error("initSight: `serviceName` is required");
207
207
  }
208
+ let _lastTier1Error = null;
209
+ let _lastTier2Error = null;
208
210
  const exporter = new SightSpanExporter({
209
211
  dsn: config.dsn,
210
212
  fetchImpl: config.fetchImpl,
211
213
  maxBatchSize: config.maxBatchSize
212
214
  });
213
215
  try {
214
- const { NodeTracerProvider } = require("@opentelemetry/sdk-trace-node");
215
- const { BatchSpanProcessor } = require("@opentelemetry/sdk-trace-base");
216
- const otelResources = require("@opentelemetry/resources");
216
+ const _require = eval("require");
217
+ const { NodeTracerProvider } = _require("@opentelemetry/sdk-trace-node");
218
+ const { BatchSpanProcessor } = _require("@opentelemetry/sdk-trace-base");
219
+ const otelResources = _require("@opentelemetry/resources");
217
220
  const processor = new BatchSpanProcessor(exporter, {
218
221
  scheduledDelayMillis: config.batchDelayMs ?? 5e3,
219
222
  maxExportBatchSize: config.maxBatchSize ?? 100,
@@ -242,32 +245,40 @@ function initSight(config) {
242
245
  await provider.shutdown();
243
246
  }
244
247
  };
245
- } catch {
248
+ } catch (tier1Err) {
249
+ _lastTier1Error = tier1Err;
246
250
  }
247
251
  try {
248
252
  const proxy = import_api.trace.getTracerProvider();
249
253
  const delegate = proxy?.getDelegate?.() ?? proxy;
250
254
  if (delegate && typeof delegate.addSpanProcessor === "function") {
251
- const processor = new SightInlineProcessor(
255
+ const processor2 = new SightInlineProcessor(
252
256
  exporter,
253
257
  config.batchDelayMs ?? 5e3,
254
258
  config.maxBatchSize ?? 100
255
259
  );
256
- delegate.addSpanProcessor(processor);
260
+ delegate.addSpanProcessor(processor2);
257
261
  return {
258
262
  tracer: import_api.trace.getTracer("@thesight/sdk"),
259
263
  mode: "piggyback",
260
264
  shutdown: async () => {
261
- await processor.shutdown();
265
+ await processor2.shutdown();
262
266
  }
263
267
  };
264
268
  }
265
- } catch {
269
+ } catch (tier2Err) {
270
+ _lastTier2Error = tier2Err;
266
271
  }
267
272
  if (typeof console !== "undefined") {
268
273
  console.warn(
269
- "[sight] Could not create an OTel tracer provider or attach to the existing one (likely an @opentelemetry version conflict with @sentry/node). Sight spans may not reach ingest. Consider aligning OTel versions via pnpm.overrides or npm overrides."
274
+ "[sight] Could not create an OTel tracer provider (tier 1) or attach to the existing one (tier 2). Sight spans may not reach ingest."
270
275
  );
276
+ if (_lastTier1Error) {
277
+ console.warn("[sight] Tier 1 error:", _lastTier1Error instanceof Error ? _lastTier1Error.message : String(_lastTier1Error));
278
+ }
279
+ if (_lastTier2Error) {
280
+ console.warn("[sight] Tier 2 error:", _lastTier2Error instanceof Error ? _lastTier2Error.message : String(_lastTier2Error));
281
+ }
271
282
  }
272
283
  return {
273
284
  tracer: import_api.trace.getTracer("@thesight/sdk"),
@@ -277,8 +288,8 @@ function initSight(config) {
277
288
  };
278
289
  }
279
290
  var SightInlineProcessor = class {
280
- constructor(exporter, delayMs, maxBatch) {
281
- this.exporter = exporter;
291
+ constructor(exporter2, delayMs, maxBatch) {
292
+ this.exporter = exporter2;
282
293
  this.maxBatch = maxBatch;
283
294
  this.timer = setInterval(() => this.flush(), delayMs);
284
295
  if (this.timer && typeof this.timer.unref === "function") {
@@ -416,7 +427,7 @@ var InstrumentedConnection = class extends import_web3.Connection {
416
427
  sightConfig;
417
428
  idlResolver;
418
429
  tracer;
419
- constructor(endpoint, config) {
430
+ constructor(endpoint, config2) {
420
431
  const {
421
432
  tracer,
422
433
  idlRpcEndpoint,
@@ -428,7 +439,7 @@ var InstrumentedConnection = class extends import_web3.Connection {
428
439
  disableAutoSpan,
429
440
  commitment,
430
441
  ...connectionConfig
431
- } = config ?? {};
442
+ } = config2 ?? {};
432
443
  super(endpoint, connectionConfig);
433
444
  this.sightConfig = {
434
445
  tracer,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/init.ts","../src/exporter.ts","../src/dsn.ts","../src/track.ts"],"sourcesContent":["import {\n Connection,\n type ConnectionConfig,\n type SendOptions,\n type Commitment,\n type Finality,\n type TransactionSignature,\n type VersionedTransactionResponse,\n} from '@solana/web3.js';\nimport {\n trace,\n context,\n SpanStatusCode,\n type Span,\n type Tracer,\n} from '@opentelemetry/api';\nimport { parseLogs, IdlResolver, enrichTree, flatAttributions } from '@thesight/core';\nimport type { AnchorIdl, CpiTree } from '@thesight/core';\n\n// ─── Config ────────────────────────────────────────────────────────────────\n\nexport interface SightConfig {\n /** OTel tracer. Defaults to `trace.getTracer('@thesight/sdk')`. */\n tracer?: Tracer;\n\n /** Override RPC endpoint for IDL fetching (defaults to same as connection) */\n idlRpcEndpoint?: string;\n\n /**\n * Commitment used when fetching confirmed tx details for enrichment.\n * Defaults to 'confirmed'.\n */\n commitment?: Commitment;\n\n /**\n * Skip IDL resolution entirely. Spans will still emit with signature and\n * timing attributes, but program names, instruction names, CPI trees, and\n * decoded errors will all be omitted. Useful when you want minimal\n * overhead and don't care about the richer observability.\n */\n skipIdlResolution?: boolean;\n\n /**\n * Pre-register IDLs at construction time. Keys are program IDs, values are\n * full Anchor IDL objects. Anchor users can pass `program.idl` directly off\n * their `Program` instance — zero setup friction.\n */\n idls?: Record<string, AnchorIdl>;\n\n /**\n * Opt into reading Anchor IDL accounts from the on-chain PDA as a fallback\n * when a program is not explicitly registered. Defaults to **false** —\n * Sight's model is that IDLs are explicitly provided by the application,\n * not silently guessed from chain.\n */\n allowOnChainIdlFetch?: boolean;\n\n /**\n * Max wall-time the background enrichment task will wait for a\n * transaction to show up in `getTransaction`. After this expires the\n * span is ended with `solana.tx.status = 'timeout'`. Default 30000 (30s).\n */\n enrichmentTimeoutMs?: number;\n\n /**\n * How often the enrichment task polls `getTransaction`. Each retry backs\n * off by 1.5x up to a cap. Default 500ms base.\n */\n enrichmentPollIntervalMs?: number;\n\n /**\n * Disable automatic span creation in the overridden `sendRawTransaction`.\n * When true, InstrumentedConnection behaves like a plain Connection. You\n * probably don't want this — it's here as an escape hatch for tests and\n * rare cases where you need the class hierarchy but not the tracing.\n */\n disableAutoSpan?: boolean;\n}\n\n// ─── Span attribute shape (documented, for downstream consumers) ──────────\n\nexport interface SightSpanAttributes {\n 'solana.tx.signature': string;\n 'solana.tx.status': 'submitted' | 'confirmed' | 'failed' | 'timeout';\n 'solana.tx.slot'?: number;\n 'solana.tx.fee_lamports'?: number;\n 'solana.tx.submit_ms': number;\n 'solana.tx.enrichment_ms'?: number;\n 'solana.tx.cu_used'?: number;\n 'solana.tx.cu_budget'?: number;\n 'solana.tx.cu_utilization'?: number;\n 'solana.tx.program'?: string;\n 'solana.tx.instruction'?: string;\n 'solana.tx.error'?: string;\n 'solana.tx.error_code'?: number;\n 'solana.tx.error_program'?: string;\n 'solana.tx.error_msg'?: string;\n}\n\n// ─── InstrumentedConnection ────────────────────────────────────────────────\n\n/**\n * Drop-in replacement for `@solana/web3.js`'s `Connection` that emits an\n * OpenTelemetry span for **every** call to `sendRawTransaction` —\n * regardless of whether the caller is your own code, an Anchor provider's\n * `sendAndConfirm`, `@solana/web3.js`'s top-level `sendAndConfirmTransaction`,\n * a wallet adapter, or anything else.\n *\n * Internally it overrides the parent class's `sendRawTransaction`, so the\n * span covers the same surface web3.js already mediates. No mutation of\n * the transaction bytes — we call `super.sendRawTransaction(rawTx, opts)`\n * verbatim and observe the signature it returns.\n *\n * After the signature is returned (synchronously from the caller's point\n * of view), a background task polls `getTransaction` until the on-chain\n * record is available, parses the program logs via `@thesight/core`, and\n * enriches the span with CU attribution, per-CPI events, program + instruction\n * names, and decoded error details. The background task never blocks the\n * caller — if it fails or times out, the span ends with status=timeout and\n * whatever partial data was collected.\n *\n * Usage:\n *\n * import { initSight, InstrumentedConnection } from '@thesight/sdk';\n *\n * initSight({ dsn: process.env.SIGHT_DSN!, serviceName: 'swap-bot' });\n *\n * // Anywhere you currently construct a Connection, construct this instead:\n * const connection = new InstrumentedConnection(rpcUrl, {\n * commitment: 'confirmed',\n * });\n *\n * // All of the following now emit spans automatically:\n * await connection.sendRawTransaction(tx.serialize());\n * await sendAndConfirmTransaction(connection, tx, signers); // web3.js\n * await program.methods.foo().rpc(); // Anchor\n * await wallet.sendTransaction(tx, connection); // wallet adapter\n */\nexport class InstrumentedConnection extends Connection {\n private sightConfig: SightConfig;\n private idlResolver: IdlResolver;\n private tracer: Tracer;\n\n constructor(endpoint: string, config?: ConnectionConfig & SightConfig) {\n const {\n tracer,\n idlRpcEndpoint,\n skipIdlResolution,\n idls,\n allowOnChainIdlFetch,\n enrichmentTimeoutMs,\n enrichmentPollIntervalMs,\n disableAutoSpan,\n commitment,\n ...connectionConfig\n } = config ?? {};\n\n super(endpoint, connectionConfig);\n\n this.sightConfig = {\n tracer,\n idlRpcEndpoint,\n skipIdlResolution,\n allowOnChainIdlFetch,\n enrichmentTimeoutMs: enrichmentTimeoutMs ?? 30_000,\n enrichmentPollIntervalMs: enrichmentPollIntervalMs ?? 500,\n disableAutoSpan,\n commitment,\n };\n this.idlResolver = new IdlResolver({\n rpcEndpoint: idlRpcEndpoint ?? endpoint,\n allowOnChainFetch: allowOnChainIdlFetch ?? false,\n });\n if (idls) this.idlResolver.registerMany(idls);\n this.tracer = tracer ?? trace.getTracer('@thesight/sdk');\n }\n\n /**\n * Register an IDL for a single program. Convenience forwarder for the\n * underlying resolver. Anchor users typically call this right after\n * constructing a `Program`:\n *\n * const program = new Program(idl, programId, provider);\n * connection.registerIdl(program.programId.toBase58(), program.idl);\n */\n registerIdl(programId: string, idl: AnchorIdl): void {\n this.idlResolver.register(programId, idl);\n }\n\n /** Bulk-register multiple IDLs. */\n registerIdls(idls: Record<string, AnchorIdl>): void {\n this.idlResolver.registerMany(idls);\n }\n\n // ─── Overridden method ────────────────────────────────────────────────────\n\n /**\n * Overrides the parent `Connection.sendRawTransaction` so every submit —\n * including those made by Anchor's `provider.sendAndConfirm`, web3.js's\n * top-level `sendAndConfirmTransaction`, and wallet adapter flows — is\n * observed by an OTel span automatically.\n *\n * The span is a child of whatever OTel context is active when the call\n * fires, so app-level parent spans (HTTP handlers, job runs, wallet flow\n * spans from another SDK) nest the Sight span correctly.\n */\n override async sendRawTransaction(\n rawTransaction: Buffer | Uint8Array | Array<number>,\n options?: SendOptions,\n ): Promise<TransactionSignature> {\n if (this.sightConfig.disableAutoSpan) {\n return super.sendRawTransaction(rawTransaction, options);\n }\n\n const span = this.tracer.startSpan('solana.sendRawTransaction', {}, context.active());\n const submitStart = Date.now();\n\n let signature: TransactionSignature;\n try {\n signature = await super.sendRawTransaction(rawTransaction, options);\n } catch (err) {\n const submitMs = Date.now() - submitStart;\n span.setAttribute('solana.tx.submit_ms', submitMs);\n span.setAttribute('solana.tx.status', 'failed');\n span.recordException(err instanceof Error ? err : new Error(String(err)));\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: err instanceof Error ? err.message : String(err),\n });\n span.end();\n throw err;\n }\n\n span.setAttribute('solana.tx.signature', signature);\n span.setAttribute('solana.tx.submit_ms', Date.now() - submitStart);\n span.setAttribute('solana.tx.status', 'submitted');\n\n // Fire-and-forget background enrichment. The returned Promise is\n // intentionally discarded — we don't want to couple the caller's\n // transaction-send latency to our observation machinery. Any enrichment\n // error is caught inside enrichSpanInBackground and attached to the\n // span, not bubbled up.\n void this.enrichSpanInBackground(span, signature, submitStart);\n\n return signature;\n }\n\n // ─── Background enrichment ───────────────────────────────────────────────\n\n /**\n * Poll `getTransaction` until the on-chain record is available, then\n * enrich the span with CU attribution, program names, decoded errors,\n * and per-CPI events.\n *\n * This runs async and is never awaited by callers of `sendRawTransaction`.\n * Failures inside this method attach to the span via recordException\n * rather than throwing.\n */\n private async enrichSpanInBackground(\n span: Span,\n signature: TransactionSignature,\n submitStart: number,\n ): Promise<void> {\n const enrichStart = Date.now();\n const deadline = enrichStart + (this.sightConfig.enrichmentTimeoutMs ?? 30_000);\n const commitment = (this.sightConfig.commitment ?? 'confirmed') as Finality;\n const basePollMs = this.sightConfig.enrichmentPollIntervalMs ?? 500;\n\n try {\n const txDetails = await this.pollForTransaction(signature, commitment, deadline, basePollMs);\n\n if (!txDetails) {\n span.setAttribute('solana.tx.status', 'timeout');\n span.setAttribute('solana.tx.enrichment_ms', Date.now() - submitStart);\n span.end();\n return;\n }\n\n this.attachTxDetailsToSpan(span, txDetails);\n\n if (!this.sightConfig.skipIdlResolution) {\n const logs = txDetails.meta?.logMessages ?? [];\n if (logs.length > 0) {\n await this.attachParsedLogsToSpan(span, logs);\n }\n }\n\n span.setAttribute('solana.tx.enrichment_ms', Date.now() - submitStart);\n\n if (txDetails.meta?.err) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n } catch (err) {\n span.recordException(err instanceof Error ? err : new Error(String(err)));\n span.setAttribute(\n 'solana.tx.enrichment_error',\n err instanceof Error ? err.message : String(err),\n );\n } finally {\n span.end();\n }\n }\n\n /**\n * Poll `getTransaction(signature)` until either the on-chain record is\n * returned or the deadline passes. Exponential backoff (1.5x) capped at\n * 2 seconds to balance responsiveness against RPC load.\n */\n private async pollForTransaction(\n signature: TransactionSignature,\n commitment: Finality,\n deadline: number,\n basePollMs: number,\n ): Promise<VersionedTransactionResponse | null> {\n let attempt = 0;\n while (Date.now() < deadline) {\n try {\n const tx = await super.getTransaction(signature, {\n commitment,\n maxSupportedTransactionVersion: 0,\n });\n if (tx) return tx;\n } catch {\n // Ignore — retry with backoff. Transient RPC errors are common\n // during the seconds immediately after submission.\n }\n attempt++;\n const waitMs = Math.min(basePollMs * Math.pow(1.5, attempt - 1), 2_000);\n await sleep(waitMs);\n }\n return null;\n }\n\n /** Attach the flat fields (slot, fee, CU, status) from a tx response to a span. */\n private attachTxDetailsToSpan(span: Span, txDetails: VersionedTransactionResponse): void {\n span.setAttribute('solana.tx.status', txDetails.meta?.err ? 'failed' : 'confirmed');\n span.setAttribute('solana.tx.slot', txDetails.slot);\n\n const fee = txDetails.meta?.fee;\n if (fee !== undefined) span.setAttribute('solana.tx.fee_lamports', fee);\n\n const cuUsed = txDetails.meta?.computeUnitsConsumed;\n if (cuUsed !== undefined && cuUsed !== null) {\n span.setAttribute('solana.tx.cu_used', Number(cuUsed));\n span.setAttribute('solana.tx.cu_budget', 200_000);\n span.setAttribute(\n 'solana.tx.cu_utilization',\n parseFloat((Number(cuUsed) / 200_000 * 100).toFixed(1)),\n );\n }\n }\n\n /**\n * Parse program logs into a CPI tree, enrich with registered IDLs, and\n * emit one `cpi.invoke` event per invocation onto the span. Root\n * program/instruction names are copied onto span attributes so dashboards\n * can filter by them without walking events.\n */\n private async attachParsedLogsToSpan(span: Span, logs: string[]): Promise<CpiTree | undefined> {\n const { cpiTree } = parseLogs({ logs });\n await enrichTree(cpiTree, this.idlResolver);\n\n const attributions = flatAttributions(cpiTree);\n for (const attr of attributions) {\n span.addEvent('cpi.invoke', {\n 'cpi.program': attr.programName ?? attr.programId,\n 'cpi.instruction': attr.instructionName ?? 'unknown',\n 'cpi.depth': attr.depth,\n 'cpi.cu_consumed': attr.cuConsumed,\n 'cpi.cu_self': attr.cuSelf,\n 'cpi.percentage': parseFloat(attr.percentage.toFixed(2)),\n });\n }\n\n const root = cpiTree.roots[0];\n if (root) {\n if (root.programName) span.setAttribute('solana.tx.program', root.programName);\n if (root.instructionName) span.setAttribute('solana.tx.instruction', root.instructionName);\n }\n\n return cpiTree;\n }\n}\n\n// ─── Utilities ────────────────────────────────────────────────────────────\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n// ─── Re-exports ────────────────────────────────────────────────────────────\n\nexport { IdlResolver } from '@thesight/core';\nexport type {\n CpiTree,\n CuAttribution,\n FlamegraphItem,\n DecodedError,\n AnchorIdl,\n} from '@thesight/core';\n\n// ─── Tracing initialization ──────────────────────────────────────────────────\n\nexport { initSight } from './init.js';\nexport type { InitSightConfig, SightTracerHandle } from './init.js';\nexport { SightSpanExporter } from './exporter.js';\nexport type { SightExporterConfig } from './exporter.js';\n\n// ─── DSN helpers ─────────────────────────────────────────────────────────\n\nexport { parseDsn, buildDsn } from './dsn.js';\nexport type { ParsedDsn } from './dsn.js';\n\n// ─── Non-wrapping observation helper ─────────────────────────────────────\n\nexport { trackSolanaTransaction } from './track.js';\nexport type { TrackTransactionOptions } from './track.js';\n","import { trace, type Tracer } from '@opentelemetry/api';\nimport { SightSpanExporter } from './exporter.js';\n\n// ─── Dynamic imports ────────────────────────────────────────────────────────\n// The OTel SDK packages (@opentelemetry/sdk-trace-node, sdk-trace-base,\n// resources, semantic-conventions) are the source of version-conflict\n// errors when Sentry or another OTel consumer is in the same process.\n// By importing them dynamically inside initSight, the conflict is:\n// (a) confined to the try/catch that handles it\n// (b) deferred until the user actually calls initSight, not at\n// module-load time (which would crash the entire require chain)\n\n// ─── Config ──────────────────────────────────────────────────────────────────\n\nexport interface InitSightConfig {\n /**\n * Sight DSN — one string encoding both the ingest endpoint and the API key.\n *\n * Format: `https://<apiKey>@<host>/ingest`\n *\n * Get yours from the Sight dashboard when you create a project. For local\n * dev against a Docker compose stack: `http://sk_live_xxx@localhost:3001/ingest`\n */\n dsn: string;\n\n /**\n * Human-readable service name that shows up on every span. Pick something\n * that identifies this process — `'swap-bot'`, `'market-maker'`,\n * `'trade-settler'`, etc.\n */\n serviceName: string;\n\n /** Optional version string for the service. Useful for correlating spans with a deploy. */\n serviceVersion?: string;\n\n /** BatchSpanProcessor flush interval, in ms. Defaults to 5s. */\n batchDelayMs?: number;\n\n /** Max spans per HTTP POST. Clamped to 100 by the exporter. */\n maxBatchSize?: number;\n\n /** Inject a fetch implementation for tests. Defaults to global fetch. */\n fetchImpl?: typeof fetch;\n}\n\nexport interface SightTracerHandle {\n /**\n * The OTel Tracer that routes spans through the Sight exporter. Pass\n * this to `InstrumentedConnection({ tracer: sight.tracer })`.\n *\n * Always works — even when another OTel provider (Sentry, Datadog)\n * is in the process. This is the recommended way to wire the connection.\n */\n tracer: Tracer;\n\n /**\n * How the tracer was obtained:\n * - `'own_provider'` — Sight created its own NodeTracerProvider and\n * (optionally) registered it globally. Full control.\n * - `'piggyback'` — Another provider (e.g. Sentry) was already\n * registered. Sight added its exporter as an additional span\n * processor on that provider. Both consumers see all spans.\n * - `'api_only'` — Neither own-provider nor piggyback worked.\n * Sight is using the global `trace.getTracer()` which may or may\n * not route to Sight's exporter depending on what else is registered.\n * This is the least reliable mode.\n */\n mode: 'own_provider' | 'piggyback' | 'api_only';\n\n /** Flush any pending spans and release the batch processor. */\n shutdown: () => Promise<void>;\n}\n\n// ─── Entry point ────────────────────────────────────────────────────────────\n\n/**\n * Initialize Sight tracing. Call this **once** near the top of your\n * process entry point.\n *\n * import { initSight, InstrumentedConnection } from '@thesight/sdk';\n *\n * const sight = initSight({\n * dsn: process.env.SIGHT_DSN!,\n * serviceName: 'swap-bot',\n * });\n *\n * const connection = new InstrumentedConnection(process.env.RPC_URL!, {\n * tracer: sight.tracer,\n * });\n *\n * Handles coexistence with other OTel providers (e.g. @sentry/node)\n * gracefully via a three-tier fallback:\n *\n * 1. **Own provider**: create a NodeTracerProvider, add the Sight\n * exporter, register globally. This is the ideal path.\n * 2. **Piggyback**: if step 1 fails (OTel version conflict with Sentry),\n * find the existing global provider and add the Sight exporter to it\n * as an additional span processor. Both Sentry and Sight see spans.\n * 3. **API-only**: if step 2 also fails, fall back to `trace.getTracer()`\n * from the OTel API layer. Spans may or may not reach Sight depending\n * on what's registered. A warning is logged.\n */\nexport function initSight(config: InitSightConfig): SightTracerHandle {\n if (!config.dsn) {\n throw new Error(\n 'initSight: `dsn` is required. ' +\n 'Get your DSN from the Sight dashboard: https://thesight.dev',\n );\n }\n if (!config.serviceName) {\n throw new Error('initSight: `serviceName` is required');\n }\n\n const exporter = new SightSpanExporter({\n dsn: config.dsn,\n fetchImpl: config.fetchImpl,\n maxBatchSize: config.maxBatchSize,\n });\n\n // ── Tier 1: own provider ──────────────────────────────────────────────\n\n try {\n const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node');\n const { BatchSpanProcessor } = require('@opentelemetry/sdk-trace-base');\n const otelResources = require('@opentelemetry/resources');\n\n const processor = new BatchSpanProcessor(exporter, {\n scheduledDelayMillis: config.batchDelayMs ?? 5_000,\n maxExportBatchSize: config.maxBatchSize ?? 100,\n maxQueueSize: 2048,\n });\n\n // OTel 2.x removed the Resource class in favor of resourceFromAttributes().\n // OTel 1.x has Resource but not resourceFromAttributes.\n // Handle both so the SDK works regardless of which version the host\n // project resolves.\n const resourceAttrs: Record<string, string> = {\n 'service.name': config.serviceName,\n ...(config.serviceVersion ? { 'service.version': config.serviceVersion } : {}),\n };\n const resource = typeof otelResources.resourceFromAttributes === 'function'\n ? otelResources.resourceFromAttributes(resourceAttrs)\n : new otelResources.Resource(resourceAttrs);\n\n // OTel 2.x removed provider.addSpanProcessor() in favor of passing\n // spanProcessors in the constructor. OTel 1.x has addSpanProcessor\n // but not the constructor option. Handle both.\n let provider: any;\n if (typeof NodeTracerProvider.prototype.addSpanProcessor === 'function') {\n // OTel 1.x path\n provider = new NodeTracerProvider({ resource });\n provider.addSpanProcessor(processor);\n } else {\n // OTel 2.x path\n provider = new NodeTracerProvider({ resource, spanProcessors: [processor] });\n }\n\n try {\n provider.register();\n } catch {\n // Registration failed (another provider owns the global), but\n // our standalone provider still works.\n }\n\n return {\n tracer: provider.getTracer('@thesight/sdk'),\n mode: 'own_provider' as const,\n shutdown: async () => { await provider.shutdown(); },\n };\n } catch {\n // Tier 1 failed.\n }\n\n // ── Tier 2: piggyback on the existing global provider ─────────────────\n //\n // If another provider (Sentry) already registered, try adding our\n // exporter as an additional span processor. This makes both Sentry\n // and Sight see the same spans — the ideal coexistence model.\n //\n // Critically, this tier does NOT import from @opentelemetry/sdk-trace-base\n // or sdk-trace-node — those are the version-conflicting modules. Instead\n // it uses a minimal inline span processor that implements the interface\n // via duck-typing. The OTel provider calls processor.onEnd(span) for\n // every finished span; our processor buffers them and calls the Sight\n // exporter. No version-sensitive imports needed.\n\n try {\n const proxy = trace.getTracerProvider() as any;\n const delegate = proxy?.getDelegate?.() ?? proxy;\n\n if (delegate && typeof delegate.addSpanProcessor === 'function') {\n const processor = new SightInlineProcessor(\n exporter,\n config.batchDelayMs ?? 5_000,\n config.maxBatchSize ?? 100,\n );\n delegate.addSpanProcessor(processor);\n return {\n tracer: trace.getTracer('@thesight/sdk'),\n mode: 'piggyback',\n shutdown: async () => { await processor.shutdown(); },\n };\n }\n } catch {\n // Tier 2 also failed.\n }\n\n // ── Tier 3: API-only fallback ─────────────────────────────────────────\n //\n // Last resort. We return a tracer from the OTel API layer. If Sentry\n // registered a provider, this tracer creates spans on Sentry's\n // provider — those spans go to Sentry but NOT to Sight (we couldn't\n // attach our processor). Log a warning so the user knows instrumentation\n // is degraded.\n\n if (typeof console !== 'undefined') {\n console.warn(\n '[sight] Could not create an OTel tracer provider or attach to the ' +\n 'existing one (likely an @opentelemetry version conflict with ' +\n '@sentry/node). Sight spans may not reach ingest. Consider ' +\n 'aligning OTel versions via pnpm.overrides or npm overrides.',\n );\n }\n\n return {\n tracer: trace.getTracer('@thesight/sdk'),\n mode: 'api_only',\n shutdown: async () => {},\n };\n}\n\n// ─── Inline span processor ─────────────────────────────────────────────────\n//\n// A minimal batch span processor that implements the OTel SpanProcessor\n// interface structurally (duck-typed). It does NOT import from\n// @opentelemetry/sdk-trace-base, which is the module that version-\n// conflicts with Sentry. Instead it implements the four methods the\n// provider calls (onStart, onEnd, shutdown, forceFlush) and drives\n// our SightSpanExporter directly.\n//\n// This class only exists for the tier-2 piggyback path. Tier 1 uses\n// the real BatchSpanProcessor from the OTel SDK (which has nicer\n// retry/backpressure logic); tier 2 uses this inline version because\n// it's the only way to avoid the version conflict.\n\nclass SightInlineProcessor {\n private buffer: unknown[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private readonly maxBatch: number;\n\n constructor(\n private readonly exporter: SightSpanExporter,\n delayMs: number,\n maxBatch: number,\n ) {\n this.maxBatch = maxBatch;\n this.timer = setInterval(() => this.flush(), delayMs);\n // Prevent the timer from keeping the process alive if everything\n // else has exited. Node-specific but safe — `unref` is a no-op in\n // environments that don't have it.\n if (this.timer && typeof (this.timer as any).unref === 'function') {\n (this.timer as any).unref();\n }\n }\n\n /** Called by the provider when a span starts. We don't need it. */\n onStart(): void {}\n\n /** Called by the provider when a span ends. Buffer it for export. */\n onEnd(span: unknown): void {\n this.buffer.push(span);\n if (this.buffer.length >= this.maxBatch) {\n this.flush();\n }\n }\n\n /** Flush pending spans to the exporter. */\n private flush(): void {\n if (this.buffer.length === 0) return;\n const batch = this.buffer.splice(0, this.maxBatch);\n // exporter.export expects ReadableSpan[] — the spans from the\n // provider's onEnd ARE ReadableSpan instances regardless of which\n // sdk-trace-base version created them, because the interface is\n // structurally stable across 1.x versions.\n this.exporter.export(batch as any, () => {});\n }\n\n async shutdown(): Promise<void> {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n this.flush();\n await this.exporter.shutdown();\n }\n\n async forceFlush(): Promise<void> {\n this.flush();\n }\n}\n","import type { SpanAttributes } from '@opentelemetry/api';\nimport type { SpanExporter, ReadableSpan } from '@opentelemetry/sdk-trace-base';\nimport {\n hrTimeToMilliseconds,\n ExportResultCode,\n type ExportResult,\n} from '@opentelemetry/core';\nimport { parseDsn } from './dsn.js';\n\n// ─── Config ──────────────────────────────────────────────────────────────────\n\nexport interface SightExporterConfig {\n /**\n * Sight DSN — one string encoding both the ingest endpoint and the API key.\n * Format: https://<apiKey>@<host>/ingest\n */\n dsn: string;\n /** Inject a fake fetch in tests. Defaults to `globalThis.fetch`. */\n fetchImpl?: typeof fetch;\n /** Max spans per POST. Ingest enforces an upper bound of 100. */\n maxBatchSize?: number;\n}\n\n// ─── Exporter ────────────────────────────────────────────────────────────────\n\n/**\n * An OTel `SpanExporter` that translates OTel spans to Sight's custom\n * ingest payload shape and POSTs them to `/ingest`.\n *\n * Each span becomes one object in the `spans` array. Solana-specific\n * attributes flow through as-is (the set that `InstrumentedConnection`\n * already populates — `solana.tx.signature`, `solana.tx.cu_used`,\n * `solana.tx.program`, etc). The exporter intentionally does not attempt\n * to translate span events or links — the ingest schema doesn't have\n * slots for those, and we prefer dropping silently over guessing.\n *\n * The BatchSpanProcessor upstream handles retries, timeouts, and batching;\n * this exporter stays a thin wire-format translator.\n */\nexport class SightSpanExporter implements SpanExporter {\n private readonly apiKey: string;\n private readonly ingestUrl: string;\n private readonly fetchImpl: typeof fetch;\n private readonly maxBatchSize: number;\n private shuttingDown = false;\n\n constructor(config: SightExporterConfig) {\n const parsed = parseDsn(config.dsn);\n this.apiKey = parsed.apiKey;\n this.ingestUrl = parsed.ingestUrl;\n this.fetchImpl = config.fetchImpl ?? (globalThis.fetch as typeof fetch);\n this.maxBatchSize = Math.min(100, config.maxBatchSize ?? 100);\n\n if (!this.fetchImpl) {\n throw new Error(\n 'SightSpanExporter: globalThis.fetch is not available. Node >= 18 ships ' +\n 'with fetch built in, or pass `fetchImpl` explicitly.',\n );\n }\n }\n\n async export(\n spans: ReadableSpan[],\n resultCallback: (result: ExportResult) => void,\n ): Promise<void> {\n if (this.shuttingDown) {\n resultCallback({ code: ExportResultCode.FAILED, error: new Error('Exporter is shut down') });\n return;\n }\n\n // Split into chunks if the upstream processor handed us a larger\n // batch than ingest will accept. Uncommon with a well-configured\n // BatchSpanProcessor but cheap to handle defensively.\n const chunks: ReadableSpan[][] = [];\n for (let i = 0; i < spans.length; i += this.maxBatchSize) {\n chunks.push(spans.slice(i, i + this.maxBatchSize));\n }\n\n try {\n for (const chunk of chunks) {\n await this.sendChunk(chunk);\n }\n resultCallback({ code: ExportResultCode.SUCCESS });\n } catch (err) {\n resultCallback({\n code: ExportResultCode.FAILED,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n }\n }\n\n async shutdown(): Promise<void> {\n this.shuttingDown = true;\n }\n\n async forceFlush(): Promise<void> {\n // The exporter is stateless — BatchSpanProcessor's own forceFlush\n // drains the queue before calling export(). Nothing to do here.\n }\n\n // ─── Internal ──────────────────────────────────────────────────────────────\n\n private async sendChunk(spans: ReadableSpan[]): Promise<void> {\n const payload = {\n spans: spans.map((span) => this.convertSpan(span)),\n };\n\n const response = await this.fetchImpl(this.ingestUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n const body = await safeReadText(response);\n throw new Error(\n `Sight ingest returned ${response.status} ${response.statusText}: ${body}`,\n );\n }\n }\n\n private convertSpan(span: ReadableSpan): IngestSpan {\n const attr = span.attributes;\n const resource = span.resource.attributes;\n const serviceName = typeof resource['service.name'] === 'string'\n ? resource['service.name']\n : undefined;\n const startTimeMs = hrTimeToMilliseconds(span.startTime);\n const endTimeMs = hrTimeToMilliseconds(span.endTime);\n const durationMs = Math.max(0, endTimeMs - startTimeMs);\n\n const out: IngestSpan = {\n traceId: span.spanContext().traceId,\n spanId: span.spanContext().spanId,\n spanName: span.name,\n startTimeMs,\n durationMs,\n };\n\n const parentSpanId = (span as { parentSpanId?: string }).parentSpanId;\n if (parentSpanId) out.parentSpanId = parentSpanId;\n if (serviceName) out.serviceName = serviceName;\n\n copyIfString(attr, 'solana.tx.signature', out);\n copyIfEnum(attr, 'solana.tx.status', out, ['submitted', 'confirmed', 'failed', 'timeout']);\n copyIfNumber(attr, 'solana.tx.slot', out);\n copyIfNumber(attr, 'solana.tx.cu_used', out);\n copyIfNumber(attr, 'solana.tx.cu_budget', out);\n copyIfNumber(attr, 'solana.tx.cu_utilization', out);\n copyIfString(attr, 'solana.tx.program', out);\n copyIfString(attr, 'solana.tx.instruction', out);\n copyIfString(attr, 'solana.tx.error', out);\n copyIfNumber(attr, 'solana.tx.error_code', out);\n copyIfString(attr, 'solana.tx.error_program', out);\n copyIfNumber(attr, 'solana.tx.submit_ms', out);\n copyIfNumber(attr, 'solana.tx.confirmation_ms', out);\n copyIfNumber(attr, 'solana.tx.fee_lamports', out);\n\n return out;\n }\n}\n\n// ─── Wire format (narrow, matches ingest's zod schema) ──────────────────────\n\ninterface IngestSpan {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n serviceName?: string;\n spanName: string;\n startTimeMs: number;\n durationMs: number;\n\n 'solana.tx.signature'?: string;\n 'solana.tx.status'?: 'confirmed' | 'failed' | 'timeout';\n 'solana.tx.slot'?: number;\n 'solana.tx.cu_used'?: number;\n 'solana.tx.cu_budget'?: number;\n 'solana.tx.cu_utilization'?: number;\n 'solana.tx.program'?: string;\n 'solana.tx.instruction'?: string;\n 'solana.tx.error'?: string;\n 'solana.tx.error_code'?: number;\n 'solana.tx.error_program'?: string;\n 'solana.tx.submit_ms'?: number;\n 'solana.tx.confirmation_ms'?: number;\n 'solana.tx.fee_lamports'?: number;\n}\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\nfunction copyIfString(\n attr: SpanAttributes,\n key: keyof IngestSpan & string,\n out: IngestSpan,\n): void {\n const v = attr[key];\n if (typeof v === 'string') {\n (out as unknown as Record<string, unknown>)[key] = v;\n }\n}\n\nfunction copyIfNumber(\n attr: SpanAttributes,\n key: keyof IngestSpan & string,\n out: IngestSpan,\n): void {\n const v = attr[key];\n if (typeof v === 'number' && Number.isFinite(v)) {\n (out as unknown as Record<string, unknown>)[key] = v;\n }\n}\n\nfunction copyIfEnum<T extends string>(\n attr: SpanAttributes,\n key: keyof IngestSpan & string,\n out: IngestSpan,\n allowed: readonly T[],\n): void {\n const v = attr[key];\n if (typeof v === 'string' && (allowed as readonly string[]).includes(v)) {\n (out as unknown as Record<string, unknown>)[key] = v;\n }\n}\n\nasync function safeReadText(res: Response): Promise<string> {\n try {\n return (await res.text()).slice(0, 500);\n } catch {\n return '<no body>';\n }\n}\n","/**\n * Parse a Sight DSN into its constituent parts.\n *\n * DSN format:\n * https://<apiKey>@<host>[:<port>]/<path>\n *\n * Examples:\n * https://sk_live_abc123@ingest.thesight.dev/ingest (production)\n * http://sk_live_abc123@localhost:3001/ingest (local dev)\n *\n * The API key sits in the URL's username position. The SDK extracts it\n * and sends it as a Bearer token to the ingest URL (with the key removed\n * from the URL). This mirrors the Sentry DSN model — one string encodes\n * both \"where to send\" and \"who you are\", so there's no separate\n * apiKey + ingestUrl to misconfig independently.\n */\n\nexport interface ParsedDsn {\n /** The API key extracted from the DSN's username position. */\n apiKey: string;\n /** The ingest endpoint URL with the API key stripped out. */\n ingestUrl: string;\n}\n\n/**\n * Parse a DSN string into `{ apiKey, ingestUrl }`.\n *\n * Throws with a clear message on:\n * - Malformed URL\n * - Missing API key (no username in the URL)\n */\nexport function parseDsn(dsn: string): ParsedDsn {\n let url: URL;\n try {\n url = new URL(dsn);\n } catch {\n throw new Error(\n `Invalid Sight DSN: \"${dsn}\". ` +\n 'Expected format: https://<apiKey>@<host>/ingest — ' +\n 'get your DSN from the Sight dashboard when you create a project.',\n );\n }\n\n const apiKey = decodeURIComponent(url.username);\n if (!apiKey) {\n throw new Error(\n `Invalid Sight DSN: no API key found in \"${dsn}\". ` +\n 'The API key goes in the username position: https://sk_live_xxx@host/ingest',\n );\n }\n\n // Strip the credentials so the ingest URL is clean for HTTP requests.\n url.username = '';\n url.password = '';\n const ingestUrl = url.toString().replace(/\\/$/, ''); // trim trailing slash\n\n return { apiKey, ingestUrl };\n}\n\n/**\n * Build a DSN from its parts. Used by the dashboard when generating the\n * DSN for a newly-created project.\n */\nexport function buildDsn(apiKey: string, ingestHost: string): string {\n const url = new URL(ingestHost);\n url.username = apiKey;\n if (!url.pathname || url.pathname === '/') {\n url.pathname = '/ingest';\n }\n return url.toString();\n}\n","import type { Connection, Finality, TransactionSignature, VersionedTransactionResponse } from '@solana/web3.js';\nimport { trace, context, SpanStatusCode, type Tracer } from '@opentelemetry/api';\nimport { parseLogs, IdlResolver, enrichTree, flatAttributions } from '@thesight/core';\nimport type { AnchorIdl } from '@thesight/core';\n\n// ─── Options ──────────────────────────────────────────────────────────────\n\nexport interface TrackTransactionOptions {\n /** The signature returned from a prior `sendRawTransaction` / `sendTransaction` call. */\n signature: TransactionSignature;\n\n /**\n * Any `@solana/web3.js` Connection — plain or instrumented. Used to fetch\n * the on-chain transaction via `getTransaction` for enrichment. Does\n * **not** need to be an `InstrumentedConnection`; this helper intentionally\n * never touches the transaction-send path.\n */\n connection: Connection;\n\n /**\n * Service name used for the span. Falls back to the service name\n * configured by `initSight`. Useful when you want a different service\n * name for a specific subsystem's spans.\n */\n serviceName?: string;\n\n /** OTel tracer override. Defaults to `trace.getTracer('@thesight/sdk')`. */\n tracer?: Tracer;\n\n /** Optional IDL resolver for program/error decoding. A fresh one is built if omitted. */\n idlResolver?: IdlResolver;\n\n /**\n * Pre-registered IDLs to hand to a freshly-built resolver. Ignored when\n * `idlResolver` is provided.\n */\n idls?: Record<string, AnchorIdl>;\n\n /** Finality commitment used for the enrichment fetch. Default 'confirmed'. */\n commitment?: Finality;\n\n /** Max wall-time before the span is ended with status=timeout. Default 30000ms. */\n timeoutMs?: number;\n\n /** Base poll interval for retrying getTransaction. Default 500ms. */\n pollIntervalMs?: number;\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────\n\n/**\n * **Non-wrapping observation helper.** Given a transaction signature that\n * was already sent through any `Connection`, fetch the on-chain record,\n * parse the logs, and emit a single OpenTelemetry span describing the\n * transaction.\n *\n * Unlike `InstrumentedConnection`, this helper **never touches the send\n * path** — it only observes after the fact via `getTransaction(signature)`.\n * Use this when:\n *\n * - You want observability for wallet-adapter flows where the send path\n * goes through the wallet and you can't easily override the Connection\n * - You're security-conscious and don't want any SDK code on the critical\n * transaction path\n * - You want to instrument a handful of high-value transactions rather\n * than every call a Connection makes\n *\n * Usage:\n *\n * import { trackSolanaTransaction } from '@thesight/sdk';\n * import { Connection } from '@solana/web3.js';\n *\n * const connection = new Connection(rpcUrl);\n * const signature = await wallet.sendTransaction(tx, connection);\n *\n * // Fire-and-forget. Span flushes on its own.\n * void trackSolanaTransaction({\n * signature,\n * connection,\n * serviceName: 'checkout',\n * idls: { [programId]: idl },\n * });\n *\n * Returns a `Promise<void>` — typically not awaited, but callers who want\n * to wait for the span to export (e.g. short-lived scripts) can await it.\n */\nexport async function trackSolanaTransaction(opts: TrackTransactionOptions): Promise<void> {\n const tracerName = opts.serviceName ?? '@thesight/sdk';\n const tracer = opts.tracer ?? trace.getTracer(tracerName);\n\n const span = tracer.startSpan('solana.trackTransaction', {}, context.active());\n span.setAttribute('solana.tx.signature', opts.signature);\n\n const start = Date.now();\n const deadline = start + (opts.timeoutMs ?? 30_000);\n const commitment = opts.commitment ?? ('confirmed' as Finality);\n const basePoll = opts.pollIntervalMs ?? 500;\n\n const resolver = opts.idlResolver ?? buildResolverFromIdls(opts.idls);\n\n try {\n const txDetails = await pollGetTransaction(opts.connection, opts.signature, commitment, deadline, basePoll);\n if (!txDetails) {\n span.setAttribute('solana.tx.status', 'timeout');\n span.setAttribute('solana.tx.enrichment_ms', Date.now() - start);\n return;\n }\n\n span.setAttribute('solana.tx.status', txDetails.meta?.err ? 'failed' : 'confirmed');\n span.setAttribute('solana.tx.slot', txDetails.slot);\n\n const fee = txDetails.meta?.fee;\n if (fee !== undefined) span.setAttribute('solana.tx.fee_lamports', fee);\n\n const cuUsed = txDetails.meta?.computeUnitsConsumed;\n if (cuUsed !== undefined && cuUsed !== null) {\n span.setAttribute('solana.tx.cu_used', Number(cuUsed));\n span.setAttribute('solana.tx.cu_budget', 200_000);\n span.setAttribute(\n 'solana.tx.cu_utilization',\n parseFloat((Number(cuUsed) / 200_000 * 100).toFixed(1)),\n );\n }\n\n const logs = txDetails.meta?.logMessages ?? [];\n if (logs.length > 0) {\n const { cpiTree } = parseLogs({ logs });\n await enrichTree(cpiTree, resolver);\n\n for (const attr of flatAttributions(cpiTree)) {\n span.addEvent('cpi.invoke', {\n 'cpi.program': attr.programName ?? attr.programId,\n 'cpi.instruction': attr.instructionName ?? 'unknown',\n 'cpi.depth': attr.depth,\n 'cpi.cu_consumed': attr.cuConsumed,\n 'cpi.cu_self': attr.cuSelf,\n 'cpi.percentage': parseFloat(attr.percentage.toFixed(2)),\n });\n }\n\n const root = cpiTree.roots[0];\n if (root) {\n if (root.programName) span.setAttribute('solana.tx.program', root.programName);\n if (root.instructionName) span.setAttribute('solana.tx.instruction', root.instructionName);\n }\n }\n\n span.setAttribute('solana.tx.enrichment_ms', Date.now() - start);\n\n if (txDetails.meta?.err) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n } catch (err) {\n span.recordException(err instanceof Error ? err : new Error(String(err)));\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: err instanceof Error ? err.message : String(err),\n });\n } finally {\n span.end();\n }\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────\n\nfunction buildResolverFromIdls(idls?: Record<string, AnchorIdl>): IdlResolver {\n const resolver = new IdlResolver();\n if (idls) resolver.registerMany(idls);\n return resolver;\n}\n\nasync function pollGetTransaction(\n connection: Connection,\n signature: TransactionSignature,\n commitment: Finality,\n deadline: number,\n basePollMs: number,\n): Promise<VersionedTransactionResponse | null> {\n let attempt = 0;\n while (Date.now() < deadline) {\n try {\n const tx = await connection.getTransaction(signature, {\n commitment,\n maxSupportedTransactionVersion: 0,\n });\n if (tx) return tx;\n } catch {\n // Ignore — retry with backoff\n }\n attempt++;\n const waitMs = Math.min(basePollMs * Math.pow(1.5, attempt - 1), 2_000);\n await new Promise((resolve) => setTimeout(resolve, waitMs));\n }\n return null;\n}\n"],"mappings":"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","names":["import_api","import_core","import_api","import_core"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/init.ts","../src/exporter.ts","../src/dsn.ts","../src/track.ts"],"sourcesContent":["import {\n Connection,\n type ConnectionConfig,\n type SendOptions,\n type Commitment,\n type Finality,\n type TransactionSignature,\n type VersionedTransactionResponse,\n} from '@solana/web3.js';\nimport {\n trace,\n context,\n SpanStatusCode,\n type Span,\n type Tracer,\n} from '@opentelemetry/api';\nimport { parseLogs, IdlResolver, enrichTree, flatAttributions } from '@thesight/core';\nimport type { AnchorIdl, CpiTree } from '@thesight/core';\n\n// ─── Config ────────────────────────────────────────────────────────────────\n\nexport interface SightConfig {\n /** OTel tracer. Defaults to `trace.getTracer('@thesight/sdk')`. */\n tracer?: Tracer;\n\n /** Override RPC endpoint for IDL fetching (defaults to same as connection) */\n idlRpcEndpoint?: string;\n\n /**\n * Commitment used when fetching confirmed tx details for enrichment.\n * Defaults to 'confirmed'.\n */\n commitment?: Commitment;\n\n /**\n * Skip IDL resolution entirely. Spans will still emit with signature and\n * timing attributes, but program names, instruction names, CPI trees, and\n * decoded errors will all be omitted. Useful when you want minimal\n * overhead and don't care about the richer observability.\n */\n skipIdlResolution?: boolean;\n\n /**\n * Pre-register IDLs at construction time. Keys are program IDs, values are\n * full Anchor IDL objects. Anchor users can pass `program.idl` directly off\n * their `Program` instance — zero setup friction.\n */\n idls?: Record<string, AnchorIdl>;\n\n /**\n * Opt into reading Anchor IDL accounts from the on-chain PDA as a fallback\n * when a program is not explicitly registered. Defaults to **false** —\n * Sight's model is that IDLs are explicitly provided by the application,\n * not silently guessed from chain.\n */\n allowOnChainIdlFetch?: boolean;\n\n /**\n * Max wall-time the background enrichment task will wait for a\n * transaction to show up in `getTransaction`. After this expires the\n * span is ended with `solana.tx.status = 'timeout'`. Default 30000 (30s).\n */\n enrichmentTimeoutMs?: number;\n\n /**\n * How often the enrichment task polls `getTransaction`. Each retry backs\n * off by 1.5x up to a cap. Default 500ms base.\n */\n enrichmentPollIntervalMs?: number;\n\n /**\n * Disable automatic span creation in the overridden `sendRawTransaction`.\n * When true, InstrumentedConnection behaves like a plain Connection. You\n * probably don't want this — it's here as an escape hatch for tests and\n * rare cases where you need the class hierarchy but not the tracing.\n */\n disableAutoSpan?: boolean;\n}\n\n// ─── Span attribute shape (documented, for downstream consumers) ──────────\n\nexport interface SightSpanAttributes {\n 'solana.tx.signature': string;\n 'solana.tx.status': 'submitted' | 'confirmed' | 'failed' | 'timeout';\n 'solana.tx.slot'?: number;\n 'solana.tx.fee_lamports'?: number;\n 'solana.tx.submit_ms': number;\n 'solana.tx.enrichment_ms'?: number;\n 'solana.tx.cu_used'?: number;\n 'solana.tx.cu_budget'?: number;\n 'solana.tx.cu_utilization'?: number;\n 'solana.tx.program'?: string;\n 'solana.tx.instruction'?: string;\n 'solana.tx.error'?: string;\n 'solana.tx.error_code'?: number;\n 'solana.tx.error_program'?: string;\n 'solana.tx.error_msg'?: string;\n}\n\n// ─── InstrumentedConnection ────────────────────────────────────────────────\n\n/**\n * Drop-in replacement for `@solana/web3.js`'s `Connection` that emits an\n * OpenTelemetry span for **every** call to `sendRawTransaction` —\n * regardless of whether the caller is your own code, an Anchor provider's\n * `sendAndConfirm`, `@solana/web3.js`'s top-level `sendAndConfirmTransaction`,\n * a wallet adapter, or anything else.\n *\n * Internally it overrides the parent class's `sendRawTransaction`, so the\n * span covers the same surface web3.js already mediates. No mutation of\n * the transaction bytes — we call `super.sendRawTransaction(rawTx, opts)`\n * verbatim and observe the signature it returns.\n *\n * After the signature is returned (synchronously from the caller's point\n * of view), a background task polls `getTransaction` until the on-chain\n * record is available, parses the program logs via `@thesight/core`, and\n * enriches the span with CU attribution, per-CPI events, program + instruction\n * names, and decoded error details. The background task never blocks the\n * caller — if it fails or times out, the span ends with status=timeout and\n * whatever partial data was collected.\n *\n * Usage:\n *\n * import { initSight, InstrumentedConnection } from '@thesight/sdk';\n *\n * initSight({ dsn: process.env.SIGHT_DSN!, serviceName: 'swap-bot' });\n *\n * // Anywhere you currently construct a Connection, construct this instead:\n * const connection = new InstrumentedConnection(rpcUrl, {\n * commitment: 'confirmed',\n * });\n *\n * // All of the following now emit spans automatically:\n * await connection.sendRawTransaction(tx.serialize());\n * await sendAndConfirmTransaction(connection, tx, signers); // web3.js\n * await program.methods.foo().rpc(); // Anchor\n * await wallet.sendTransaction(tx, connection); // wallet adapter\n */\nexport class InstrumentedConnection extends Connection {\n private sightConfig: SightConfig;\n private idlResolver: IdlResolver;\n private tracer: Tracer;\n\n constructor(endpoint: string, config?: ConnectionConfig & SightConfig) {\n const {\n tracer,\n idlRpcEndpoint,\n skipIdlResolution,\n idls,\n allowOnChainIdlFetch,\n enrichmentTimeoutMs,\n enrichmentPollIntervalMs,\n disableAutoSpan,\n commitment,\n ...connectionConfig\n } = config ?? {};\n\n super(endpoint, connectionConfig);\n\n this.sightConfig = {\n tracer,\n idlRpcEndpoint,\n skipIdlResolution,\n allowOnChainIdlFetch,\n enrichmentTimeoutMs: enrichmentTimeoutMs ?? 30_000,\n enrichmentPollIntervalMs: enrichmentPollIntervalMs ?? 500,\n disableAutoSpan,\n commitment,\n };\n this.idlResolver = new IdlResolver({\n rpcEndpoint: idlRpcEndpoint ?? endpoint,\n allowOnChainFetch: allowOnChainIdlFetch ?? false,\n });\n if (idls) this.idlResolver.registerMany(idls);\n this.tracer = tracer ?? trace.getTracer('@thesight/sdk');\n }\n\n /**\n * Register an IDL for a single program. Convenience forwarder for the\n * underlying resolver. Anchor users typically call this right after\n * constructing a `Program`:\n *\n * const program = new Program(idl, programId, provider);\n * connection.registerIdl(program.programId.toBase58(), program.idl);\n */\n registerIdl(programId: string, idl: AnchorIdl): void {\n this.idlResolver.register(programId, idl);\n }\n\n /** Bulk-register multiple IDLs. */\n registerIdls(idls: Record<string, AnchorIdl>): void {\n this.idlResolver.registerMany(idls);\n }\n\n // ─── Overridden method ────────────────────────────────────────────────────\n\n /**\n * Overrides the parent `Connection.sendRawTransaction` so every submit —\n * including those made by Anchor's `provider.sendAndConfirm`, web3.js's\n * top-level `sendAndConfirmTransaction`, and wallet adapter flows — is\n * observed by an OTel span automatically.\n *\n * The span is a child of whatever OTel context is active when the call\n * fires, so app-level parent spans (HTTP handlers, job runs, wallet flow\n * spans from another SDK) nest the Sight span correctly.\n */\n override async sendRawTransaction(\n rawTransaction: Buffer | Uint8Array | Array<number>,\n options?: SendOptions,\n ): Promise<TransactionSignature> {\n if (this.sightConfig.disableAutoSpan) {\n return super.sendRawTransaction(rawTransaction, options);\n }\n\n const span = this.tracer.startSpan('solana.sendRawTransaction', {}, context.active());\n const submitStart = Date.now();\n\n let signature: TransactionSignature;\n try {\n signature = await super.sendRawTransaction(rawTransaction, options);\n } catch (err) {\n const submitMs = Date.now() - submitStart;\n span.setAttribute('solana.tx.submit_ms', submitMs);\n span.setAttribute('solana.tx.status', 'failed');\n span.recordException(err instanceof Error ? err : new Error(String(err)));\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: err instanceof Error ? err.message : String(err),\n });\n span.end();\n throw err;\n }\n\n span.setAttribute('solana.tx.signature', signature);\n span.setAttribute('solana.tx.submit_ms', Date.now() - submitStart);\n span.setAttribute('solana.tx.status', 'submitted');\n\n // Fire-and-forget background enrichment. The returned Promise is\n // intentionally discarded — we don't want to couple the caller's\n // transaction-send latency to our observation machinery. Any enrichment\n // error is caught inside enrichSpanInBackground and attached to the\n // span, not bubbled up.\n void this.enrichSpanInBackground(span, signature, submitStart);\n\n return signature;\n }\n\n // ─── Background enrichment ───────────────────────────────────────────────\n\n /**\n * Poll `getTransaction` until the on-chain record is available, then\n * enrich the span with CU attribution, program names, decoded errors,\n * and per-CPI events.\n *\n * This runs async and is never awaited by callers of `sendRawTransaction`.\n * Failures inside this method attach to the span via recordException\n * rather than throwing.\n */\n private async enrichSpanInBackground(\n span: Span,\n signature: TransactionSignature,\n submitStart: number,\n ): Promise<void> {\n const enrichStart = Date.now();\n const deadline = enrichStart + (this.sightConfig.enrichmentTimeoutMs ?? 30_000);\n const commitment = (this.sightConfig.commitment ?? 'confirmed') as Finality;\n const basePollMs = this.sightConfig.enrichmentPollIntervalMs ?? 500;\n\n try {\n const txDetails = await this.pollForTransaction(signature, commitment, deadline, basePollMs);\n\n if (!txDetails) {\n span.setAttribute('solana.tx.status', 'timeout');\n span.setAttribute('solana.tx.enrichment_ms', Date.now() - submitStart);\n span.end();\n return;\n }\n\n this.attachTxDetailsToSpan(span, txDetails);\n\n if (!this.sightConfig.skipIdlResolution) {\n const logs = txDetails.meta?.logMessages ?? [];\n if (logs.length > 0) {\n await this.attachParsedLogsToSpan(span, logs);\n }\n }\n\n span.setAttribute('solana.tx.enrichment_ms', Date.now() - submitStart);\n\n if (txDetails.meta?.err) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n } catch (err) {\n span.recordException(err instanceof Error ? err : new Error(String(err)));\n span.setAttribute(\n 'solana.tx.enrichment_error',\n err instanceof Error ? err.message : String(err),\n );\n } finally {\n span.end();\n }\n }\n\n /**\n * Poll `getTransaction(signature)` until either the on-chain record is\n * returned or the deadline passes. Exponential backoff (1.5x) capped at\n * 2 seconds to balance responsiveness against RPC load.\n */\n private async pollForTransaction(\n signature: TransactionSignature,\n commitment: Finality,\n deadline: number,\n basePollMs: number,\n ): Promise<VersionedTransactionResponse | null> {\n let attempt = 0;\n while (Date.now() < deadline) {\n try {\n const tx = await super.getTransaction(signature, {\n commitment,\n maxSupportedTransactionVersion: 0,\n });\n if (tx) return tx;\n } catch {\n // Ignore — retry with backoff. Transient RPC errors are common\n // during the seconds immediately after submission.\n }\n attempt++;\n const waitMs = Math.min(basePollMs * Math.pow(1.5, attempt - 1), 2_000);\n await sleep(waitMs);\n }\n return null;\n }\n\n /** Attach the flat fields (slot, fee, CU, status) from a tx response to a span. */\n private attachTxDetailsToSpan(span: Span, txDetails: VersionedTransactionResponse): void {\n span.setAttribute('solana.tx.status', txDetails.meta?.err ? 'failed' : 'confirmed');\n span.setAttribute('solana.tx.slot', txDetails.slot);\n\n const fee = txDetails.meta?.fee;\n if (fee !== undefined) span.setAttribute('solana.tx.fee_lamports', fee);\n\n const cuUsed = txDetails.meta?.computeUnitsConsumed;\n if (cuUsed !== undefined && cuUsed !== null) {\n span.setAttribute('solana.tx.cu_used', Number(cuUsed));\n span.setAttribute('solana.tx.cu_budget', 200_000);\n span.setAttribute(\n 'solana.tx.cu_utilization',\n parseFloat((Number(cuUsed) / 200_000 * 100).toFixed(1)),\n );\n }\n }\n\n /**\n * Parse program logs into a CPI tree, enrich with registered IDLs, and\n * emit one `cpi.invoke` event per invocation onto the span. Root\n * program/instruction names are copied onto span attributes so dashboards\n * can filter by them without walking events.\n */\n private async attachParsedLogsToSpan(span: Span, logs: string[]): Promise<CpiTree | undefined> {\n const { cpiTree } = parseLogs({ logs });\n await enrichTree(cpiTree, this.idlResolver);\n\n const attributions = flatAttributions(cpiTree);\n for (const attr of attributions) {\n span.addEvent('cpi.invoke', {\n 'cpi.program': attr.programName ?? attr.programId,\n 'cpi.instruction': attr.instructionName ?? 'unknown',\n 'cpi.depth': attr.depth,\n 'cpi.cu_consumed': attr.cuConsumed,\n 'cpi.cu_self': attr.cuSelf,\n 'cpi.percentage': parseFloat(attr.percentage.toFixed(2)),\n });\n }\n\n const root = cpiTree.roots[0];\n if (root) {\n if (root.programName) span.setAttribute('solana.tx.program', root.programName);\n if (root.instructionName) span.setAttribute('solana.tx.instruction', root.instructionName);\n }\n\n return cpiTree;\n }\n}\n\n// ─── Utilities ────────────────────────────────────────────────────────────\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n// ─── Re-exports ────────────────────────────────────────────────────────────\n\nexport { IdlResolver } from '@thesight/core';\nexport type {\n CpiTree,\n CuAttribution,\n FlamegraphItem,\n DecodedError,\n AnchorIdl,\n} from '@thesight/core';\n\n// ─── Tracing initialization ──────────────────────────────────────────────────\n\nexport { initSight } from './init.js';\nexport type { InitSightConfig, SightTracerHandle } from './init.js';\nexport { SightSpanExporter } from './exporter.js';\nexport type { SightExporterConfig } from './exporter.js';\n\n// ─── DSN helpers ─────────────────────────────────────────────────────────\n\nexport { parseDsn, buildDsn } from './dsn.js';\nexport type { ParsedDsn } from './dsn.js';\n\n// ─── Non-wrapping observation helper ─────────────────────────────────────\n\nexport { trackSolanaTransaction } from './track.js';\nexport type { TrackTransactionOptions } from './track.js';\n","import { trace, type Tracer } from '@opentelemetry/api';\nimport { SightSpanExporter } from './exporter.js';\n\n// ─── Dynamic imports ────────────────────────────────────────────────────────\n// The OTel SDK packages (@opentelemetry/sdk-trace-node, sdk-trace-base,\n// resources, semantic-conventions) are the source of version-conflict\n// errors when Sentry or another OTel consumer is in the same process.\n// By importing them dynamically inside initSight, the conflict is:\n// (a) confined to the try/catch that handles it\n// (b) deferred until the user actually calls initSight, not at\n// module-load time (which would crash the entire require chain)\n\n// ─── Config ──────────────────────────────────────────────────────────────────\n\nexport interface InitSightConfig {\n /**\n * Sight DSN — one string encoding both the ingest endpoint and the API key.\n *\n * Format: `https://<apiKey>@<host>/ingest`\n *\n * Get yours from the Sight dashboard when you create a project. For local\n * dev against a Docker compose stack: `http://sk_live_xxx@localhost:3001/ingest`\n */\n dsn: string;\n\n /**\n * Human-readable service name that shows up on every span. Pick something\n * that identifies this process — `'swap-bot'`, `'market-maker'`,\n * `'trade-settler'`, etc.\n */\n serviceName: string;\n\n /** Optional version string for the service. Useful for correlating spans with a deploy. */\n serviceVersion?: string;\n\n /** BatchSpanProcessor flush interval, in ms. Defaults to 5s. */\n batchDelayMs?: number;\n\n /** Max spans per HTTP POST. Clamped to 100 by the exporter. */\n maxBatchSize?: number;\n\n /** Inject a fetch implementation for tests. Defaults to global fetch. */\n fetchImpl?: typeof fetch;\n}\n\nexport interface SightTracerHandle {\n /**\n * The OTel Tracer that routes spans through the Sight exporter. Pass\n * this to `InstrumentedConnection({ tracer: sight.tracer })`.\n *\n * Always works — even when another OTel provider (Sentry, Datadog)\n * is in the process. This is the recommended way to wire the connection.\n */\n tracer: Tracer;\n\n /**\n * How the tracer was obtained:\n * - `'own_provider'` — Sight created its own NodeTracerProvider and\n * (optionally) registered it globally. Full control.\n * - `'piggyback'` — Another provider (e.g. Sentry) was already\n * registered. Sight added its exporter as an additional span\n * processor on that provider. Both consumers see all spans.\n * - `'api_only'` — Neither own-provider nor piggyback worked.\n * Sight is using the global `trace.getTracer()` which may or may\n * not route to Sight's exporter depending on what else is registered.\n * This is the least reliable mode.\n */\n mode: 'own_provider' | 'piggyback' | 'api_only';\n\n /** Flush any pending spans and release the batch processor. */\n shutdown: () => Promise<void>;\n}\n\n// ─── Entry point ────────────────────────────────────────────────────────────\n\n/**\n * Initialize Sight tracing. Call this **once** near the top of your\n * process entry point.\n *\n * import { initSight, InstrumentedConnection } from '@thesight/sdk';\n *\n * const sight = initSight({\n * dsn: process.env.SIGHT_DSN!,\n * serviceName: 'swap-bot',\n * });\n *\n * const connection = new InstrumentedConnection(process.env.RPC_URL!, {\n * tracer: sight.tracer,\n * });\n *\n * Handles coexistence with other OTel providers (e.g. @sentry/node)\n * gracefully via a three-tier fallback:\n *\n * 1. **Own provider**: create a NodeTracerProvider, add the Sight\n * exporter, register globally. This is the ideal path.\n * 2. **Piggyback**: if step 1 fails (OTel version conflict with Sentry),\n * find the existing global provider and add the Sight exporter to it\n * as an additional span processor. Both Sentry and Sight see spans.\n * 3. **API-only**: if step 2 also fails, fall back to `trace.getTracer()`\n * from the OTel API layer. Spans may or may not reach Sight depending\n * on what's registered. A warning is logged.\n */\nexport function initSight(config: InitSightConfig): SightTracerHandle {\n if (!config.dsn) {\n throw new Error(\n 'initSight: `dsn` is required. ' +\n 'Get your DSN from the Sight dashboard: https://thesight.dev',\n );\n }\n if (!config.serviceName) {\n throw new Error('initSight: `serviceName` is required');\n }\n\n let _lastTier1Error: unknown = null;\n let _lastTier2Error: unknown = null;\n\n const exporter = new SightSpanExporter({\n dsn: config.dsn,\n fetchImpl: config.fetchImpl,\n maxBatchSize: config.maxBatchSize,\n });\n\n // ── Tier 1: own provider ──────────────────────────────────────────────\n\n try {\n // Use eval('require') to hide these from Next.js webpack / turbopack.\n // Without eval, webpack replaces require() with its own module\n // resolution which can return different exports than Node's native\n // require — causing NodeTracerProvider to be undefined even though\n // the package is installed. This is the same pattern the bankroll-sdk\n // uses for @sentry/node.\n // eslint-disable-next-line no-eval\n const _require = eval('require') as NodeRequire;\n const { NodeTracerProvider } = _require('@opentelemetry/sdk-trace-node');\n const { BatchSpanProcessor } = _require('@opentelemetry/sdk-trace-base');\n const otelResources = _require('@opentelemetry/resources');\n\n const processor = new BatchSpanProcessor(exporter, {\n scheduledDelayMillis: config.batchDelayMs ?? 5_000,\n maxExportBatchSize: config.maxBatchSize ?? 100,\n maxQueueSize: 2048,\n });\n\n // OTel 2.x removed the Resource class in favor of resourceFromAttributes().\n // OTel 1.x has Resource but not resourceFromAttributes.\n // Handle both so the SDK works regardless of which version the host\n // project resolves.\n const resourceAttrs: Record<string, string> = {\n 'service.name': config.serviceName,\n ...(config.serviceVersion ? { 'service.version': config.serviceVersion } : {}),\n };\n const resource = typeof otelResources.resourceFromAttributes === 'function'\n ? otelResources.resourceFromAttributes(resourceAttrs)\n : new otelResources.Resource(resourceAttrs);\n\n // OTel 2.x removed provider.addSpanProcessor() in favor of passing\n // spanProcessors in the constructor. OTel 1.x has addSpanProcessor\n // but not the constructor option. Handle both.\n let provider: any;\n if (typeof NodeTracerProvider.prototype.addSpanProcessor === 'function') {\n // OTel 1.x path\n provider = new NodeTracerProvider({ resource });\n provider.addSpanProcessor(processor);\n } else {\n // OTel 2.x path\n provider = new NodeTracerProvider({ resource, spanProcessors: [processor] });\n }\n\n try {\n provider.register();\n } catch {\n // Registration failed (another provider owns the global), but\n // our standalone provider still works.\n }\n\n return {\n tracer: provider.getTracer('@thesight/sdk'),\n mode: 'own_provider' as const,\n shutdown: async () => { await provider.shutdown(); },\n };\n } catch (tier1Err) {\n _lastTier1Error = tier1Err;\n }\n\n // ── Tier 2: piggyback on the existing global provider ─────────────────\n //\n // If another provider (Sentry) already registered, try adding our\n // exporter as an additional span processor. This makes both Sentry\n // and Sight see the same spans — the ideal coexistence model.\n //\n // Critically, this tier does NOT import from @opentelemetry/sdk-trace-base\n // or sdk-trace-node — those are the version-conflicting modules. Instead\n // it uses a minimal inline span processor that implements the interface\n // via duck-typing. The OTel provider calls processor.onEnd(span) for\n // every finished span; our processor buffers them and calls the Sight\n // exporter. No version-sensitive imports needed.\n\n try {\n const proxy = trace.getTracerProvider() as any;\n const delegate = proxy?.getDelegate?.() ?? proxy;\n\n if (delegate && typeof delegate.addSpanProcessor === 'function') {\n const processor = new SightInlineProcessor(\n exporter,\n config.batchDelayMs ?? 5_000,\n config.maxBatchSize ?? 100,\n );\n delegate.addSpanProcessor(processor);\n return {\n tracer: trace.getTracer('@thesight/sdk'),\n mode: 'piggyback',\n shutdown: async () => { await processor.shutdown(); },\n };\n }\n } catch (tier2Err) {\n _lastTier2Error = tier2Err;\n }\n\n // ── Tier 3: API-only fallback ─────────────────────────────────────────\n\n if (typeof console !== 'undefined') {\n console.warn(\n '[sight] Could not create an OTel tracer provider (tier 1) or ' +\n 'attach to the existing one (tier 2). Sight spans may not reach ingest.',\n );\n if (_lastTier1Error) {\n console.warn('[sight] Tier 1 error:', _lastTier1Error instanceof Error ? _lastTier1Error.message : String(_lastTier1Error));\n }\n if (_lastTier2Error) {\n console.warn('[sight] Tier 2 error:', _lastTier2Error instanceof Error ? _lastTier2Error.message : String(_lastTier2Error));\n }\n }\n\n return {\n tracer: trace.getTracer('@thesight/sdk'),\n mode: 'api_only',\n shutdown: async () => {},\n };\n}\n\n// ─── Inline span processor ─────────────────────────────────────────────────\n//\n// A minimal batch span processor that implements the OTel SpanProcessor\n// interface structurally (duck-typed). It does NOT import from\n// @opentelemetry/sdk-trace-base, which is the module that version-\n// conflicts with Sentry. Instead it implements the four methods the\n// provider calls (onStart, onEnd, shutdown, forceFlush) and drives\n// our SightSpanExporter directly.\n//\n// This class only exists for the tier-2 piggyback path. Tier 1 uses\n// the real BatchSpanProcessor from the OTel SDK (which has nicer\n// retry/backpressure logic); tier 2 uses this inline version because\n// it's the only way to avoid the version conflict.\n\nclass SightInlineProcessor {\n private buffer: unknown[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private readonly maxBatch: number;\n\n constructor(\n private readonly exporter: SightSpanExporter,\n delayMs: number,\n maxBatch: number,\n ) {\n this.maxBatch = maxBatch;\n this.timer = setInterval(() => this.flush(), delayMs);\n // Prevent the timer from keeping the process alive if everything\n // else has exited. Node-specific but safe — `unref` is a no-op in\n // environments that don't have it.\n if (this.timer && typeof (this.timer as any).unref === 'function') {\n (this.timer as any).unref();\n }\n }\n\n /** Called by the provider when a span starts. We don't need it. */\n onStart(): void {}\n\n /** Called by the provider when a span ends. Buffer it for export. */\n onEnd(span: unknown): void {\n this.buffer.push(span);\n if (this.buffer.length >= this.maxBatch) {\n this.flush();\n }\n }\n\n /** Flush pending spans to the exporter. */\n private flush(): void {\n if (this.buffer.length === 0) return;\n const batch = this.buffer.splice(0, this.maxBatch);\n // exporter.export expects ReadableSpan[] — the spans from the\n // provider's onEnd ARE ReadableSpan instances regardless of which\n // sdk-trace-base version created them, because the interface is\n // structurally stable across 1.x versions.\n this.exporter.export(batch as any, () => {});\n }\n\n async shutdown(): Promise<void> {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n this.flush();\n await this.exporter.shutdown();\n }\n\n async forceFlush(): Promise<void> {\n this.flush();\n }\n}\n","import type { SpanAttributes } from '@opentelemetry/api';\nimport type { SpanExporter, ReadableSpan } from '@opentelemetry/sdk-trace-base';\nimport {\n hrTimeToMilliseconds,\n ExportResultCode,\n type ExportResult,\n} from '@opentelemetry/core';\nimport { parseDsn } from './dsn.js';\n\n// ─── Config ──────────────────────────────────────────────────────────────────\n\nexport interface SightExporterConfig {\n /**\n * Sight DSN — one string encoding both the ingest endpoint and the API key.\n * Format: https://<apiKey>@<host>/ingest\n */\n dsn: string;\n /** Inject a fake fetch in tests. Defaults to `globalThis.fetch`. */\n fetchImpl?: typeof fetch;\n /** Max spans per POST. Ingest enforces an upper bound of 100. */\n maxBatchSize?: number;\n}\n\n// ─── Exporter ────────────────────────────────────────────────────────────────\n\n/**\n * An OTel `SpanExporter` that translates OTel spans to Sight's custom\n * ingest payload shape and POSTs them to `/ingest`.\n *\n * Each span becomes one object in the `spans` array. Solana-specific\n * attributes flow through as-is (the set that `InstrumentedConnection`\n * already populates — `solana.tx.signature`, `solana.tx.cu_used`,\n * `solana.tx.program`, etc). The exporter intentionally does not attempt\n * to translate span events or links — the ingest schema doesn't have\n * slots for those, and we prefer dropping silently over guessing.\n *\n * The BatchSpanProcessor upstream handles retries, timeouts, and batching;\n * this exporter stays a thin wire-format translator.\n */\nexport class SightSpanExporter implements SpanExporter {\n private readonly apiKey: string;\n private readonly ingestUrl: string;\n private readonly fetchImpl: typeof fetch;\n private readonly maxBatchSize: number;\n private shuttingDown = false;\n\n constructor(config: SightExporterConfig) {\n const parsed = parseDsn(config.dsn);\n this.apiKey = parsed.apiKey;\n this.ingestUrl = parsed.ingestUrl;\n this.fetchImpl = config.fetchImpl ?? (globalThis.fetch as typeof fetch);\n this.maxBatchSize = Math.min(100, config.maxBatchSize ?? 100);\n\n if (!this.fetchImpl) {\n throw new Error(\n 'SightSpanExporter: globalThis.fetch is not available. Node >= 18 ships ' +\n 'with fetch built in, or pass `fetchImpl` explicitly.',\n );\n }\n }\n\n async export(\n spans: ReadableSpan[],\n resultCallback: (result: ExportResult) => void,\n ): Promise<void> {\n if (this.shuttingDown) {\n resultCallback({ code: ExportResultCode.FAILED, error: new Error('Exporter is shut down') });\n return;\n }\n\n // Split into chunks if the upstream processor handed us a larger\n // batch than ingest will accept. Uncommon with a well-configured\n // BatchSpanProcessor but cheap to handle defensively.\n const chunks: ReadableSpan[][] = [];\n for (let i = 0; i < spans.length; i += this.maxBatchSize) {\n chunks.push(spans.slice(i, i + this.maxBatchSize));\n }\n\n try {\n for (const chunk of chunks) {\n await this.sendChunk(chunk);\n }\n resultCallback({ code: ExportResultCode.SUCCESS });\n } catch (err) {\n resultCallback({\n code: ExportResultCode.FAILED,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n }\n }\n\n async shutdown(): Promise<void> {\n this.shuttingDown = true;\n }\n\n async forceFlush(): Promise<void> {\n // The exporter is stateless — BatchSpanProcessor's own forceFlush\n // drains the queue before calling export(). Nothing to do here.\n }\n\n // ─── Internal ──────────────────────────────────────────────────────────────\n\n private async sendChunk(spans: ReadableSpan[]): Promise<void> {\n const payload = {\n spans: spans.map((span) => this.convertSpan(span)),\n };\n\n const response = await this.fetchImpl(this.ingestUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n const body = await safeReadText(response);\n throw new Error(\n `Sight ingest returned ${response.status} ${response.statusText}: ${body}`,\n );\n }\n }\n\n private convertSpan(span: ReadableSpan): IngestSpan {\n const attr = span.attributes;\n const resource = span.resource.attributes;\n const serviceName = typeof resource['service.name'] === 'string'\n ? resource['service.name']\n : undefined;\n const startTimeMs = hrTimeToMilliseconds(span.startTime);\n const endTimeMs = hrTimeToMilliseconds(span.endTime);\n const durationMs = Math.max(0, endTimeMs - startTimeMs);\n\n const out: IngestSpan = {\n traceId: span.spanContext().traceId,\n spanId: span.spanContext().spanId,\n spanName: span.name,\n startTimeMs,\n durationMs,\n };\n\n const parentSpanId = (span as { parentSpanId?: string }).parentSpanId;\n if (parentSpanId) out.parentSpanId = parentSpanId;\n if (serviceName) out.serviceName = serviceName;\n\n copyIfString(attr, 'solana.tx.signature', out);\n copyIfEnum(attr, 'solana.tx.status', out, ['submitted', 'confirmed', 'failed', 'timeout']);\n copyIfNumber(attr, 'solana.tx.slot', out);\n copyIfNumber(attr, 'solana.tx.cu_used', out);\n copyIfNumber(attr, 'solana.tx.cu_budget', out);\n copyIfNumber(attr, 'solana.tx.cu_utilization', out);\n copyIfString(attr, 'solana.tx.program', out);\n copyIfString(attr, 'solana.tx.instruction', out);\n copyIfString(attr, 'solana.tx.error', out);\n copyIfNumber(attr, 'solana.tx.error_code', out);\n copyIfString(attr, 'solana.tx.error_program', out);\n copyIfNumber(attr, 'solana.tx.submit_ms', out);\n copyIfNumber(attr, 'solana.tx.confirmation_ms', out);\n copyIfNumber(attr, 'solana.tx.fee_lamports', out);\n\n return out;\n }\n}\n\n// ─── Wire format (narrow, matches ingest's zod schema) ──────────────────────\n\ninterface IngestSpan {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n serviceName?: string;\n spanName: string;\n startTimeMs: number;\n durationMs: number;\n\n 'solana.tx.signature'?: string;\n 'solana.tx.status'?: 'confirmed' | 'failed' | 'timeout';\n 'solana.tx.slot'?: number;\n 'solana.tx.cu_used'?: number;\n 'solana.tx.cu_budget'?: number;\n 'solana.tx.cu_utilization'?: number;\n 'solana.tx.program'?: string;\n 'solana.tx.instruction'?: string;\n 'solana.tx.error'?: string;\n 'solana.tx.error_code'?: number;\n 'solana.tx.error_program'?: string;\n 'solana.tx.submit_ms'?: number;\n 'solana.tx.confirmation_ms'?: number;\n 'solana.tx.fee_lamports'?: number;\n}\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\nfunction copyIfString(\n attr: SpanAttributes,\n key: keyof IngestSpan & string,\n out: IngestSpan,\n): void {\n const v = attr[key];\n if (typeof v === 'string') {\n (out as unknown as Record<string, unknown>)[key] = v;\n }\n}\n\nfunction copyIfNumber(\n attr: SpanAttributes,\n key: keyof IngestSpan & string,\n out: IngestSpan,\n): void {\n const v = attr[key];\n if (typeof v === 'number' && Number.isFinite(v)) {\n (out as unknown as Record<string, unknown>)[key] = v;\n }\n}\n\nfunction copyIfEnum<T extends string>(\n attr: SpanAttributes,\n key: keyof IngestSpan & string,\n out: IngestSpan,\n allowed: readonly T[],\n): void {\n const v = attr[key];\n if (typeof v === 'string' && (allowed as readonly string[]).includes(v)) {\n (out as unknown as Record<string, unknown>)[key] = v;\n }\n}\n\nasync function safeReadText(res: Response): Promise<string> {\n try {\n return (await res.text()).slice(0, 500);\n } catch {\n return '<no body>';\n }\n}\n","/**\n * Parse a Sight DSN into its constituent parts.\n *\n * DSN format:\n * https://<apiKey>@<host>[:<port>]/<path>\n *\n * Examples:\n * https://sk_live_abc123@ingest.thesight.dev/ingest (production)\n * http://sk_live_abc123@localhost:3001/ingest (local dev)\n *\n * The API key sits in the URL's username position. The SDK extracts it\n * and sends it as a Bearer token to the ingest URL (with the key removed\n * from the URL). This mirrors the Sentry DSN model — one string encodes\n * both \"where to send\" and \"who you are\", so there's no separate\n * apiKey + ingestUrl to misconfig independently.\n */\n\nexport interface ParsedDsn {\n /** The API key extracted from the DSN's username position. */\n apiKey: string;\n /** The ingest endpoint URL with the API key stripped out. */\n ingestUrl: string;\n}\n\n/**\n * Parse a DSN string into `{ apiKey, ingestUrl }`.\n *\n * Throws with a clear message on:\n * - Malformed URL\n * - Missing API key (no username in the URL)\n */\nexport function parseDsn(dsn: string): ParsedDsn {\n let url: URL;\n try {\n url = new URL(dsn);\n } catch {\n throw new Error(\n `Invalid Sight DSN: \"${dsn}\". ` +\n 'Expected format: https://<apiKey>@<host>/ingest — ' +\n 'get your DSN from the Sight dashboard when you create a project.',\n );\n }\n\n const apiKey = decodeURIComponent(url.username);\n if (!apiKey) {\n throw new Error(\n `Invalid Sight DSN: no API key found in \"${dsn}\". ` +\n 'The API key goes in the username position: https://sk_live_xxx@host/ingest',\n );\n }\n\n // Strip the credentials so the ingest URL is clean for HTTP requests.\n url.username = '';\n url.password = '';\n const ingestUrl = url.toString().replace(/\\/$/, ''); // trim trailing slash\n\n return { apiKey, ingestUrl };\n}\n\n/**\n * Build a DSN from its parts. Used by the dashboard when generating the\n * DSN for a newly-created project.\n */\nexport function buildDsn(apiKey: string, ingestHost: string): string {\n const url = new URL(ingestHost);\n url.username = apiKey;\n if (!url.pathname || url.pathname === '/') {\n url.pathname = '/ingest';\n }\n return url.toString();\n}\n","import type { Connection, Finality, TransactionSignature, VersionedTransactionResponse } from '@solana/web3.js';\nimport { trace, context, SpanStatusCode, type Tracer } from '@opentelemetry/api';\nimport { parseLogs, IdlResolver, enrichTree, flatAttributions } from '@thesight/core';\nimport type { AnchorIdl } from '@thesight/core';\n\n// ─── Options ──────────────────────────────────────────────────────────────\n\nexport interface TrackTransactionOptions {\n /** The signature returned from a prior `sendRawTransaction` / `sendTransaction` call. */\n signature: TransactionSignature;\n\n /**\n * Any `@solana/web3.js` Connection — plain or instrumented. Used to fetch\n * the on-chain transaction via `getTransaction` for enrichment. Does\n * **not** need to be an `InstrumentedConnection`; this helper intentionally\n * never touches the transaction-send path.\n */\n connection: Connection;\n\n /**\n * Service name used for the span. Falls back to the service name\n * configured by `initSight`. Useful when you want a different service\n * name for a specific subsystem's spans.\n */\n serviceName?: string;\n\n /** OTel tracer override. Defaults to `trace.getTracer('@thesight/sdk')`. */\n tracer?: Tracer;\n\n /** Optional IDL resolver for program/error decoding. A fresh one is built if omitted. */\n idlResolver?: IdlResolver;\n\n /**\n * Pre-registered IDLs to hand to a freshly-built resolver. Ignored when\n * `idlResolver` is provided.\n */\n idls?: Record<string, AnchorIdl>;\n\n /** Finality commitment used for the enrichment fetch. Default 'confirmed'. */\n commitment?: Finality;\n\n /** Max wall-time before the span is ended with status=timeout. Default 30000ms. */\n timeoutMs?: number;\n\n /** Base poll interval for retrying getTransaction. Default 500ms. */\n pollIntervalMs?: number;\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────\n\n/**\n * **Non-wrapping observation helper.** Given a transaction signature that\n * was already sent through any `Connection`, fetch the on-chain record,\n * parse the logs, and emit a single OpenTelemetry span describing the\n * transaction.\n *\n * Unlike `InstrumentedConnection`, this helper **never touches the send\n * path** — it only observes after the fact via `getTransaction(signature)`.\n * Use this when:\n *\n * - You want observability for wallet-adapter flows where the send path\n * goes through the wallet and you can't easily override the Connection\n * - You're security-conscious and don't want any SDK code on the critical\n * transaction path\n * - You want to instrument a handful of high-value transactions rather\n * than every call a Connection makes\n *\n * Usage:\n *\n * import { trackSolanaTransaction } from '@thesight/sdk';\n * import { Connection } from '@solana/web3.js';\n *\n * const connection = new Connection(rpcUrl);\n * const signature = await wallet.sendTransaction(tx, connection);\n *\n * // Fire-and-forget. Span flushes on its own.\n * void trackSolanaTransaction({\n * signature,\n * connection,\n * serviceName: 'checkout',\n * idls: { [programId]: idl },\n * });\n *\n * Returns a `Promise<void>` — typically not awaited, but callers who want\n * to wait for the span to export (e.g. short-lived scripts) can await it.\n */\nexport async function trackSolanaTransaction(opts: TrackTransactionOptions): Promise<void> {\n const tracerName = opts.serviceName ?? '@thesight/sdk';\n const tracer = opts.tracer ?? trace.getTracer(tracerName);\n\n const span = tracer.startSpan('solana.trackTransaction', {}, context.active());\n span.setAttribute('solana.tx.signature', opts.signature);\n\n const start = Date.now();\n const deadline = start + (opts.timeoutMs ?? 30_000);\n const commitment = opts.commitment ?? ('confirmed' as Finality);\n const basePoll = opts.pollIntervalMs ?? 500;\n\n const resolver = opts.idlResolver ?? buildResolverFromIdls(opts.idls);\n\n try {\n const txDetails = await pollGetTransaction(opts.connection, opts.signature, commitment, deadline, basePoll);\n if (!txDetails) {\n span.setAttribute('solana.tx.status', 'timeout');\n span.setAttribute('solana.tx.enrichment_ms', Date.now() - start);\n return;\n }\n\n span.setAttribute('solana.tx.status', txDetails.meta?.err ? 'failed' : 'confirmed');\n span.setAttribute('solana.tx.slot', txDetails.slot);\n\n const fee = txDetails.meta?.fee;\n if (fee !== undefined) span.setAttribute('solana.tx.fee_lamports', fee);\n\n const cuUsed = txDetails.meta?.computeUnitsConsumed;\n if (cuUsed !== undefined && cuUsed !== null) {\n span.setAttribute('solana.tx.cu_used', Number(cuUsed));\n span.setAttribute('solana.tx.cu_budget', 200_000);\n span.setAttribute(\n 'solana.tx.cu_utilization',\n parseFloat((Number(cuUsed) / 200_000 * 100).toFixed(1)),\n );\n }\n\n const logs = txDetails.meta?.logMessages ?? [];\n if (logs.length > 0) {\n const { cpiTree } = parseLogs({ logs });\n await enrichTree(cpiTree, resolver);\n\n for (const attr of flatAttributions(cpiTree)) {\n span.addEvent('cpi.invoke', {\n 'cpi.program': attr.programName ?? attr.programId,\n 'cpi.instruction': attr.instructionName ?? 'unknown',\n 'cpi.depth': attr.depth,\n 'cpi.cu_consumed': attr.cuConsumed,\n 'cpi.cu_self': attr.cuSelf,\n 'cpi.percentage': parseFloat(attr.percentage.toFixed(2)),\n });\n }\n\n const root = cpiTree.roots[0];\n if (root) {\n if (root.programName) span.setAttribute('solana.tx.program', root.programName);\n if (root.instructionName) span.setAttribute('solana.tx.instruction', root.instructionName);\n }\n }\n\n span.setAttribute('solana.tx.enrichment_ms', Date.now() - start);\n\n if (txDetails.meta?.err) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n } catch (err) {\n span.recordException(err instanceof Error ? err : new Error(String(err)));\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: err instanceof Error ? err.message : String(err),\n });\n } finally {\n span.end();\n }\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────\n\nfunction buildResolverFromIdls(idls?: Record<string, AnchorIdl>): IdlResolver {\n const resolver = new IdlResolver();\n if (idls) resolver.registerMany(idls);\n return resolver;\n}\n\nasync function pollGetTransaction(\n connection: Connection,\n signature: TransactionSignature,\n commitment: Finality,\n deadline: number,\n basePollMs: number,\n): Promise<VersionedTransactionResponse | null> {\n let attempt = 0;\n while (Date.now() < deadline) {\n try {\n const tx = await connection.getTransaction(signature, {\n commitment,\n maxSupportedTransactionVersion: 0,\n });\n if (tx) return tx;\n } catch {\n // Ignore — retry with backoff\n }\n attempt++;\n const waitMs = Math.min(basePollMs * Math.pow(1.5, attempt - 1), 2_000);\n await new Promise((resolve) => setTimeout(resolve, waitMs));\n }\n return null;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAQO;AACP,IAAAA,cAMO;AACP,IAAAC,eAAqE;AA0XrE,IAAAA,eAA4B;;;AC1Y5B,iBAAmC;;;ACEnC,kBAIO;;;ACyBA,SAAS,SAAS,KAAwB;AAC/C,MAAI;AACJ,MAAI;AACF,UAAM,IAAI,IAAI,GAAG;AAAA,EACnB,QAAQ;AACN,UAAM,IAAI;AAAA,MACR,uBAAuB,GAAG;AAAA,IAG5B;AAAA,EACF;AAEA,QAAM,SAAS,mBAAmB,IAAI,QAAQ;AAC9C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR,2CAA2C,GAAG;AAAA,IAEhD;AAAA,EACF;AAGA,MAAI,WAAW;AACf,MAAI,WAAW;AACf,QAAM,YAAY,IAAI,SAAS,EAAE,QAAQ,OAAO,EAAE;AAElD,SAAO,EAAE,QAAQ,UAAU;AAC7B;AAMO,SAAS,SAAS,QAAgB,YAA4B;AACnE,QAAM,MAAM,IAAI,IAAI,UAAU;AAC9B,MAAI,WAAW;AACf,MAAI,CAAC,IAAI,YAAY,IAAI,aAAa,KAAK;AACzC,QAAI,WAAW;AAAA,EACjB;AACA,SAAO,IAAI,SAAS;AACtB;;;AD/BO,IAAM,oBAAN,MAAgD;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACT,eAAe;AAAA,EAEvB,YAAYC,SAA6B;AACvC,UAAM,SAAW,SAASA,QAAO,GAAG;AACpC,SAAK,SAAY,OAAO;AACxB,SAAK,YAAY,OAAO;AACxB,SAAK,YAAYA,QAAO,aAAc,WAAW;AACjD,SAAK,eAAe,KAAK,IAAI,KAAKA,QAAO,gBAAgB,GAAG;AAE5D,QAAI,CAAC,KAAK,WAAW;AACnB,YAAM,IAAI;AAAA,QACR;AAAA,MAEF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,OACJ,OACA,gBACe;AACf,QAAI,KAAK,cAAc;AACrB,qBAAe,EAAE,MAAM,6BAAiB,QAAQ,OAAO,IAAI,MAAM,uBAAuB,EAAE,CAAC;AAC3F;AAAA,IACF;AAKA,UAAM,SAA2B,CAAC;AAClC,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,KAAK,cAAc;AACxD,aAAO,KAAK,MAAM,MAAM,GAAG,IAAI,KAAK,YAAY,CAAC;AAAA,IACnD;AAEA,QAAI;AACF,iBAAW,SAAS,QAAQ;AAC1B,cAAM,KAAK,UAAU,KAAK;AAAA,MAC5B;AACA,qBAAe,EAAE,MAAM,6BAAiB,QAAQ,CAAC;AAAA,IACnD,SAAS,KAAK;AACZ,qBAAe;AAAA,QACb,MAAO,6BAAiB;AAAA,QACxB,OAAO,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAAA,MAC3D,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAM,WAA0B;AAC9B,SAAK,eAAe;AAAA,EACtB;AAAA,EAEA,MAAM,aAA4B;AAAA,EAGlC;AAAA;AAAA,EAIA,MAAc,UAAU,OAAsC;AAC5D,UAAM,UAAU;AAAA,MACd,OAAO,MAAM,IAAI,CAAC,SAAS,KAAK,YAAY,IAAI,CAAC;AAAA,IACnD;AAEA,UAAM,WAAW,MAAM,KAAK,UAAU,KAAK,WAAW;AAAA,MACpD,QAAS;AAAA,MACT,SAAS;AAAA,QACP,gBAAiB;AAAA,QACjB,iBAAiB,UAAU,KAAK,MAAM;AAAA,MACxC;AAAA,MACA,MAAM,KAAK,UAAU,OAAO;AAAA,IAC9B,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,aAAa,QAAQ;AACxC,YAAM,IAAI;AAAA,QACR,yBAAyB,SAAS,MAAM,IAAI,SAAS,UAAU,KAAK,IAAI;AAAA,MAC1E;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,YAAY,MAAgC;AAClD,UAAM,OAAe,KAAK;AAC1B,UAAMC,YAAe,KAAK,SAAS;AACnC,UAAM,cAAe,OAAOA,UAAS,cAAc,MAAM,WACrDA,UAAS,cAAc,IACvB;AACJ,UAAM,kBAAe,kCAAqB,KAAK,SAAS;AACxD,UAAM,gBAAe,kCAAqB,KAAK,OAAO;AACtD,UAAM,aAAe,KAAK,IAAI,GAAG,YAAY,WAAW;AAExD,UAAM,MAAkB;AAAA,MACtB,SAAa,KAAK,YAAY,EAAE;AAAA,MAChC,QAAa,KAAK,YAAY,EAAE;AAAA,MAChC,UAAa,KAAK;AAAA,MAClB;AAAA,MACA;AAAA,IACF;AAEA,UAAM,eAAgB,KAAmC;AACzD,QAAI,aAAc,KAAI,eAAe;AACrC,QAAI,YAAc,KAAI,cAAe;AAErC,iBAAa,MAAM,uBAAyB,GAAG;AAC/C,eAAW,MAAQ,oBAAyB,KAAK,CAAC,aAAa,aAAa,UAAU,SAAS,CAAC;AAChG,iBAAa,MAAM,kBAAyB,GAAG;AAC/C,iBAAa,MAAM,qBAAyB,GAAG;AAC/C,iBAAa,MAAM,uBAAyB,GAAG;AAC/C,iBAAa,MAAM,4BAA4B,GAAG;AAClD,iBAAa,MAAM,qBAAyB,GAAG;AAC/C,iBAAa,MAAM,yBAAyB,GAAG;AAC/C,iBAAa,MAAM,mBAAyB,GAAG;AAC/C,iBAAa,MAAM,wBAAyB,GAAG;AAC/C,iBAAa,MAAM,2BAA2B,GAAG;AACjD,iBAAa,MAAM,uBAAyB,GAAG;AAC/C,iBAAa,MAAM,6BAA6B,GAAG;AACnD,iBAAa,MAAM,0BAA0B,GAAG;AAEhD,WAAO;AAAA,EACT;AACF;AA+BA,SAAS,aACP,MACA,KACA,KACM;AACN,QAAM,IAAI,KAAK,GAAG;AAClB,MAAI,OAAO,MAAM,UAAU;AACzB,IAAC,IAA2C,GAAG,IAAI;AAAA,EACrD;AACF;AAEA,SAAS,aACP,MACA,KACA,KACM;AACN,QAAM,IAAI,KAAK,GAAG;AAClB,MAAI,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC,GAAG;AAC/C,IAAC,IAA2C,GAAG,IAAI;AAAA,EACrD;AACF;AAEA,SAAS,WACP,MACA,KACA,KACA,SACM;AACN,QAAM,IAAI,KAAK,GAAG;AAClB,MAAI,OAAO,MAAM,YAAa,QAA8B,SAAS,CAAC,GAAG;AACvE,IAAC,IAA2C,GAAG,IAAI;AAAA,EACrD;AACF;AAEA,eAAe,aAAa,KAAgC;AAC1D,MAAI;AACF,YAAQ,MAAM,IAAI,KAAK,GAAG,MAAM,GAAG,GAAG;AAAA,EACxC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ADpIO,SAAS,UAAU,QAA4C;AACpE,MAAI,CAAC,OAAO,KAAK;AACf,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AACA,MAAI,CAAC,OAAO,aAAa;AACvB,UAAM,IAAI,MAAM,sCAAsC;AAAA,EACxD;AAEA,MAAI,kBAA2B;AAC/B,MAAI,kBAA2B;AAE/B,QAAM,WAAW,IAAI,kBAAkB;AAAA,IACrC,KAAc,OAAO;AAAA,IACrB,WAAc,OAAO;AAAA,IACrB,cAAc,OAAO;AAAA,EACvB,CAAC;AAID,MAAI;AAQF,UAAM,WAAW,KAAK,SAAS;AAC/B,UAAM,EAAE,mBAAmB,IAAI,SAAS,+BAA+B;AACvE,UAAM,EAAE,mBAAmB,IAAI,SAAS,+BAA+B;AACvE,UAAM,gBAAyB,SAAS,0BAA0B;AAElE,UAAM,YAAY,IAAI,mBAAmB,UAAU;AAAA,MACjD,sBAAsB,OAAO,gBAAgB;AAAA,MAC7C,oBAAsB,OAAO,gBAAgB;AAAA,MAC7C,cAAsB;AAAA,IACxB,CAAC;AAMD,UAAM,gBAAwC;AAAA,MAC5C,gBAAgB,OAAO;AAAA,MACvB,GAAI,OAAO,iBAAiB,EAAE,mBAAmB,OAAO,eAAe,IAAI,CAAC;AAAA,IAC9E;AACA,UAAM,WAAW,OAAO,cAAc,2BAA2B,aAC7D,cAAc,uBAAuB,aAAa,IAClD,IAAI,cAAc,SAAS,aAAa;AAK5C,QAAI;AACJ,QAAI,OAAO,mBAAmB,UAAU,qBAAqB,YAAY;AAEvE,iBAAW,IAAI,mBAAmB,EAAE,SAAS,CAAC;AAC9C,eAAS,iBAAiB,SAAS;AAAA,IACrC,OAAO;AAEL,iBAAW,IAAI,mBAAmB,EAAE,UAAU,gBAAgB,CAAC,SAAS,EAAE,CAAC;AAAA,IAC7E;AAEA,QAAI;AACF,eAAS,SAAS;AAAA,IACpB,QAAQ;AAAA,IAGR;AAEA,WAAO;AAAA,MACL,QAAQ,SAAS,UAAU,eAAe;AAAA,MAC1C,MAAQ;AAAA,MACR,UAAU,YAAY;AAAE,cAAM,SAAS,SAAS;AAAA,MAAG;AAAA,IACrD;AAAA,EACF,SAAS,UAAU;AACjB,sBAAkB;AAAA,EACpB;AAeA,MAAI;AACF,UAAM,QAAW,iBAAM,kBAAkB;AACzC,UAAM,WAAW,OAAO,cAAc,KAAK;AAE3C,QAAI,YAAY,OAAO,SAAS,qBAAqB,YAAY;AAC/D,YAAMC,aAAY,IAAI;AAAA,QACpB;AAAA,QACA,OAAO,gBAAgB;AAAA,QACvB,OAAO,gBAAgB;AAAA,MACzB;AACA,eAAS,iBAAiBA,UAAS;AACnC,aAAO;AAAA,QACL,QAAQ,iBAAM,UAAU,eAAe;AAAA,QACvC,MAAQ;AAAA,QACR,UAAU,YAAY;AAAE,gBAAMA,WAAU,SAAS;AAAA,QAAG;AAAA,MACtD;AAAA,IACF;AAAA,EACF,SAAS,UAAU;AACjB,sBAAkB;AAAA,EACpB;AAIA,MAAI,OAAO,YAAY,aAAa;AAClC,YAAQ;AAAA,MACN;AAAA,IAEF;AACA,QAAI,iBAAiB;AACnB,cAAQ,KAAK,yBAAyB,2BAA2B,QAAQ,gBAAgB,UAAU,OAAO,eAAe,CAAC;AAAA,IAC5H;AACA,QAAI,iBAAiB;AACnB,cAAQ,KAAK,yBAAyB,2BAA2B,QAAQ,gBAAgB,UAAU,OAAO,eAAe,CAAC;AAAA,IAC5H;AAAA,EACF;AAEA,SAAO;AAAA,IACL,QAAQ,iBAAM,UAAU,eAAe;AAAA,IACvC,MAAQ;AAAA,IACR,UAAU,YAAY;AAAA,IAAC;AAAA,EACzB;AACF;AAgBA,IAAM,uBAAN,MAA2B;AAAA,EAKzB,YACmBC,WACjB,SACA,UACA;AAHiB,oBAAAA;AAIjB,SAAK,WAAW;AAChB,SAAK,QAAQ,YAAY,MAAM,KAAK,MAAM,GAAG,OAAO;AAIpD,QAAI,KAAK,SAAS,OAAQ,KAAK,MAAc,UAAU,YAAY;AACjE,MAAC,KAAK,MAAc,MAAM;AAAA,IAC5B;AAAA,EACF;AAAA,EAZmB;AAAA,EALX,SAAoB,CAAC;AAAA,EACrB,QAA+C;AAAA,EACtC;AAAA;AAAA,EAkBjB,UAAgB;AAAA,EAAC;AAAA;AAAA,EAGjB,MAAM,MAAqB;AACzB,SAAK,OAAO,KAAK,IAAI;AACrB,QAAI,KAAK,OAAO,UAAU,KAAK,UAAU;AACvC,WAAK,MAAM;AAAA,IACb;AAAA,EACF;AAAA;AAAA,EAGQ,QAAc;AACpB,QAAI,KAAK,OAAO,WAAW,EAAG;AAC9B,UAAM,QAAQ,KAAK,OAAO,OAAO,GAAG,KAAK,QAAQ;AAKjD,SAAK,SAAS,OAAO,OAAc,MAAM;AAAA,IAAC,CAAC;AAAA,EAC7C;AAAA,EAEA,MAAM,WAA0B;AAC9B,QAAI,KAAK,OAAO;AACd,oBAAc,KAAK,KAAK;AACxB,WAAK,QAAQ;AAAA,IACf;AACA,SAAK,MAAM;AACX,UAAM,KAAK,SAAS,SAAS;AAAA,EAC/B;AAAA,EAEA,MAAM,aAA4B;AAChC,SAAK,MAAM;AAAA,EACb;AACF;;;AGnTA,IAAAC,cAA4D;AAC5D,IAAAC,eAAqE;AAoFrE,eAAsB,uBAAuB,MAA8C;AACzF,QAAM,aAAa,KAAK,eAAe;AACvC,QAAM,SAAa,KAAK,UAAU,kBAAM,UAAU,UAAU;AAE5D,QAAM,OAAO,OAAO,UAAU,2BAA2B,CAAC,GAAG,oBAAQ,OAAO,CAAC;AAC7E,OAAK,aAAa,uBAAuB,KAAK,SAAS;AAEvD,QAAM,QAAa,KAAK,IAAI;AAC5B,QAAM,WAAa,SAAS,KAAK,aAAa;AAC9C,QAAM,aAAa,KAAK,cAAe;AACvC,QAAM,WAAa,KAAK,kBAAkB;AAE1C,QAAM,WAAW,KAAK,eAAe,sBAAsB,KAAK,IAAI;AAEpE,MAAI;AACF,UAAM,YAAY,MAAM,mBAAmB,KAAK,YAAY,KAAK,WAAW,YAAY,UAAU,QAAQ;AAC1G,QAAI,CAAC,WAAW;AACd,WAAK,aAAa,oBAAoB,SAAS;AAC/C,WAAK,aAAa,2BAA2B,KAAK,IAAI,IAAI,KAAK;AAC/D;AAAA,IACF;AAEA,SAAK,aAAa,oBAAoB,UAAU,MAAM,MAAM,WAAW,WAAW;AAClF,SAAK,aAAa,kBAAkB,UAAU,IAAI;AAElD,UAAM,MAAM,UAAU,MAAM;AAC5B,QAAI,QAAQ,OAAW,MAAK,aAAa,0BAA0B,GAAG;AAEtE,UAAM,SAAS,UAAU,MAAM;AAC/B,QAAI,WAAW,UAAa,WAAW,MAAM;AAC3C,WAAK,aAAa,qBAA6B,OAAO,MAAM,CAAC;AAC7D,WAAK,aAAa,uBAA6B,GAAO;AACtD,WAAK;AAAA,QACH;AAAA,QACA,YAAY,OAAO,MAAM,IAAI,MAAU,KAAK,QAAQ,CAAC,CAAC;AAAA,MACxD;AAAA,IACF;AAEA,UAAM,OAAO,UAAU,MAAM,eAAe,CAAC;AAC7C,QAAI,KAAK,SAAS,GAAG;AACnB,YAAM,EAAE,QAAQ,QAAI,wBAAU,EAAE,KAAK,CAAC;AACtC,gBAAM,yBAAW,SAAS,QAAQ;AAElC,iBAAW,YAAQ,+BAAiB,OAAO,GAAG;AAC5C,aAAK,SAAS,cAAc;AAAA,UAC1B,eAAmB,KAAK,eAAe,KAAK;AAAA,UAC5C,mBAAmB,KAAK,mBAAmB;AAAA,UAC3C,aAAmB,KAAK;AAAA,UACxB,mBAAmB,KAAK;AAAA,UACxB,eAAmB,KAAK;AAAA,UACxB,kBAAmB,WAAW,KAAK,WAAW,QAAQ,CAAC,CAAC;AAAA,QAC1D,CAAC;AAAA,MACH;AAEA,YAAM,OAAO,QAAQ,MAAM,CAAC;AAC5B,UAAI,MAAM;AACR,YAAI,KAAK,YAAiB,MAAK,aAAa,qBAAyB,KAAK,WAAW;AACrF,YAAI,KAAK,gBAAiB,MAAK,aAAa,yBAAyB,KAAK,eAAe;AAAA,MAC3F;AAAA,IACF;AAEA,SAAK,aAAa,2BAA2B,KAAK,IAAI,IAAI,KAAK;AAE/D,QAAI,UAAU,MAAM,KAAK;AACvB,WAAK,UAAU,EAAE,MAAM,2BAAe,MAAM,CAAC;AAAA,IAC/C,OAAO;AACL,WAAK,UAAU,EAAE,MAAM,2BAAe,GAAG,CAAC;AAAA,IAC5C;AAAA,EACF,SAAS,KAAK;AACZ,SAAK,gBAAgB,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AACxE,SAAK,UAAU;AAAA,MACb,MAAS,2BAAe;AAAA,MACxB,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,IAC1D,CAAC;AAAA,EACH,UAAE;AACA,SAAK,IAAI;AAAA,EACX;AACF;AAIA,SAAS,sBAAsB,MAA+C;AAC5E,QAAM,WAAW,IAAI,yBAAY;AACjC,MAAI,KAAM,UAAS,aAAa,IAAI;AACpC,SAAO;AACT;AAEA,eAAe,mBACb,YACA,WACA,YACA,UACA,YAC8C;AAC9C,MAAI,UAAU;AACd,SAAO,KAAK,IAAI,IAAI,UAAU;AAC5B,QAAI;AACF,YAAM,KAAK,MAAM,WAAW,eAAe,WAAW;AAAA,QACpD;AAAA,QACA,gCAAgC;AAAA,MAClC,CAAC;AACD,UAAI,GAAI,QAAO;AAAA,IACjB,QAAQ;AAAA,IAER;AACA;AACA,UAAM,SAAS,KAAK,IAAI,aAAa,KAAK,IAAI,KAAK,UAAU,CAAC,GAAG,GAAK;AACtE,UAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,MAAM,CAAC;AAAA,EAC5D;AACA,SAAO;AACT;;;AJ1DO,IAAM,yBAAN,cAAqC,uBAAW;AAAA,EAC7C;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,UAAkBC,SAAyC;AACrE,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAG;AAAA,IACL,IAAIA,WAAU,CAAC;AAEf,UAAM,UAAU,gBAAgB;AAEhC,SAAK,cAAc;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,qBAAyB,uBAA2B;AAAA,MACpD,0BAA0B,4BAA4B;AAAA,MACtD;AAAA,MACA;AAAA,IACF;AACA,SAAK,cAAc,IAAI,yBAAY;AAAA,MACjC,aAAmB,kBAAkB;AAAA,MACrC,mBAAmB,wBAAwB;AAAA,IAC7C,CAAC;AACD,QAAI,KAAM,MAAK,YAAY,aAAa,IAAI;AAC5C,SAAK,SAAS,UAAU,kBAAM,UAAU,eAAe;AAAA,EACzD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,YAAY,WAAmB,KAAsB;AACnD,SAAK,YAAY,SAAS,WAAW,GAAG;AAAA,EAC1C;AAAA;AAAA,EAGA,aAAa,MAAuC;AAClD,SAAK,YAAY,aAAa,IAAI;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAe,mBACb,gBACA,SAC+B;AAC/B,QAAI,KAAK,YAAY,iBAAiB;AACpC,aAAO,MAAM,mBAAmB,gBAAgB,OAAO;AAAA,IACzD;AAEA,UAAM,OAAO,KAAK,OAAO,UAAU,6BAA6B,CAAC,GAAG,oBAAQ,OAAO,CAAC;AACpF,UAAM,cAAc,KAAK,IAAI;AAE7B,QAAI;AACJ,QAAI;AACF,kBAAY,MAAM,MAAM,mBAAmB,gBAAgB,OAAO;AAAA,IACpE,SAAS,KAAK;AACZ,YAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,WAAK,aAAa,uBAAuB,QAAQ;AACjD,WAAK,aAAa,oBAAoB,QAAQ;AAC9C,WAAK,gBAAgB,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AACxE,WAAK,UAAU;AAAA,QACb,MAAS,2BAAe;AAAA,QACxB,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MAC1D,CAAC;AACD,WAAK,IAAI;AACT,YAAM;AAAA,IACR;AAEA,SAAK,aAAa,uBAAuB,SAAS;AAClD,SAAK,aAAa,uBAAuB,KAAK,IAAI,IAAI,WAAW;AACjE,SAAK,aAAa,oBAAuB,WAAW;AAOpD,SAAK,KAAK,uBAAuB,MAAM,WAAW,WAAW;AAE7D,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAc,uBACZ,MACA,WACA,aACe;AACf,UAAM,cAAc,KAAK,IAAI;AAC7B,UAAM,WAAc,eAAe,KAAK,YAAY,uBAAuB;AAC3E,UAAM,aAAe,KAAK,YAAY,cAAc;AACpD,UAAM,aAAc,KAAK,YAAY,4BAA4B;AAEjE,QAAI;AACF,YAAM,YAAY,MAAM,KAAK,mBAAmB,WAAW,YAAY,UAAU,UAAU;AAE3F,UAAI,CAAC,WAAW;AACd,aAAK,aAAa,oBAAoB,SAAS;AAC/C,aAAK,aAAa,2BAA2B,KAAK,IAAI,IAAI,WAAW;AACrE,aAAK,IAAI;AACT;AAAA,MACF;AAEA,WAAK,sBAAsB,MAAM,SAAS;AAE1C,UAAI,CAAC,KAAK,YAAY,mBAAmB;AACvC,cAAM,OAAO,UAAU,MAAM,eAAe,CAAC;AAC7C,YAAI,KAAK,SAAS,GAAG;AACnB,gBAAM,KAAK,uBAAuB,MAAM,IAAI;AAAA,QAC9C;AAAA,MACF;AAEA,WAAK,aAAa,2BAA2B,KAAK,IAAI,IAAI,WAAW;AAErE,UAAI,UAAU,MAAM,KAAK;AACvB,aAAK,UAAU,EAAE,MAAM,2BAAe,MAAM,CAAC;AAAA,MAC/C,OAAO;AACL,aAAK,UAAU,EAAE,MAAM,2BAAe,GAAG,CAAC;AAAA,MAC5C;AAAA,IACF,SAAS,KAAK;AACZ,WAAK,gBAAgB,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AACxE,WAAK;AAAA,QACH;AAAA,QACA,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MACjD;AAAA,IACF,UAAE;AACA,WAAK,IAAI;AAAA,IACX;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,mBACZ,WACA,YACA,UACA,YAC8C;AAC9C,QAAI,UAAU;AACd,WAAO,KAAK,IAAI,IAAI,UAAU;AAC5B,UAAI;AACF,cAAM,KAAK,MAAM,MAAM,eAAe,WAAW;AAAA,UAC/C;AAAA,UACA,gCAAgC;AAAA,QAClC,CAAC;AACD,YAAI,GAAI,QAAO;AAAA,MACjB,QAAQ;AAAA,MAGR;AACA;AACA,YAAM,SAAS,KAAK,IAAI,aAAa,KAAK,IAAI,KAAK,UAAU,CAAC,GAAG,GAAK;AACtE,YAAM,MAAM,MAAM;AAAA,IACpB;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGQ,sBAAsB,MAAY,WAA+C;AACvF,SAAK,aAAa,oBAAoB,UAAU,MAAM,MAAM,WAAW,WAAW;AAClF,SAAK,aAAa,kBAAkB,UAAU,IAAI;AAElD,UAAM,MAAM,UAAU,MAAM;AAC5B,QAAI,QAAQ,OAAW,MAAK,aAAa,0BAA0B,GAAG;AAEtE,UAAM,SAAS,UAAU,MAAM;AAC/B,QAAI,WAAW,UAAa,WAAW,MAAM;AAC3C,WAAK,aAAa,qBAA6B,OAAO,MAAM,CAAC;AAC7D,WAAK,aAAa,uBAA6B,GAAO;AACtD,WAAK;AAAA,QACH;AAAA,QACA,YAAY,OAAO,MAAM,IAAI,MAAU,KAAK,QAAQ,CAAC,CAAC;AAAA,MACxD;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,uBAAuB,MAAY,MAA8C;AAC7F,UAAM,EAAE,QAAQ,QAAI,wBAAU,EAAE,KAAK,CAAC;AACtC,cAAM,yBAAW,SAAS,KAAK,WAAW;AAE1C,UAAM,mBAAe,+BAAiB,OAAO;AAC7C,eAAW,QAAQ,cAAc;AAC/B,WAAK,SAAS,cAAc;AAAA,QAC1B,eAAoB,KAAK,eAAe,KAAK;AAAA,QAC7C,mBAAoB,KAAK,mBAAmB;AAAA,QAC5C,aAAoB,KAAK;AAAA,QACzB,mBAAoB,KAAK;AAAA,QACzB,eAAoB,KAAK;AAAA,QACzB,kBAAoB,WAAW,KAAK,WAAW,QAAQ,CAAC,CAAC;AAAA,MAC3D,CAAC;AAAA,IACH;AAEA,UAAM,OAAO,QAAQ,MAAM,CAAC;AAC5B,QAAI,MAAM;AACR,UAAI,KAAK,YAAiB,MAAK,aAAa,qBAAyB,KAAK,WAAW;AACrF,UAAI,KAAK,gBAAiB,MAAK,aAAa,yBAAyB,KAAK,eAAe;AAAA,IAC3F;AAEA,WAAO;AAAA,EACT;AACF;AAIA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;","names":["import_api","import_core","config","resource","processor","exporter","import_api","import_core","config"]}
package/dist/index.js CHANGED
@@ -1,10 +1,3 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined") return require.apply(this, arguments);
5
- throw Error('Dynamic require of "' + x + '" is not supported');
6
- });
7
-
8
1
  // src/index.ts
9
2
  import {
10
3
  Connection
@@ -63,12 +56,12 @@ var SightSpanExporter = class {
63
56
  fetchImpl;
64
57
  maxBatchSize;
65
58
  shuttingDown = false;
66
- constructor(config) {
67
- const parsed = parseDsn(config.dsn);
59
+ constructor(config2) {
60
+ const parsed = parseDsn(config2.dsn);
68
61
  this.apiKey = parsed.apiKey;
69
62
  this.ingestUrl = parsed.ingestUrl;
70
- this.fetchImpl = config.fetchImpl ?? globalThis.fetch;
71
- this.maxBatchSize = Math.min(100, config.maxBatchSize ?? 100);
63
+ this.fetchImpl = config2.fetchImpl ?? globalThis.fetch;
64
+ this.maxBatchSize = Math.min(100, config2.maxBatchSize ?? 100);
72
65
  if (!this.fetchImpl) {
73
66
  throw new Error(
74
67
  "SightSpanExporter: globalThis.fetch is not available. Node >= 18 ships with fetch built in, or pass `fetchImpl` explicitly."
@@ -123,8 +116,8 @@ var SightSpanExporter = class {
123
116
  }
124
117
  convertSpan(span) {
125
118
  const attr = span.attributes;
126
- const resource = span.resource.attributes;
127
- const serviceName = typeof resource["service.name"] === "string" ? resource["service.name"] : void 0;
119
+ const resource2 = span.resource.attributes;
120
+ const serviceName = typeof resource2["service.name"] === "string" ? resource2["service.name"] : void 0;
128
121
  const startTimeMs = hrTimeToMilliseconds(span.startTime);
129
122
  const endTimeMs = hrTimeToMilliseconds(span.endTime);
130
123
  const durationMs = Math.max(0, endTimeMs - startTimeMs);
@@ -191,15 +184,18 @@ function initSight(config) {
191
184
  if (!config.serviceName) {
192
185
  throw new Error("initSight: `serviceName` is required");
193
186
  }
187
+ let _lastTier1Error = null;
188
+ let _lastTier2Error = null;
194
189
  const exporter = new SightSpanExporter({
195
190
  dsn: config.dsn,
196
191
  fetchImpl: config.fetchImpl,
197
192
  maxBatchSize: config.maxBatchSize
198
193
  });
199
194
  try {
200
- const { NodeTracerProvider } = __require("@opentelemetry/sdk-trace-node");
201
- const { BatchSpanProcessor } = __require("@opentelemetry/sdk-trace-base");
202
- const otelResources = __require("@opentelemetry/resources");
195
+ const _require = eval("require");
196
+ const { NodeTracerProvider } = _require("@opentelemetry/sdk-trace-node");
197
+ const { BatchSpanProcessor } = _require("@opentelemetry/sdk-trace-base");
198
+ const otelResources = _require("@opentelemetry/resources");
203
199
  const processor = new BatchSpanProcessor(exporter, {
204
200
  scheduledDelayMillis: config.batchDelayMs ?? 5e3,
205
201
  maxExportBatchSize: config.maxBatchSize ?? 100,
@@ -228,32 +224,40 @@ function initSight(config) {
228
224
  await provider.shutdown();
229
225
  }
230
226
  };
231
- } catch {
227
+ } catch (tier1Err) {
228
+ _lastTier1Error = tier1Err;
232
229
  }
233
230
  try {
234
231
  const proxy = trace.getTracerProvider();
235
232
  const delegate = proxy?.getDelegate?.() ?? proxy;
236
233
  if (delegate && typeof delegate.addSpanProcessor === "function") {
237
- const processor = new SightInlineProcessor(
234
+ const processor2 = new SightInlineProcessor(
238
235
  exporter,
239
236
  config.batchDelayMs ?? 5e3,
240
237
  config.maxBatchSize ?? 100
241
238
  );
242
- delegate.addSpanProcessor(processor);
239
+ delegate.addSpanProcessor(processor2);
243
240
  return {
244
241
  tracer: trace.getTracer("@thesight/sdk"),
245
242
  mode: "piggyback",
246
243
  shutdown: async () => {
247
- await processor.shutdown();
244
+ await processor2.shutdown();
248
245
  }
249
246
  };
250
247
  }
251
- } catch {
248
+ } catch (tier2Err) {
249
+ _lastTier2Error = tier2Err;
252
250
  }
253
251
  if (typeof console !== "undefined") {
254
252
  console.warn(
255
- "[sight] Could not create an OTel tracer provider or attach to the existing one (likely an @opentelemetry version conflict with @sentry/node). Sight spans may not reach ingest. Consider aligning OTel versions via pnpm.overrides or npm overrides."
253
+ "[sight] Could not create an OTel tracer provider (tier 1) or attach to the existing one (tier 2). Sight spans may not reach ingest."
256
254
  );
255
+ if (_lastTier1Error) {
256
+ console.warn("[sight] Tier 1 error:", _lastTier1Error instanceof Error ? _lastTier1Error.message : String(_lastTier1Error));
257
+ }
258
+ if (_lastTier2Error) {
259
+ console.warn("[sight] Tier 2 error:", _lastTier2Error instanceof Error ? _lastTier2Error.message : String(_lastTier2Error));
260
+ }
257
261
  }
258
262
  return {
259
263
  tracer: trace.getTracer("@thesight/sdk"),
@@ -263,8 +267,8 @@ function initSight(config) {
263
267
  };
264
268
  }
265
269
  var SightInlineProcessor = class {
266
- constructor(exporter, delayMs, maxBatch) {
267
- this.exporter = exporter;
270
+ constructor(exporter2, delayMs, maxBatch) {
271
+ this.exporter = exporter2;
268
272
  this.maxBatch = maxBatch;
269
273
  this.timer = setInterval(() => this.flush(), delayMs);
270
274
  if (this.timer && typeof this.timer.unref === "function") {
@@ -402,7 +406,7 @@ var InstrumentedConnection = class extends Connection {
402
406
  sightConfig;
403
407
  idlResolver;
404
408
  tracer;
405
- constructor(endpoint, config) {
409
+ constructor(endpoint, config2) {
406
410
  const {
407
411
  tracer,
408
412
  idlRpcEndpoint,
@@ -414,7 +418,7 @@ var InstrumentedConnection = class extends Connection {
414
418
  disableAutoSpan,
415
419
  commitment,
416
420
  ...connectionConfig
417
- } = config ?? {};
421
+ } = config2 ?? {};
418
422
  super(endpoint, connectionConfig);
419
423
  this.sightConfig = {
420
424
  tracer,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/init.ts","../src/exporter.ts","../src/dsn.ts","../src/track.ts"],"sourcesContent":["import {\n Connection,\n type ConnectionConfig,\n type SendOptions,\n type Commitment,\n type Finality,\n type TransactionSignature,\n type VersionedTransactionResponse,\n} from '@solana/web3.js';\nimport {\n trace,\n context,\n SpanStatusCode,\n type Span,\n type Tracer,\n} from '@opentelemetry/api';\nimport { parseLogs, IdlResolver, enrichTree, flatAttributions } from '@thesight/core';\nimport type { AnchorIdl, CpiTree } from '@thesight/core';\n\n// ─── Config ────────────────────────────────────────────────────────────────\n\nexport interface SightConfig {\n /** OTel tracer. Defaults to `trace.getTracer('@thesight/sdk')`. */\n tracer?: Tracer;\n\n /** Override RPC endpoint for IDL fetching (defaults to same as connection) */\n idlRpcEndpoint?: string;\n\n /**\n * Commitment used when fetching confirmed tx details for enrichment.\n * Defaults to 'confirmed'.\n */\n commitment?: Commitment;\n\n /**\n * Skip IDL resolution entirely. Spans will still emit with signature and\n * timing attributes, but program names, instruction names, CPI trees, and\n * decoded errors will all be omitted. Useful when you want minimal\n * overhead and don't care about the richer observability.\n */\n skipIdlResolution?: boolean;\n\n /**\n * Pre-register IDLs at construction time. Keys are program IDs, values are\n * full Anchor IDL objects. Anchor users can pass `program.idl` directly off\n * their `Program` instance — zero setup friction.\n */\n idls?: Record<string, AnchorIdl>;\n\n /**\n * Opt into reading Anchor IDL accounts from the on-chain PDA as a fallback\n * when a program is not explicitly registered. Defaults to **false** —\n * Sight's model is that IDLs are explicitly provided by the application,\n * not silently guessed from chain.\n */\n allowOnChainIdlFetch?: boolean;\n\n /**\n * Max wall-time the background enrichment task will wait for a\n * transaction to show up in `getTransaction`. After this expires the\n * span is ended with `solana.tx.status = 'timeout'`. Default 30000 (30s).\n */\n enrichmentTimeoutMs?: number;\n\n /**\n * How often the enrichment task polls `getTransaction`. Each retry backs\n * off by 1.5x up to a cap. Default 500ms base.\n */\n enrichmentPollIntervalMs?: number;\n\n /**\n * Disable automatic span creation in the overridden `sendRawTransaction`.\n * When true, InstrumentedConnection behaves like a plain Connection. You\n * probably don't want this — it's here as an escape hatch for tests and\n * rare cases where you need the class hierarchy but not the tracing.\n */\n disableAutoSpan?: boolean;\n}\n\n// ─── Span attribute shape (documented, for downstream consumers) ──────────\n\nexport interface SightSpanAttributes {\n 'solana.tx.signature': string;\n 'solana.tx.status': 'submitted' | 'confirmed' | 'failed' | 'timeout';\n 'solana.tx.slot'?: number;\n 'solana.tx.fee_lamports'?: number;\n 'solana.tx.submit_ms': number;\n 'solana.tx.enrichment_ms'?: number;\n 'solana.tx.cu_used'?: number;\n 'solana.tx.cu_budget'?: number;\n 'solana.tx.cu_utilization'?: number;\n 'solana.tx.program'?: string;\n 'solana.tx.instruction'?: string;\n 'solana.tx.error'?: string;\n 'solana.tx.error_code'?: number;\n 'solana.tx.error_program'?: string;\n 'solana.tx.error_msg'?: string;\n}\n\n// ─── InstrumentedConnection ────────────────────────────────────────────────\n\n/**\n * Drop-in replacement for `@solana/web3.js`'s `Connection` that emits an\n * OpenTelemetry span for **every** call to `sendRawTransaction` —\n * regardless of whether the caller is your own code, an Anchor provider's\n * `sendAndConfirm`, `@solana/web3.js`'s top-level `sendAndConfirmTransaction`,\n * a wallet adapter, or anything else.\n *\n * Internally it overrides the parent class's `sendRawTransaction`, so the\n * span covers the same surface web3.js already mediates. No mutation of\n * the transaction bytes — we call `super.sendRawTransaction(rawTx, opts)`\n * verbatim and observe the signature it returns.\n *\n * After the signature is returned (synchronously from the caller's point\n * of view), a background task polls `getTransaction` until the on-chain\n * record is available, parses the program logs via `@thesight/core`, and\n * enriches the span with CU attribution, per-CPI events, program + instruction\n * names, and decoded error details. The background task never blocks the\n * caller — if it fails or times out, the span ends with status=timeout and\n * whatever partial data was collected.\n *\n * Usage:\n *\n * import { initSight, InstrumentedConnection } from '@thesight/sdk';\n *\n * initSight({ dsn: process.env.SIGHT_DSN!, serviceName: 'swap-bot' });\n *\n * // Anywhere you currently construct a Connection, construct this instead:\n * const connection = new InstrumentedConnection(rpcUrl, {\n * commitment: 'confirmed',\n * });\n *\n * // All of the following now emit spans automatically:\n * await connection.sendRawTransaction(tx.serialize());\n * await sendAndConfirmTransaction(connection, tx, signers); // web3.js\n * await program.methods.foo().rpc(); // Anchor\n * await wallet.sendTransaction(tx, connection); // wallet adapter\n */\nexport class InstrumentedConnection extends Connection {\n private sightConfig: SightConfig;\n private idlResolver: IdlResolver;\n private tracer: Tracer;\n\n constructor(endpoint: string, config?: ConnectionConfig & SightConfig) {\n const {\n tracer,\n idlRpcEndpoint,\n skipIdlResolution,\n idls,\n allowOnChainIdlFetch,\n enrichmentTimeoutMs,\n enrichmentPollIntervalMs,\n disableAutoSpan,\n commitment,\n ...connectionConfig\n } = config ?? {};\n\n super(endpoint, connectionConfig);\n\n this.sightConfig = {\n tracer,\n idlRpcEndpoint,\n skipIdlResolution,\n allowOnChainIdlFetch,\n enrichmentTimeoutMs: enrichmentTimeoutMs ?? 30_000,\n enrichmentPollIntervalMs: enrichmentPollIntervalMs ?? 500,\n disableAutoSpan,\n commitment,\n };\n this.idlResolver = new IdlResolver({\n rpcEndpoint: idlRpcEndpoint ?? endpoint,\n allowOnChainFetch: allowOnChainIdlFetch ?? false,\n });\n if (idls) this.idlResolver.registerMany(idls);\n this.tracer = tracer ?? trace.getTracer('@thesight/sdk');\n }\n\n /**\n * Register an IDL for a single program. Convenience forwarder for the\n * underlying resolver. Anchor users typically call this right after\n * constructing a `Program`:\n *\n * const program = new Program(idl, programId, provider);\n * connection.registerIdl(program.programId.toBase58(), program.idl);\n */\n registerIdl(programId: string, idl: AnchorIdl): void {\n this.idlResolver.register(programId, idl);\n }\n\n /** Bulk-register multiple IDLs. */\n registerIdls(idls: Record<string, AnchorIdl>): void {\n this.idlResolver.registerMany(idls);\n }\n\n // ─── Overridden method ────────────────────────────────────────────────────\n\n /**\n * Overrides the parent `Connection.sendRawTransaction` so every submit —\n * including those made by Anchor's `provider.sendAndConfirm`, web3.js's\n * top-level `sendAndConfirmTransaction`, and wallet adapter flows — is\n * observed by an OTel span automatically.\n *\n * The span is a child of whatever OTel context is active when the call\n * fires, so app-level parent spans (HTTP handlers, job runs, wallet flow\n * spans from another SDK) nest the Sight span correctly.\n */\n override async sendRawTransaction(\n rawTransaction: Buffer | Uint8Array | Array<number>,\n options?: SendOptions,\n ): Promise<TransactionSignature> {\n if (this.sightConfig.disableAutoSpan) {\n return super.sendRawTransaction(rawTransaction, options);\n }\n\n const span = this.tracer.startSpan('solana.sendRawTransaction', {}, context.active());\n const submitStart = Date.now();\n\n let signature: TransactionSignature;\n try {\n signature = await super.sendRawTransaction(rawTransaction, options);\n } catch (err) {\n const submitMs = Date.now() - submitStart;\n span.setAttribute('solana.tx.submit_ms', submitMs);\n span.setAttribute('solana.tx.status', 'failed');\n span.recordException(err instanceof Error ? err : new Error(String(err)));\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: err instanceof Error ? err.message : String(err),\n });\n span.end();\n throw err;\n }\n\n span.setAttribute('solana.tx.signature', signature);\n span.setAttribute('solana.tx.submit_ms', Date.now() - submitStart);\n span.setAttribute('solana.tx.status', 'submitted');\n\n // Fire-and-forget background enrichment. The returned Promise is\n // intentionally discarded — we don't want to couple the caller's\n // transaction-send latency to our observation machinery. Any enrichment\n // error is caught inside enrichSpanInBackground and attached to the\n // span, not bubbled up.\n void this.enrichSpanInBackground(span, signature, submitStart);\n\n return signature;\n }\n\n // ─── Background enrichment ───────────────────────────────────────────────\n\n /**\n * Poll `getTransaction` until the on-chain record is available, then\n * enrich the span with CU attribution, program names, decoded errors,\n * and per-CPI events.\n *\n * This runs async and is never awaited by callers of `sendRawTransaction`.\n * Failures inside this method attach to the span via recordException\n * rather than throwing.\n */\n private async enrichSpanInBackground(\n span: Span,\n signature: TransactionSignature,\n submitStart: number,\n ): Promise<void> {\n const enrichStart = Date.now();\n const deadline = enrichStart + (this.sightConfig.enrichmentTimeoutMs ?? 30_000);\n const commitment = (this.sightConfig.commitment ?? 'confirmed') as Finality;\n const basePollMs = this.sightConfig.enrichmentPollIntervalMs ?? 500;\n\n try {\n const txDetails = await this.pollForTransaction(signature, commitment, deadline, basePollMs);\n\n if (!txDetails) {\n span.setAttribute('solana.tx.status', 'timeout');\n span.setAttribute('solana.tx.enrichment_ms', Date.now() - submitStart);\n span.end();\n return;\n }\n\n this.attachTxDetailsToSpan(span, txDetails);\n\n if (!this.sightConfig.skipIdlResolution) {\n const logs = txDetails.meta?.logMessages ?? [];\n if (logs.length > 0) {\n await this.attachParsedLogsToSpan(span, logs);\n }\n }\n\n span.setAttribute('solana.tx.enrichment_ms', Date.now() - submitStart);\n\n if (txDetails.meta?.err) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n } catch (err) {\n span.recordException(err instanceof Error ? err : new Error(String(err)));\n span.setAttribute(\n 'solana.tx.enrichment_error',\n err instanceof Error ? err.message : String(err),\n );\n } finally {\n span.end();\n }\n }\n\n /**\n * Poll `getTransaction(signature)` until either the on-chain record is\n * returned or the deadline passes. Exponential backoff (1.5x) capped at\n * 2 seconds to balance responsiveness against RPC load.\n */\n private async pollForTransaction(\n signature: TransactionSignature,\n commitment: Finality,\n deadline: number,\n basePollMs: number,\n ): Promise<VersionedTransactionResponse | null> {\n let attempt = 0;\n while (Date.now() < deadline) {\n try {\n const tx = await super.getTransaction(signature, {\n commitment,\n maxSupportedTransactionVersion: 0,\n });\n if (tx) return tx;\n } catch {\n // Ignore — retry with backoff. Transient RPC errors are common\n // during the seconds immediately after submission.\n }\n attempt++;\n const waitMs = Math.min(basePollMs * Math.pow(1.5, attempt - 1), 2_000);\n await sleep(waitMs);\n }\n return null;\n }\n\n /** Attach the flat fields (slot, fee, CU, status) from a tx response to a span. */\n private attachTxDetailsToSpan(span: Span, txDetails: VersionedTransactionResponse): void {\n span.setAttribute('solana.tx.status', txDetails.meta?.err ? 'failed' : 'confirmed');\n span.setAttribute('solana.tx.slot', txDetails.slot);\n\n const fee = txDetails.meta?.fee;\n if (fee !== undefined) span.setAttribute('solana.tx.fee_lamports', fee);\n\n const cuUsed = txDetails.meta?.computeUnitsConsumed;\n if (cuUsed !== undefined && cuUsed !== null) {\n span.setAttribute('solana.tx.cu_used', Number(cuUsed));\n span.setAttribute('solana.tx.cu_budget', 200_000);\n span.setAttribute(\n 'solana.tx.cu_utilization',\n parseFloat((Number(cuUsed) / 200_000 * 100).toFixed(1)),\n );\n }\n }\n\n /**\n * Parse program logs into a CPI tree, enrich with registered IDLs, and\n * emit one `cpi.invoke` event per invocation onto the span. Root\n * program/instruction names are copied onto span attributes so dashboards\n * can filter by them without walking events.\n */\n private async attachParsedLogsToSpan(span: Span, logs: string[]): Promise<CpiTree | undefined> {\n const { cpiTree } = parseLogs({ logs });\n await enrichTree(cpiTree, this.idlResolver);\n\n const attributions = flatAttributions(cpiTree);\n for (const attr of attributions) {\n span.addEvent('cpi.invoke', {\n 'cpi.program': attr.programName ?? attr.programId,\n 'cpi.instruction': attr.instructionName ?? 'unknown',\n 'cpi.depth': attr.depth,\n 'cpi.cu_consumed': attr.cuConsumed,\n 'cpi.cu_self': attr.cuSelf,\n 'cpi.percentage': parseFloat(attr.percentage.toFixed(2)),\n });\n }\n\n const root = cpiTree.roots[0];\n if (root) {\n if (root.programName) span.setAttribute('solana.tx.program', root.programName);\n if (root.instructionName) span.setAttribute('solana.tx.instruction', root.instructionName);\n }\n\n return cpiTree;\n }\n}\n\n// ─── Utilities ────────────────────────────────────────────────────────────\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n// ─── Re-exports ────────────────────────────────────────────────────────────\n\nexport { IdlResolver } from '@thesight/core';\nexport type {\n CpiTree,\n CuAttribution,\n FlamegraphItem,\n DecodedError,\n AnchorIdl,\n} from '@thesight/core';\n\n// ─── Tracing initialization ──────────────────────────────────────────────────\n\nexport { initSight } from './init.js';\nexport type { InitSightConfig, SightTracerHandle } from './init.js';\nexport { SightSpanExporter } from './exporter.js';\nexport type { SightExporterConfig } from './exporter.js';\n\n// ─── DSN helpers ─────────────────────────────────────────────────────────\n\nexport { parseDsn, buildDsn } from './dsn.js';\nexport type { ParsedDsn } from './dsn.js';\n\n// ─── Non-wrapping observation helper ─────────────────────────────────────\n\nexport { trackSolanaTransaction } from './track.js';\nexport type { TrackTransactionOptions } from './track.js';\n","import { trace, type Tracer } from '@opentelemetry/api';\nimport { SightSpanExporter } from './exporter.js';\n\n// ─── Dynamic imports ────────────────────────────────────────────────────────\n// The OTel SDK packages (@opentelemetry/sdk-trace-node, sdk-trace-base,\n// resources, semantic-conventions) are the source of version-conflict\n// errors when Sentry or another OTel consumer is in the same process.\n// By importing them dynamically inside initSight, the conflict is:\n// (a) confined to the try/catch that handles it\n// (b) deferred until the user actually calls initSight, not at\n// module-load time (which would crash the entire require chain)\n\n// ─── Config ──────────────────────────────────────────────────────────────────\n\nexport interface InitSightConfig {\n /**\n * Sight DSN — one string encoding both the ingest endpoint and the API key.\n *\n * Format: `https://<apiKey>@<host>/ingest`\n *\n * Get yours from the Sight dashboard when you create a project. For local\n * dev against a Docker compose stack: `http://sk_live_xxx@localhost:3001/ingest`\n */\n dsn: string;\n\n /**\n * Human-readable service name that shows up on every span. Pick something\n * that identifies this process — `'swap-bot'`, `'market-maker'`,\n * `'trade-settler'`, etc.\n */\n serviceName: string;\n\n /** Optional version string for the service. Useful for correlating spans with a deploy. */\n serviceVersion?: string;\n\n /** BatchSpanProcessor flush interval, in ms. Defaults to 5s. */\n batchDelayMs?: number;\n\n /** Max spans per HTTP POST. Clamped to 100 by the exporter. */\n maxBatchSize?: number;\n\n /** Inject a fetch implementation for tests. Defaults to global fetch. */\n fetchImpl?: typeof fetch;\n}\n\nexport interface SightTracerHandle {\n /**\n * The OTel Tracer that routes spans through the Sight exporter. Pass\n * this to `InstrumentedConnection({ tracer: sight.tracer })`.\n *\n * Always works — even when another OTel provider (Sentry, Datadog)\n * is in the process. This is the recommended way to wire the connection.\n */\n tracer: Tracer;\n\n /**\n * How the tracer was obtained:\n * - `'own_provider'` — Sight created its own NodeTracerProvider and\n * (optionally) registered it globally. Full control.\n * - `'piggyback'` — Another provider (e.g. Sentry) was already\n * registered. Sight added its exporter as an additional span\n * processor on that provider. Both consumers see all spans.\n * - `'api_only'` — Neither own-provider nor piggyback worked.\n * Sight is using the global `trace.getTracer()` which may or may\n * not route to Sight's exporter depending on what else is registered.\n * This is the least reliable mode.\n */\n mode: 'own_provider' | 'piggyback' | 'api_only';\n\n /** Flush any pending spans and release the batch processor. */\n shutdown: () => Promise<void>;\n}\n\n// ─── Entry point ────────────────────────────────────────────────────────────\n\n/**\n * Initialize Sight tracing. Call this **once** near the top of your\n * process entry point.\n *\n * import { initSight, InstrumentedConnection } from '@thesight/sdk';\n *\n * const sight = initSight({\n * dsn: process.env.SIGHT_DSN!,\n * serviceName: 'swap-bot',\n * });\n *\n * const connection = new InstrumentedConnection(process.env.RPC_URL!, {\n * tracer: sight.tracer,\n * });\n *\n * Handles coexistence with other OTel providers (e.g. @sentry/node)\n * gracefully via a three-tier fallback:\n *\n * 1. **Own provider**: create a NodeTracerProvider, add the Sight\n * exporter, register globally. This is the ideal path.\n * 2. **Piggyback**: if step 1 fails (OTel version conflict with Sentry),\n * find the existing global provider and add the Sight exporter to it\n * as an additional span processor. Both Sentry and Sight see spans.\n * 3. **API-only**: if step 2 also fails, fall back to `trace.getTracer()`\n * from the OTel API layer. Spans may or may not reach Sight depending\n * on what's registered. A warning is logged.\n */\nexport function initSight(config: InitSightConfig): SightTracerHandle {\n if (!config.dsn) {\n throw new Error(\n 'initSight: `dsn` is required. ' +\n 'Get your DSN from the Sight dashboard: https://thesight.dev',\n );\n }\n if (!config.serviceName) {\n throw new Error('initSight: `serviceName` is required');\n }\n\n const exporter = new SightSpanExporter({\n dsn: config.dsn,\n fetchImpl: config.fetchImpl,\n maxBatchSize: config.maxBatchSize,\n });\n\n // ── Tier 1: own provider ──────────────────────────────────────────────\n\n try {\n const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node');\n const { BatchSpanProcessor } = require('@opentelemetry/sdk-trace-base');\n const otelResources = require('@opentelemetry/resources');\n\n const processor = new BatchSpanProcessor(exporter, {\n scheduledDelayMillis: config.batchDelayMs ?? 5_000,\n maxExportBatchSize: config.maxBatchSize ?? 100,\n maxQueueSize: 2048,\n });\n\n // OTel 2.x removed the Resource class in favor of resourceFromAttributes().\n // OTel 1.x has Resource but not resourceFromAttributes.\n // Handle both so the SDK works regardless of which version the host\n // project resolves.\n const resourceAttrs: Record<string, string> = {\n 'service.name': config.serviceName,\n ...(config.serviceVersion ? { 'service.version': config.serviceVersion } : {}),\n };\n const resource = typeof otelResources.resourceFromAttributes === 'function'\n ? otelResources.resourceFromAttributes(resourceAttrs)\n : new otelResources.Resource(resourceAttrs);\n\n // OTel 2.x removed provider.addSpanProcessor() in favor of passing\n // spanProcessors in the constructor. OTel 1.x has addSpanProcessor\n // but not the constructor option. Handle both.\n let provider: any;\n if (typeof NodeTracerProvider.prototype.addSpanProcessor === 'function') {\n // OTel 1.x path\n provider = new NodeTracerProvider({ resource });\n provider.addSpanProcessor(processor);\n } else {\n // OTel 2.x path\n provider = new NodeTracerProvider({ resource, spanProcessors: [processor] });\n }\n\n try {\n provider.register();\n } catch {\n // Registration failed (another provider owns the global), but\n // our standalone provider still works.\n }\n\n return {\n tracer: provider.getTracer('@thesight/sdk'),\n mode: 'own_provider' as const,\n shutdown: async () => { await provider.shutdown(); },\n };\n } catch {\n // Tier 1 failed.\n }\n\n // ── Tier 2: piggyback on the existing global provider ─────────────────\n //\n // If another provider (Sentry) already registered, try adding our\n // exporter as an additional span processor. This makes both Sentry\n // and Sight see the same spans — the ideal coexistence model.\n //\n // Critically, this tier does NOT import from @opentelemetry/sdk-trace-base\n // or sdk-trace-node — those are the version-conflicting modules. Instead\n // it uses a minimal inline span processor that implements the interface\n // via duck-typing. The OTel provider calls processor.onEnd(span) for\n // every finished span; our processor buffers them and calls the Sight\n // exporter. No version-sensitive imports needed.\n\n try {\n const proxy = trace.getTracerProvider() as any;\n const delegate = proxy?.getDelegate?.() ?? proxy;\n\n if (delegate && typeof delegate.addSpanProcessor === 'function') {\n const processor = new SightInlineProcessor(\n exporter,\n config.batchDelayMs ?? 5_000,\n config.maxBatchSize ?? 100,\n );\n delegate.addSpanProcessor(processor);\n return {\n tracer: trace.getTracer('@thesight/sdk'),\n mode: 'piggyback',\n shutdown: async () => { await processor.shutdown(); },\n };\n }\n } catch {\n // Tier 2 also failed.\n }\n\n // ── Tier 3: API-only fallback ─────────────────────────────────────────\n //\n // Last resort. We return a tracer from the OTel API layer. If Sentry\n // registered a provider, this tracer creates spans on Sentry's\n // provider — those spans go to Sentry but NOT to Sight (we couldn't\n // attach our processor). Log a warning so the user knows instrumentation\n // is degraded.\n\n if (typeof console !== 'undefined') {\n console.warn(\n '[sight] Could not create an OTel tracer provider or attach to the ' +\n 'existing one (likely an @opentelemetry version conflict with ' +\n '@sentry/node). Sight spans may not reach ingest. Consider ' +\n 'aligning OTel versions via pnpm.overrides or npm overrides.',\n );\n }\n\n return {\n tracer: trace.getTracer('@thesight/sdk'),\n mode: 'api_only',\n shutdown: async () => {},\n };\n}\n\n// ─── Inline span processor ─────────────────────────────────────────────────\n//\n// A minimal batch span processor that implements the OTel SpanProcessor\n// interface structurally (duck-typed). It does NOT import from\n// @opentelemetry/sdk-trace-base, which is the module that version-\n// conflicts with Sentry. Instead it implements the four methods the\n// provider calls (onStart, onEnd, shutdown, forceFlush) and drives\n// our SightSpanExporter directly.\n//\n// This class only exists for the tier-2 piggyback path. Tier 1 uses\n// the real BatchSpanProcessor from the OTel SDK (which has nicer\n// retry/backpressure logic); tier 2 uses this inline version because\n// it's the only way to avoid the version conflict.\n\nclass SightInlineProcessor {\n private buffer: unknown[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private readonly maxBatch: number;\n\n constructor(\n private readonly exporter: SightSpanExporter,\n delayMs: number,\n maxBatch: number,\n ) {\n this.maxBatch = maxBatch;\n this.timer = setInterval(() => this.flush(), delayMs);\n // Prevent the timer from keeping the process alive if everything\n // else has exited. Node-specific but safe — `unref` is a no-op in\n // environments that don't have it.\n if (this.timer && typeof (this.timer as any).unref === 'function') {\n (this.timer as any).unref();\n }\n }\n\n /** Called by the provider when a span starts. We don't need it. */\n onStart(): void {}\n\n /** Called by the provider when a span ends. Buffer it for export. */\n onEnd(span: unknown): void {\n this.buffer.push(span);\n if (this.buffer.length >= this.maxBatch) {\n this.flush();\n }\n }\n\n /** Flush pending spans to the exporter. */\n private flush(): void {\n if (this.buffer.length === 0) return;\n const batch = this.buffer.splice(0, this.maxBatch);\n // exporter.export expects ReadableSpan[] — the spans from the\n // provider's onEnd ARE ReadableSpan instances regardless of which\n // sdk-trace-base version created them, because the interface is\n // structurally stable across 1.x versions.\n this.exporter.export(batch as any, () => {});\n }\n\n async shutdown(): Promise<void> {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n this.flush();\n await this.exporter.shutdown();\n }\n\n async forceFlush(): Promise<void> {\n this.flush();\n }\n}\n","import type { SpanAttributes } from '@opentelemetry/api';\nimport type { SpanExporter, ReadableSpan } from '@opentelemetry/sdk-trace-base';\nimport {\n hrTimeToMilliseconds,\n ExportResultCode,\n type ExportResult,\n} from '@opentelemetry/core';\nimport { parseDsn } from './dsn.js';\n\n// ─── Config ──────────────────────────────────────────────────────────────────\n\nexport interface SightExporterConfig {\n /**\n * Sight DSN — one string encoding both the ingest endpoint and the API key.\n * Format: https://<apiKey>@<host>/ingest\n */\n dsn: string;\n /** Inject a fake fetch in tests. Defaults to `globalThis.fetch`. */\n fetchImpl?: typeof fetch;\n /** Max spans per POST. Ingest enforces an upper bound of 100. */\n maxBatchSize?: number;\n}\n\n// ─── Exporter ────────────────────────────────────────────────────────────────\n\n/**\n * An OTel `SpanExporter` that translates OTel spans to Sight's custom\n * ingest payload shape and POSTs them to `/ingest`.\n *\n * Each span becomes one object in the `spans` array. Solana-specific\n * attributes flow through as-is (the set that `InstrumentedConnection`\n * already populates — `solana.tx.signature`, `solana.tx.cu_used`,\n * `solana.tx.program`, etc). The exporter intentionally does not attempt\n * to translate span events or links — the ingest schema doesn't have\n * slots for those, and we prefer dropping silently over guessing.\n *\n * The BatchSpanProcessor upstream handles retries, timeouts, and batching;\n * this exporter stays a thin wire-format translator.\n */\nexport class SightSpanExporter implements SpanExporter {\n private readonly apiKey: string;\n private readonly ingestUrl: string;\n private readonly fetchImpl: typeof fetch;\n private readonly maxBatchSize: number;\n private shuttingDown = false;\n\n constructor(config: SightExporterConfig) {\n const parsed = parseDsn(config.dsn);\n this.apiKey = parsed.apiKey;\n this.ingestUrl = parsed.ingestUrl;\n this.fetchImpl = config.fetchImpl ?? (globalThis.fetch as typeof fetch);\n this.maxBatchSize = Math.min(100, config.maxBatchSize ?? 100);\n\n if (!this.fetchImpl) {\n throw new Error(\n 'SightSpanExporter: globalThis.fetch is not available. Node >= 18 ships ' +\n 'with fetch built in, or pass `fetchImpl` explicitly.',\n );\n }\n }\n\n async export(\n spans: ReadableSpan[],\n resultCallback: (result: ExportResult) => void,\n ): Promise<void> {\n if (this.shuttingDown) {\n resultCallback({ code: ExportResultCode.FAILED, error: new Error('Exporter is shut down') });\n return;\n }\n\n // Split into chunks if the upstream processor handed us a larger\n // batch than ingest will accept. Uncommon with a well-configured\n // BatchSpanProcessor but cheap to handle defensively.\n const chunks: ReadableSpan[][] = [];\n for (let i = 0; i < spans.length; i += this.maxBatchSize) {\n chunks.push(spans.slice(i, i + this.maxBatchSize));\n }\n\n try {\n for (const chunk of chunks) {\n await this.sendChunk(chunk);\n }\n resultCallback({ code: ExportResultCode.SUCCESS });\n } catch (err) {\n resultCallback({\n code: ExportResultCode.FAILED,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n }\n }\n\n async shutdown(): Promise<void> {\n this.shuttingDown = true;\n }\n\n async forceFlush(): Promise<void> {\n // The exporter is stateless — BatchSpanProcessor's own forceFlush\n // drains the queue before calling export(). Nothing to do here.\n }\n\n // ─── Internal ──────────────────────────────────────────────────────────────\n\n private async sendChunk(spans: ReadableSpan[]): Promise<void> {\n const payload = {\n spans: spans.map((span) => this.convertSpan(span)),\n };\n\n const response = await this.fetchImpl(this.ingestUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n const body = await safeReadText(response);\n throw new Error(\n `Sight ingest returned ${response.status} ${response.statusText}: ${body}`,\n );\n }\n }\n\n private convertSpan(span: ReadableSpan): IngestSpan {\n const attr = span.attributes;\n const resource = span.resource.attributes;\n const serviceName = typeof resource['service.name'] === 'string'\n ? resource['service.name']\n : undefined;\n const startTimeMs = hrTimeToMilliseconds(span.startTime);\n const endTimeMs = hrTimeToMilliseconds(span.endTime);\n const durationMs = Math.max(0, endTimeMs - startTimeMs);\n\n const out: IngestSpan = {\n traceId: span.spanContext().traceId,\n spanId: span.spanContext().spanId,\n spanName: span.name,\n startTimeMs,\n durationMs,\n };\n\n const parentSpanId = (span as { parentSpanId?: string }).parentSpanId;\n if (parentSpanId) out.parentSpanId = parentSpanId;\n if (serviceName) out.serviceName = serviceName;\n\n copyIfString(attr, 'solana.tx.signature', out);\n copyIfEnum(attr, 'solana.tx.status', out, ['submitted', 'confirmed', 'failed', 'timeout']);\n copyIfNumber(attr, 'solana.tx.slot', out);\n copyIfNumber(attr, 'solana.tx.cu_used', out);\n copyIfNumber(attr, 'solana.tx.cu_budget', out);\n copyIfNumber(attr, 'solana.tx.cu_utilization', out);\n copyIfString(attr, 'solana.tx.program', out);\n copyIfString(attr, 'solana.tx.instruction', out);\n copyIfString(attr, 'solana.tx.error', out);\n copyIfNumber(attr, 'solana.tx.error_code', out);\n copyIfString(attr, 'solana.tx.error_program', out);\n copyIfNumber(attr, 'solana.tx.submit_ms', out);\n copyIfNumber(attr, 'solana.tx.confirmation_ms', out);\n copyIfNumber(attr, 'solana.tx.fee_lamports', out);\n\n return out;\n }\n}\n\n// ─── Wire format (narrow, matches ingest's zod schema) ──────────────────────\n\ninterface IngestSpan {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n serviceName?: string;\n spanName: string;\n startTimeMs: number;\n durationMs: number;\n\n 'solana.tx.signature'?: string;\n 'solana.tx.status'?: 'confirmed' | 'failed' | 'timeout';\n 'solana.tx.slot'?: number;\n 'solana.tx.cu_used'?: number;\n 'solana.tx.cu_budget'?: number;\n 'solana.tx.cu_utilization'?: number;\n 'solana.tx.program'?: string;\n 'solana.tx.instruction'?: string;\n 'solana.tx.error'?: string;\n 'solana.tx.error_code'?: number;\n 'solana.tx.error_program'?: string;\n 'solana.tx.submit_ms'?: number;\n 'solana.tx.confirmation_ms'?: number;\n 'solana.tx.fee_lamports'?: number;\n}\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\nfunction copyIfString(\n attr: SpanAttributes,\n key: keyof IngestSpan & string,\n out: IngestSpan,\n): void {\n const v = attr[key];\n if (typeof v === 'string') {\n (out as unknown as Record<string, unknown>)[key] = v;\n }\n}\n\nfunction copyIfNumber(\n attr: SpanAttributes,\n key: keyof IngestSpan & string,\n out: IngestSpan,\n): void {\n const v = attr[key];\n if (typeof v === 'number' && Number.isFinite(v)) {\n (out as unknown as Record<string, unknown>)[key] = v;\n }\n}\n\nfunction copyIfEnum<T extends string>(\n attr: SpanAttributes,\n key: keyof IngestSpan & string,\n out: IngestSpan,\n allowed: readonly T[],\n): void {\n const v = attr[key];\n if (typeof v === 'string' && (allowed as readonly string[]).includes(v)) {\n (out as unknown as Record<string, unknown>)[key] = v;\n }\n}\n\nasync function safeReadText(res: Response): Promise<string> {\n try {\n return (await res.text()).slice(0, 500);\n } catch {\n return '<no body>';\n }\n}\n","/**\n * Parse a Sight DSN into its constituent parts.\n *\n * DSN format:\n * https://<apiKey>@<host>[:<port>]/<path>\n *\n * Examples:\n * https://sk_live_abc123@ingest.thesight.dev/ingest (production)\n * http://sk_live_abc123@localhost:3001/ingest (local dev)\n *\n * The API key sits in the URL's username position. The SDK extracts it\n * and sends it as a Bearer token to the ingest URL (with the key removed\n * from the URL). This mirrors the Sentry DSN model — one string encodes\n * both \"where to send\" and \"who you are\", so there's no separate\n * apiKey + ingestUrl to misconfig independently.\n */\n\nexport interface ParsedDsn {\n /** The API key extracted from the DSN's username position. */\n apiKey: string;\n /** The ingest endpoint URL with the API key stripped out. */\n ingestUrl: string;\n}\n\n/**\n * Parse a DSN string into `{ apiKey, ingestUrl }`.\n *\n * Throws with a clear message on:\n * - Malformed URL\n * - Missing API key (no username in the URL)\n */\nexport function parseDsn(dsn: string): ParsedDsn {\n let url: URL;\n try {\n url = new URL(dsn);\n } catch {\n throw new Error(\n `Invalid Sight DSN: \"${dsn}\". ` +\n 'Expected format: https://<apiKey>@<host>/ingest — ' +\n 'get your DSN from the Sight dashboard when you create a project.',\n );\n }\n\n const apiKey = decodeURIComponent(url.username);\n if (!apiKey) {\n throw new Error(\n `Invalid Sight DSN: no API key found in \"${dsn}\". ` +\n 'The API key goes in the username position: https://sk_live_xxx@host/ingest',\n );\n }\n\n // Strip the credentials so the ingest URL is clean for HTTP requests.\n url.username = '';\n url.password = '';\n const ingestUrl = url.toString().replace(/\\/$/, ''); // trim trailing slash\n\n return { apiKey, ingestUrl };\n}\n\n/**\n * Build a DSN from its parts. Used by the dashboard when generating the\n * DSN for a newly-created project.\n */\nexport function buildDsn(apiKey: string, ingestHost: string): string {\n const url = new URL(ingestHost);\n url.username = apiKey;\n if (!url.pathname || url.pathname === '/') {\n url.pathname = '/ingest';\n }\n return url.toString();\n}\n","import type { Connection, Finality, TransactionSignature, VersionedTransactionResponse } from '@solana/web3.js';\nimport { trace, context, SpanStatusCode, type Tracer } from '@opentelemetry/api';\nimport { parseLogs, IdlResolver, enrichTree, flatAttributions } from '@thesight/core';\nimport type { AnchorIdl } from '@thesight/core';\n\n// ─── Options ──────────────────────────────────────────────────────────────\n\nexport interface TrackTransactionOptions {\n /** The signature returned from a prior `sendRawTransaction` / `sendTransaction` call. */\n signature: TransactionSignature;\n\n /**\n * Any `@solana/web3.js` Connection — plain or instrumented. Used to fetch\n * the on-chain transaction via `getTransaction` for enrichment. Does\n * **not** need to be an `InstrumentedConnection`; this helper intentionally\n * never touches the transaction-send path.\n */\n connection: Connection;\n\n /**\n * Service name used for the span. Falls back to the service name\n * configured by `initSight`. Useful when you want a different service\n * name for a specific subsystem's spans.\n */\n serviceName?: string;\n\n /** OTel tracer override. Defaults to `trace.getTracer('@thesight/sdk')`. */\n tracer?: Tracer;\n\n /** Optional IDL resolver for program/error decoding. A fresh one is built if omitted. */\n idlResolver?: IdlResolver;\n\n /**\n * Pre-registered IDLs to hand to a freshly-built resolver. Ignored when\n * `idlResolver` is provided.\n */\n idls?: Record<string, AnchorIdl>;\n\n /** Finality commitment used for the enrichment fetch. Default 'confirmed'. */\n commitment?: Finality;\n\n /** Max wall-time before the span is ended with status=timeout. Default 30000ms. */\n timeoutMs?: number;\n\n /** Base poll interval for retrying getTransaction. Default 500ms. */\n pollIntervalMs?: number;\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────\n\n/**\n * **Non-wrapping observation helper.** Given a transaction signature that\n * was already sent through any `Connection`, fetch the on-chain record,\n * parse the logs, and emit a single OpenTelemetry span describing the\n * transaction.\n *\n * Unlike `InstrumentedConnection`, this helper **never touches the send\n * path** — it only observes after the fact via `getTransaction(signature)`.\n * Use this when:\n *\n * - You want observability for wallet-adapter flows where the send path\n * goes through the wallet and you can't easily override the Connection\n * - You're security-conscious and don't want any SDK code on the critical\n * transaction path\n * - You want to instrument a handful of high-value transactions rather\n * than every call a Connection makes\n *\n * Usage:\n *\n * import { trackSolanaTransaction } from '@thesight/sdk';\n * import { Connection } from '@solana/web3.js';\n *\n * const connection = new Connection(rpcUrl);\n * const signature = await wallet.sendTransaction(tx, connection);\n *\n * // Fire-and-forget. Span flushes on its own.\n * void trackSolanaTransaction({\n * signature,\n * connection,\n * serviceName: 'checkout',\n * idls: { [programId]: idl },\n * });\n *\n * Returns a `Promise<void>` — typically not awaited, but callers who want\n * to wait for the span to export (e.g. short-lived scripts) can await it.\n */\nexport async function trackSolanaTransaction(opts: TrackTransactionOptions): Promise<void> {\n const tracerName = opts.serviceName ?? '@thesight/sdk';\n const tracer = opts.tracer ?? trace.getTracer(tracerName);\n\n const span = tracer.startSpan('solana.trackTransaction', {}, context.active());\n span.setAttribute('solana.tx.signature', opts.signature);\n\n const start = Date.now();\n const deadline = start + (opts.timeoutMs ?? 30_000);\n const commitment = opts.commitment ?? ('confirmed' as Finality);\n const basePoll = opts.pollIntervalMs ?? 500;\n\n const resolver = opts.idlResolver ?? buildResolverFromIdls(opts.idls);\n\n try {\n const txDetails = await pollGetTransaction(opts.connection, opts.signature, commitment, deadline, basePoll);\n if (!txDetails) {\n span.setAttribute('solana.tx.status', 'timeout');\n span.setAttribute('solana.tx.enrichment_ms', Date.now() - start);\n return;\n }\n\n span.setAttribute('solana.tx.status', txDetails.meta?.err ? 'failed' : 'confirmed');\n span.setAttribute('solana.tx.slot', txDetails.slot);\n\n const fee = txDetails.meta?.fee;\n if (fee !== undefined) span.setAttribute('solana.tx.fee_lamports', fee);\n\n const cuUsed = txDetails.meta?.computeUnitsConsumed;\n if (cuUsed !== undefined && cuUsed !== null) {\n span.setAttribute('solana.tx.cu_used', Number(cuUsed));\n span.setAttribute('solana.tx.cu_budget', 200_000);\n span.setAttribute(\n 'solana.tx.cu_utilization',\n parseFloat((Number(cuUsed) / 200_000 * 100).toFixed(1)),\n );\n }\n\n const logs = txDetails.meta?.logMessages ?? [];\n if (logs.length > 0) {\n const { cpiTree } = parseLogs({ logs });\n await enrichTree(cpiTree, resolver);\n\n for (const attr of flatAttributions(cpiTree)) {\n span.addEvent('cpi.invoke', {\n 'cpi.program': attr.programName ?? attr.programId,\n 'cpi.instruction': attr.instructionName ?? 'unknown',\n 'cpi.depth': attr.depth,\n 'cpi.cu_consumed': attr.cuConsumed,\n 'cpi.cu_self': attr.cuSelf,\n 'cpi.percentage': parseFloat(attr.percentage.toFixed(2)),\n });\n }\n\n const root = cpiTree.roots[0];\n if (root) {\n if (root.programName) span.setAttribute('solana.tx.program', root.programName);\n if (root.instructionName) span.setAttribute('solana.tx.instruction', root.instructionName);\n }\n }\n\n span.setAttribute('solana.tx.enrichment_ms', Date.now() - start);\n\n if (txDetails.meta?.err) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n } catch (err) {\n span.recordException(err instanceof Error ? err : new Error(String(err)));\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: err instanceof Error ? err.message : String(err),\n });\n } finally {\n span.end();\n }\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────\n\nfunction buildResolverFromIdls(idls?: Record<string, AnchorIdl>): IdlResolver {\n const resolver = new IdlResolver();\n if (idls) resolver.registerMany(idls);\n return resolver;\n}\n\nasync function pollGetTransaction(\n connection: Connection,\n signature: TransactionSignature,\n commitment: Finality,\n deadline: number,\n basePollMs: number,\n): Promise<VersionedTransactionResponse | null> {\n let attempt = 0;\n while (Date.now() < deadline) {\n try {\n const tx = await connection.getTransaction(signature, {\n commitment,\n maxSupportedTransactionVersion: 0,\n });\n if (tx) return tx;\n } catch {\n // Ignore — retry with backoff\n }\n attempt++;\n const waitMs = Math.min(basePollMs * Math.pow(1.5, attempt - 1), 2_000);\n await new Promise((resolve) => setTimeout(resolve, waitMs));\n }\n return null;\n}\n"],"mappings":"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","names":["trace","context","SpanStatusCode","parseLogs","IdlResolver","enrichTree","flatAttributions","trace","IdlResolver","trace","context","SpanStatusCode","parseLogs","enrichTree","flatAttributions"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/init.ts","../src/exporter.ts","../src/dsn.ts","../src/track.ts"],"sourcesContent":["import {\n Connection,\n type ConnectionConfig,\n type SendOptions,\n type Commitment,\n type Finality,\n type TransactionSignature,\n type VersionedTransactionResponse,\n} from '@solana/web3.js';\nimport {\n trace,\n context,\n SpanStatusCode,\n type Span,\n type Tracer,\n} from '@opentelemetry/api';\nimport { parseLogs, IdlResolver, enrichTree, flatAttributions } from '@thesight/core';\nimport type { AnchorIdl, CpiTree } from '@thesight/core';\n\n// ─── Config ────────────────────────────────────────────────────────────────\n\nexport interface SightConfig {\n /** OTel tracer. Defaults to `trace.getTracer('@thesight/sdk')`. */\n tracer?: Tracer;\n\n /** Override RPC endpoint for IDL fetching (defaults to same as connection) */\n idlRpcEndpoint?: string;\n\n /**\n * Commitment used when fetching confirmed tx details for enrichment.\n * Defaults to 'confirmed'.\n */\n commitment?: Commitment;\n\n /**\n * Skip IDL resolution entirely. Spans will still emit with signature and\n * timing attributes, but program names, instruction names, CPI trees, and\n * decoded errors will all be omitted. Useful when you want minimal\n * overhead and don't care about the richer observability.\n */\n skipIdlResolution?: boolean;\n\n /**\n * Pre-register IDLs at construction time. Keys are program IDs, values are\n * full Anchor IDL objects. Anchor users can pass `program.idl` directly off\n * their `Program` instance — zero setup friction.\n */\n idls?: Record<string, AnchorIdl>;\n\n /**\n * Opt into reading Anchor IDL accounts from the on-chain PDA as a fallback\n * when a program is not explicitly registered. Defaults to **false** —\n * Sight's model is that IDLs are explicitly provided by the application,\n * not silently guessed from chain.\n */\n allowOnChainIdlFetch?: boolean;\n\n /**\n * Max wall-time the background enrichment task will wait for a\n * transaction to show up in `getTransaction`. After this expires the\n * span is ended with `solana.tx.status = 'timeout'`. Default 30000 (30s).\n */\n enrichmentTimeoutMs?: number;\n\n /**\n * How often the enrichment task polls `getTransaction`. Each retry backs\n * off by 1.5x up to a cap. Default 500ms base.\n */\n enrichmentPollIntervalMs?: number;\n\n /**\n * Disable automatic span creation in the overridden `sendRawTransaction`.\n * When true, InstrumentedConnection behaves like a plain Connection. You\n * probably don't want this — it's here as an escape hatch for tests and\n * rare cases where you need the class hierarchy but not the tracing.\n */\n disableAutoSpan?: boolean;\n}\n\n// ─── Span attribute shape (documented, for downstream consumers) ──────────\n\nexport interface SightSpanAttributes {\n 'solana.tx.signature': string;\n 'solana.tx.status': 'submitted' | 'confirmed' | 'failed' | 'timeout';\n 'solana.tx.slot'?: number;\n 'solana.tx.fee_lamports'?: number;\n 'solana.tx.submit_ms': number;\n 'solana.tx.enrichment_ms'?: number;\n 'solana.tx.cu_used'?: number;\n 'solana.tx.cu_budget'?: number;\n 'solana.tx.cu_utilization'?: number;\n 'solana.tx.program'?: string;\n 'solana.tx.instruction'?: string;\n 'solana.tx.error'?: string;\n 'solana.tx.error_code'?: number;\n 'solana.tx.error_program'?: string;\n 'solana.tx.error_msg'?: string;\n}\n\n// ─── InstrumentedConnection ────────────────────────────────────────────────\n\n/**\n * Drop-in replacement for `@solana/web3.js`'s `Connection` that emits an\n * OpenTelemetry span for **every** call to `sendRawTransaction` —\n * regardless of whether the caller is your own code, an Anchor provider's\n * `sendAndConfirm`, `@solana/web3.js`'s top-level `sendAndConfirmTransaction`,\n * a wallet adapter, or anything else.\n *\n * Internally it overrides the parent class's `sendRawTransaction`, so the\n * span covers the same surface web3.js already mediates. No mutation of\n * the transaction bytes — we call `super.sendRawTransaction(rawTx, opts)`\n * verbatim and observe the signature it returns.\n *\n * After the signature is returned (synchronously from the caller's point\n * of view), a background task polls `getTransaction` until the on-chain\n * record is available, parses the program logs via `@thesight/core`, and\n * enriches the span with CU attribution, per-CPI events, program + instruction\n * names, and decoded error details. The background task never blocks the\n * caller — if it fails or times out, the span ends with status=timeout and\n * whatever partial data was collected.\n *\n * Usage:\n *\n * import { initSight, InstrumentedConnection } from '@thesight/sdk';\n *\n * initSight({ dsn: process.env.SIGHT_DSN!, serviceName: 'swap-bot' });\n *\n * // Anywhere you currently construct a Connection, construct this instead:\n * const connection = new InstrumentedConnection(rpcUrl, {\n * commitment: 'confirmed',\n * });\n *\n * // All of the following now emit spans automatically:\n * await connection.sendRawTransaction(tx.serialize());\n * await sendAndConfirmTransaction(connection, tx, signers); // web3.js\n * await program.methods.foo().rpc(); // Anchor\n * await wallet.sendTransaction(tx, connection); // wallet adapter\n */\nexport class InstrumentedConnection extends Connection {\n private sightConfig: SightConfig;\n private idlResolver: IdlResolver;\n private tracer: Tracer;\n\n constructor(endpoint: string, config?: ConnectionConfig & SightConfig) {\n const {\n tracer,\n idlRpcEndpoint,\n skipIdlResolution,\n idls,\n allowOnChainIdlFetch,\n enrichmentTimeoutMs,\n enrichmentPollIntervalMs,\n disableAutoSpan,\n commitment,\n ...connectionConfig\n } = config ?? {};\n\n super(endpoint, connectionConfig);\n\n this.sightConfig = {\n tracer,\n idlRpcEndpoint,\n skipIdlResolution,\n allowOnChainIdlFetch,\n enrichmentTimeoutMs: enrichmentTimeoutMs ?? 30_000,\n enrichmentPollIntervalMs: enrichmentPollIntervalMs ?? 500,\n disableAutoSpan,\n commitment,\n };\n this.idlResolver = new IdlResolver({\n rpcEndpoint: idlRpcEndpoint ?? endpoint,\n allowOnChainFetch: allowOnChainIdlFetch ?? false,\n });\n if (idls) this.idlResolver.registerMany(idls);\n this.tracer = tracer ?? trace.getTracer('@thesight/sdk');\n }\n\n /**\n * Register an IDL for a single program. Convenience forwarder for the\n * underlying resolver. Anchor users typically call this right after\n * constructing a `Program`:\n *\n * const program = new Program(idl, programId, provider);\n * connection.registerIdl(program.programId.toBase58(), program.idl);\n */\n registerIdl(programId: string, idl: AnchorIdl): void {\n this.idlResolver.register(programId, idl);\n }\n\n /** Bulk-register multiple IDLs. */\n registerIdls(idls: Record<string, AnchorIdl>): void {\n this.idlResolver.registerMany(idls);\n }\n\n // ─── Overridden method ────────────────────────────────────────────────────\n\n /**\n * Overrides the parent `Connection.sendRawTransaction` so every submit —\n * including those made by Anchor's `provider.sendAndConfirm`, web3.js's\n * top-level `sendAndConfirmTransaction`, and wallet adapter flows — is\n * observed by an OTel span automatically.\n *\n * The span is a child of whatever OTel context is active when the call\n * fires, so app-level parent spans (HTTP handlers, job runs, wallet flow\n * spans from another SDK) nest the Sight span correctly.\n */\n override async sendRawTransaction(\n rawTransaction: Buffer | Uint8Array | Array<number>,\n options?: SendOptions,\n ): Promise<TransactionSignature> {\n if (this.sightConfig.disableAutoSpan) {\n return super.sendRawTransaction(rawTransaction, options);\n }\n\n const span = this.tracer.startSpan('solana.sendRawTransaction', {}, context.active());\n const submitStart = Date.now();\n\n let signature: TransactionSignature;\n try {\n signature = await super.sendRawTransaction(rawTransaction, options);\n } catch (err) {\n const submitMs = Date.now() - submitStart;\n span.setAttribute('solana.tx.submit_ms', submitMs);\n span.setAttribute('solana.tx.status', 'failed');\n span.recordException(err instanceof Error ? err : new Error(String(err)));\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: err instanceof Error ? err.message : String(err),\n });\n span.end();\n throw err;\n }\n\n span.setAttribute('solana.tx.signature', signature);\n span.setAttribute('solana.tx.submit_ms', Date.now() - submitStart);\n span.setAttribute('solana.tx.status', 'submitted');\n\n // Fire-and-forget background enrichment. The returned Promise is\n // intentionally discarded — we don't want to couple the caller's\n // transaction-send latency to our observation machinery. Any enrichment\n // error is caught inside enrichSpanInBackground and attached to the\n // span, not bubbled up.\n void this.enrichSpanInBackground(span, signature, submitStart);\n\n return signature;\n }\n\n // ─── Background enrichment ───────────────────────────────────────────────\n\n /**\n * Poll `getTransaction` until the on-chain record is available, then\n * enrich the span with CU attribution, program names, decoded errors,\n * and per-CPI events.\n *\n * This runs async and is never awaited by callers of `sendRawTransaction`.\n * Failures inside this method attach to the span via recordException\n * rather than throwing.\n */\n private async enrichSpanInBackground(\n span: Span,\n signature: TransactionSignature,\n submitStart: number,\n ): Promise<void> {\n const enrichStart = Date.now();\n const deadline = enrichStart + (this.sightConfig.enrichmentTimeoutMs ?? 30_000);\n const commitment = (this.sightConfig.commitment ?? 'confirmed') as Finality;\n const basePollMs = this.sightConfig.enrichmentPollIntervalMs ?? 500;\n\n try {\n const txDetails = await this.pollForTransaction(signature, commitment, deadline, basePollMs);\n\n if (!txDetails) {\n span.setAttribute('solana.tx.status', 'timeout');\n span.setAttribute('solana.tx.enrichment_ms', Date.now() - submitStart);\n span.end();\n return;\n }\n\n this.attachTxDetailsToSpan(span, txDetails);\n\n if (!this.sightConfig.skipIdlResolution) {\n const logs = txDetails.meta?.logMessages ?? [];\n if (logs.length > 0) {\n await this.attachParsedLogsToSpan(span, logs);\n }\n }\n\n span.setAttribute('solana.tx.enrichment_ms', Date.now() - submitStart);\n\n if (txDetails.meta?.err) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n } catch (err) {\n span.recordException(err instanceof Error ? err : new Error(String(err)));\n span.setAttribute(\n 'solana.tx.enrichment_error',\n err instanceof Error ? err.message : String(err),\n );\n } finally {\n span.end();\n }\n }\n\n /**\n * Poll `getTransaction(signature)` until either the on-chain record is\n * returned or the deadline passes. Exponential backoff (1.5x) capped at\n * 2 seconds to balance responsiveness against RPC load.\n */\n private async pollForTransaction(\n signature: TransactionSignature,\n commitment: Finality,\n deadline: number,\n basePollMs: number,\n ): Promise<VersionedTransactionResponse | null> {\n let attempt = 0;\n while (Date.now() < deadline) {\n try {\n const tx = await super.getTransaction(signature, {\n commitment,\n maxSupportedTransactionVersion: 0,\n });\n if (tx) return tx;\n } catch {\n // Ignore — retry with backoff. Transient RPC errors are common\n // during the seconds immediately after submission.\n }\n attempt++;\n const waitMs = Math.min(basePollMs * Math.pow(1.5, attempt - 1), 2_000);\n await sleep(waitMs);\n }\n return null;\n }\n\n /** Attach the flat fields (slot, fee, CU, status) from a tx response to a span. */\n private attachTxDetailsToSpan(span: Span, txDetails: VersionedTransactionResponse): void {\n span.setAttribute('solana.tx.status', txDetails.meta?.err ? 'failed' : 'confirmed');\n span.setAttribute('solana.tx.slot', txDetails.slot);\n\n const fee = txDetails.meta?.fee;\n if (fee !== undefined) span.setAttribute('solana.tx.fee_lamports', fee);\n\n const cuUsed = txDetails.meta?.computeUnitsConsumed;\n if (cuUsed !== undefined && cuUsed !== null) {\n span.setAttribute('solana.tx.cu_used', Number(cuUsed));\n span.setAttribute('solana.tx.cu_budget', 200_000);\n span.setAttribute(\n 'solana.tx.cu_utilization',\n parseFloat((Number(cuUsed) / 200_000 * 100).toFixed(1)),\n );\n }\n }\n\n /**\n * Parse program logs into a CPI tree, enrich with registered IDLs, and\n * emit one `cpi.invoke` event per invocation onto the span. Root\n * program/instruction names are copied onto span attributes so dashboards\n * can filter by them without walking events.\n */\n private async attachParsedLogsToSpan(span: Span, logs: string[]): Promise<CpiTree | undefined> {\n const { cpiTree } = parseLogs({ logs });\n await enrichTree(cpiTree, this.idlResolver);\n\n const attributions = flatAttributions(cpiTree);\n for (const attr of attributions) {\n span.addEvent('cpi.invoke', {\n 'cpi.program': attr.programName ?? attr.programId,\n 'cpi.instruction': attr.instructionName ?? 'unknown',\n 'cpi.depth': attr.depth,\n 'cpi.cu_consumed': attr.cuConsumed,\n 'cpi.cu_self': attr.cuSelf,\n 'cpi.percentage': parseFloat(attr.percentage.toFixed(2)),\n });\n }\n\n const root = cpiTree.roots[0];\n if (root) {\n if (root.programName) span.setAttribute('solana.tx.program', root.programName);\n if (root.instructionName) span.setAttribute('solana.tx.instruction', root.instructionName);\n }\n\n return cpiTree;\n }\n}\n\n// ─── Utilities ────────────────────────────────────────────────────────────\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n// ─── Re-exports ────────────────────────────────────────────────────────────\n\nexport { IdlResolver } from '@thesight/core';\nexport type {\n CpiTree,\n CuAttribution,\n FlamegraphItem,\n DecodedError,\n AnchorIdl,\n} from '@thesight/core';\n\n// ─── Tracing initialization ──────────────────────────────────────────────────\n\nexport { initSight } from './init.js';\nexport type { InitSightConfig, SightTracerHandle } from './init.js';\nexport { SightSpanExporter } from './exporter.js';\nexport type { SightExporterConfig } from './exporter.js';\n\n// ─── DSN helpers ─────────────────────────────────────────────────────────\n\nexport { parseDsn, buildDsn } from './dsn.js';\nexport type { ParsedDsn } from './dsn.js';\n\n// ─── Non-wrapping observation helper ─────────────────────────────────────\n\nexport { trackSolanaTransaction } from './track.js';\nexport type { TrackTransactionOptions } from './track.js';\n","import { trace, type Tracer } from '@opentelemetry/api';\nimport { SightSpanExporter } from './exporter.js';\n\n// ─── Dynamic imports ────────────────────────────────────────────────────────\n// The OTel SDK packages (@opentelemetry/sdk-trace-node, sdk-trace-base,\n// resources, semantic-conventions) are the source of version-conflict\n// errors when Sentry or another OTel consumer is in the same process.\n// By importing them dynamically inside initSight, the conflict is:\n// (a) confined to the try/catch that handles it\n// (b) deferred until the user actually calls initSight, not at\n// module-load time (which would crash the entire require chain)\n\n// ─── Config ──────────────────────────────────────────────────────────────────\n\nexport interface InitSightConfig {\n /**\n * Sight DSN — one string encoding both the ingest endpoint and the API key.\n *\n * Format: `https://<apiKey>@<host>/ingest`\n *\n * Get yours from the Sight dashboard when you create a project. For local\n * dev against a Docker compose stack: `http://sk_live_xxx@localhost:3001/ingest`\n */\n dsn: string;\n\n /**\n * Human-readable service name that shows up on every span. Pick something\n * that identifies this process — `'swap-bot'`, `'market-maker'`,\n * `'trade-settler'`, etc.\n */\n serviceName: string;\n\n /** Optional version string for the service. Useful for correlating spans with a deploy. */\n serviceVersion?: string;\n\n /** BatchSpanProcessor flush interval, in ms. Defaults to 5s. */\n batchDelayMs?: number;\n\n /** Max spans per HTTP POST. Clamped to 100 by the exporter. */\n maxBatchSize?: number;\n\n /** Inject a fetch implementation for tests. Defaults to global fetch. */\n fetchImpl?: typeof fetch;\n}\n\nexport interface SightTracerHandle {\n /**\n * The OTel Tracer that routes spans through the Sight exporter. Pass\n * this to `InstrumentedConnection({ tracer: sight.tracer })`.\n *\n * Always works — even when another OTel provider (Sentry, Datadog)\n * is in the process. This is the recommended way to wire the connection.\n */\n tracer: Tracer;\n\n /**\n * How the tracer was obtained:\n * - `'own_provider'` — Sight created its own NodeTracerProvider and\n * (optionally) registered it globally. Full control.\n * - `'piggyback'` — Another provider (e.g. Sentry) was already\n * registered. Sight added its exporter as an additional span\n * processor on that provider. Both consumers see all spans.\n * - `'api_only'` — Neither own-provider nor piggyback worked.\n * Sight is using the global `trace.getTracer()` which may or may\n * not route to Sight's exporter depending on what else is registered.\n * This is the least reliable mode.\n */\n mode: 'own_provider' | 'piggyback' | 'api_only';\n\n /** Flush any pending spans and release the batch processor. */\n shutdown: () => Promise<void>;\n}\n\n// ─── Entry point ────────────────────────────────────────────────────────────\n\n/**\n * Initialize Sight tracing. Call this **once** near the top of your\n * process entry point.\n *\n * import { initSight, InstrumentedConnection } from '@thesight/sdk';\n *\n * const sight = initSight({\n * dsn: process.env.SIGHT_DSN!,\n * serviceName: 'swap-bot',\n * });\n *\n * const connection = new InstrumentedConnection(process.env.RPC_URL!, {\n * tracer: sight.tracer,\n * });\n *\n * Handles coexistence with other OTel providers (e.g. @sentry/node)\n * gracefully via a three-tier fallback:\n *\n * 1. **Own provider**: create a NodeTracerProvider, add the Sight\n * exporter, register globally. This is the ideal path.\n * 2. **Piggyback**: if step 1 fails (OTel version conflict with Sentry),\n * find the existing global provider and add the Sight exporter to it\n * as an additional span processor. Both Sentry and Sight see spans.\n * 3. **API-only**: if step 2 also fails, fall back to `trace.getTracer()`\n * from the OTel API layer. Spans may or may not reach Sight depending\n * on what's registered. A warning is logged.\n */\nexport function initSight(config: InitSightConfig): SightTracerHandle {\n if (!config.dsn) {\n throw new Error(\n 'initSight: `dsn` is required. ' +\n 'Get your DSN from the Sight dashboard: https://thesight.dev',\n );\n }\n if (!config.serviceName) {\n throw new Error('initSight: `serviceName` is required');\n }\n\n let _lastTier1Error: unknown = null;\n let _lastTier2Error: unknown = null;\n\n const exporter = new SightSpanExporter({\n dsn: config.dsn,\n fetchImpl: config.fetchImpl,\n maxBatchSize: config.maxBatchSize,\n });\n\n // ── Tier 1: own provider ──────────────────────────────────────────────\n\n try {\n // Use eval('require') to hide these from Next.js webpack / turbopack.\n // Without eval, webpack replaces require() with its own module\n // resolution which can return different exports than Node's native\n // require — causing NodeTracerProvider to be undefined even though\n // the package is installed. This is the same pattern the bankroll-sdk\n // uses for @sentry/node.\n // eslint-disable-next-line no-eval\n const _require = eval('require') as NodeRequire;\n const { NodeTracerProvider } = _require('@opentelemetry/sdk-trace-node');\n const { BatchSpanProcessor } = _require('@opentelemetry/sdk-trace-base');\n const otelResources = _require('@opentelemetry/resources');\n\n const processor = new BatchSpanProcessor(exporter, {\n scheduledDelayMillis: config.batchDelayMs ?? 5_000,\n maxExportBatchSize: config.maxBatchSize ?? 100,\n maxQueueSize: 2048,\n });\n\n // OTel 2.x removed the Resource class in favor of resourceFromAttributes().\n // OTel 1.x has Resource but not resourceFromAttributes.\n // Handle both so the SDK works regardless of which version the host\n // project resolves.\n const resourceAttrs: Record<string, string> = {\n 'service.name': config.serviceName,\n ...(config.serviceVersion ? { 'service.version': config.serviceVersion } : {}),\n };\n const resource = typeof otelResources.resourceFromAttributes === 'function'\n ? otelResources.resourceFromAttributes(resourceAttrs)\n : new otelResources.Resource(resourceAttrs);\n\n // OTel 2.x removed provider.addSpanProcessor() in favor of passing\n // spanProcessors in the constructor. OTel 1.x has addSpanProcessor\n // but not the constructor option. Handle both.\n let provider: any;\n if (typeof NodeTracerProvider.prototype.addSpanProcessor === 'function') {\n // OTel 1.x path\n provider = new NodeTracerProvider({ resource });\n provider.addSpanProcessor(processor);\n } else {\n // OTel 2.x path\n provider = new NodeTracerProvider({ resource, spanProcessors: [processor] });\n }\n\n try {\n provider.register();\n } catch {\n // Registration failed (another provider owns the global), but\n // our standalone provider still works.\n }\n\n return {\n tracer: provider.getTracer('@thesight/sdk'),\n mode: 'own_provider' as const,\n shutdown: async () => { await provider.shutdown(); },\n };\n } catch (tier1Err) {\n _lastTier1Error = tier1Err;\n }\n\n // ── Tier 2: piggyback on the existing global provider ─────────────────\n //\n // If another provider (Sentry) already registered, try adding our\n // exporter as an additional span processor. This makes both Sentry\n // and Sight see the same spans — the ideal coexistence model.\n //\n // Critically, this tier does NOT import from @opentelemetry/sdk-trace-base\n // or sdk-trace-node — those are the version-conflicting modules. Instead\n // it uses a minimal inline span processor that implements the interface\n // via duck-typing. The OTel provider calls processor.onEnd(span) for\n // every finished span; our processor buffers them and calls the Sight\n // exporter. No version-sensitive imports needed.\n\n try {\n const proxy = trace.getTracerProvider() as any;\n const delegate = proxy?.getDelegate?.() ?? proxy;\n\n if (delegate && typeof delegate.addSpanProcessor === 'function') {\n const processor = new SightInlineProcessor(\n exporter,\n config.batchDelayMs ?? 5_000,\n config.maxBatchSize ?? 100,\n );\n delegate.addSpanProcessor(processor);\n return {\n tracer: trace.getTracer('@thesight/sdk'),\n mode: 'piggyback',\n shutdown: async () => { await processor.shutdown(); },\n };\n }\n } catch (tier2Err) {\n _lastTier2Error = tier2Err;\n }\n\n // ── Tier 3: API-only fallback ─────────────────────────────────────────\n\n if (typeof console !== 'undefined') {\n console.warn(\n '[sight] Could not create an OTel tracer provider (tier 1) or ' +\n 'attach to the existing one (tier 2). Sight spans may not reach ingest.',\n );\n if (_lastTier1Error) {\n console.warn('[sight] Tier 1 error:', _lastTier1Error instanceof Error ? _lastTier1Error.message : String(_lastTier1Error));\n }\n if (_lastTier2Error) {\n console.warn('[sight] Tier 2 error:', _lastTier2Error instanceof Error ? _lastTier2Error.message : String(_lastTier2Error));\n }\n }\n\n return {\n tracer: trace.getTracer('@thesight/sdk'),\n mode: 'api_only',\n shutdown: async () => {},\n };\n}\n\n// ─── Inline span processor ─────────────────────────────────────────────────\n//\n// A minimal batch span processor that implements the OTel SpanProcessor\n// interface structurally (duck-typed). It does NOT import from\n// @opentelemetry/sdk-trace-base, which is the module that version-\n// conflicts with Sentry. Instead it implements the four methods the\n// provider calls (onStart, onEnd, shutdown, forceFlush) and drives\n// our SightSpanExporter directly.\n//\n// This class only exists for the tier-2 piggyback path. Tier 1 uses\n// the real BatchSpanProcessor from the OTel SDK (which has nicer\n// retry/backpressure logic); tier 2 uses this inline version because\n// it's the only way to avoid the version conflict.\n\nclass SightInlineProcessor {\n private buffer: unknown[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private readonly maxBatch: number;\n\n constructor(\n private readonly exporter: SightSpanExporter,\n delayMs: number,\n maxBatch: number,\n ) {\n this.maxBatch = maxBatch;\n this.timer = setInterval(() => this.flush(), delayMs);\n // Prevent the timer from keeping the process alive if everything\n // else has exited. Node-specific but safe — `unref` is a no-op in\n // environments that don't have it.\n if (this.timer && typeof (this.timer as any).unref === 'function') {\n (this.timer as any).unref();\n }\n }\n\n /** Called by the provider when a span starts. We don't need it. */\n onStart(): void {}\n\n /** Called by the provider when a span ends. Buffer it for export. */\n onEnd(span: unknown): void {\n this.buffer.push(span);\n if (this.buffer.length >= this.maxBatch) {\n this.flush();\n }\n }\n\n /** Flush pending spans to the exporter. */\n private flush(): void {\n if (this.buffer.length === 0) return;\n const batch = this.buffer.splice(0, this.maxBatch);\n // exporter.export expects ReadableSpan[] — the spans from the\n // provider's onEnd ARE ReadableSpan instances regardless of which\n // sdk-trace-base version created them, because the interface is\n // structurally stable across 1.x versions.\n this.exporter.export(batch as any, () => {});\n }\n\n async shutdown(): Promise<void> {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n this.flush();\n await this.exporter.shutdown();\n }\n\n async forceFlush(): Promise<void> {\n this.flush();\n }\n}\n","import type { SpanAttributes } from '@opentelemetry/api';\nimport type { SpanExporter, ReadableSpan } from '@opentelemetry/sdk-trace-base';\nimport {\n hrTimeToMilliseconds,\n ExportResultCode,\n type ExportResult,\n} from '@opentelemetry/core';\nimport { parseDsn } from './dsn.js';\n\n// ─── Config ──────────────────────────────────────────────────────────────────\n\nexport interface SightExporterConfig {\n /**\n * Sight DSN — one string encoding both the ingest endpoint and the API key.\n * Format: https://<apiKey>@<host>/ingest\n */\n dsn: string;\n /** Inject a fake fetch in tests. Defaults to `globalThis.fetch`. */\n fetchImpl?: typeof fetch;\n /** Max spans per POST. Ingest enforces an upper bound of 100. */\n maxBatchSize?: number;\n}\n\n// ─── Exporter ────────────────────────────────────────────────────────────────\n\n/**\n * An OTel `SpanExporter` that translates OTel spans to Sight's custom\n * ingest payload shape and POSTs them to `/ingest`.\n *\n * Each span becomes one object in the `spans` array. Solana-specific\n * attributes flow through as-is (the set that `InstrumentedConnection`\n * already populates — `solana.tx.signature`, `solana.tx.cu_used`,\n * `solana.tx.program`, etc). The exporter intentionally does not attempt\n * to translate span events or links — the ingest schema doesn't have\n * slots for those, and we prefer dropping silently over guessing.\n *\n * The BatchSpanProcessor upstream handles retries, timeouts, and batching;\n * this exporter stays a thin wire-format translator.\n */\nexport class SightSpanExporter implements SpanExporter {\n private readonly apiKey: string;\n private readonly ingestUrl: string;\n private readonly fetchImpl: typeof fetch;\n private readonly maxBatchSize: number;\n private shuttingDown = false;\n\n constructor(config: SightExporterConfig) {\n const parsed = parseDsn(config.dsn);\n this.apiKey = parsed.apiKey;\n this.ingestUrl = parsed.ingestUrl;\n this.fetchImpl = config.fetchImpl ?? (globalThis.fetch as typeof fetch);\n this.maxBatchSize = Math.min(100, config.maxBatchSize ?? 100);\n\n if (!this.fetchImpl) {\n throw new Error(\n 'SightSpanExporter: globalThis.fetch is not available. Node >= 18 ships ' +\n 'with fetch built in, or pass `fetchImpl` explicitly.',\n );\n }\n }\n\n async export(\n spans: ReadableSpan[],\n resultCallback: (result: ExportResult) => void,\n ): Promise<void> {\n if (this.shuttingDown) {\n resultCallback({ code: ExportResultCode.FAILED, error: new Error('Exporter is shut down') });\n return;\n }\n\n // Split into chunks if the upstream processor handed us a larger\n // batch than ingest will accept. Uncommon with a well-configured\n // BatchSpanProcessor but cheap to handle defensively.\n const chunks: ReadableSpan[][] = [];\n for (let i = 0; i < spans.length; i += this.maxBatchSize) {\n chunks.push(spans.slice(i, i + this.maxBatchSize));\n }\n\n try {\n for (const chunk of chunks) {\n await this.sendChunk(chunk);\n }\n resultCallback({ code: ExportResultCode.SUCCESS });\n } catch (err) {\n resultCallback({\n code: ExportResultCode.FAILED,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n }\n }\n\n async shutdown(): Promise<void> {\n this.shuttingDown = true;\n }\n\n async forceFlush(): Promise<void> {\n // The exporter is stateless — BatchSpanProcessor's own forceFlush\n // drains the queue before calling export(). Nothing to do here.\n }\n\n // ─── Internal ──────────────────────────────────────────────────────────────\n\n private async sendChunk(spans: ReadableSpan[]): Promise<void> {\n const payload = {\n spans: spans.map((span) => this.convertSpan(span)),\n };\n\n const response = await this.fetchImpl(this.ingestUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n const body = await safeReadText(response);\n throw new Error(\n `Sight ingest returned ${response.status} ${response.statusText}: ${body}`,\n );\n }\n }\n\n private convertSpan(span: ReadableSpan): IngestSpan {\n const attr = span.attributes;\n const resource = span.resource.attributes;\n const serviceName = typeof resource['service.name'] === 'string'\n ? resource['service.name']\n : undefined;\n const startTimeMs = hrTimeToMilliseconds(span.startTime);\n const endTimeMs = hrTimeToMilliseconds(span.endTime);\n const durationMs = Math.max(0, endTimeMs - startTimeMs);\n\n const out: IngestSpan = {\n traceId: span.spanContext().traceId,\n spanId: span.spanContext().spanId,\n spanName: span.name,\n startTimeMs,\n durationMs,\n };\n\n const parentSpanId = (span as { parentSpanId?: string }).parentSpanId;\n if (parentSpanId) out.parentSpanId = parentSpanId;\n if (serviceName) out.serviceName = serviceName;\n\n copyIfString(attr, 'solana.tx.signature', out);\n copyIfEnum(attr, 'solana.tx.status', out, ['submitted', 'confirmed', 'failed', 'timeout']);\n copyIfNumber(attr, 'solana.tx.slot', out);\n copyIfNumber(attr, 'solana.tx.cu_used', out);\n copyIfNumber(attr, 'solana.tx.cu_budget', out);\n copyIfNumber(attr, 'solana.tx.cu_utilization', out);\n copyIfString(attr, 'solana.tx.program', out);\n copyIfString(attr, 'solana.tx.instruction', out);\n copyIfString(attr, 'solana.tx.error', out);\n copyIfNumber(attr, 'solana.tx.error_code', out);\n copyIfString(attr, 'solana.tx.error_program', out);\n copyIfNumber(attr, 'solana.tx.submit_ms', out);\n copyIfNumber(attr, 'solana.tx.confirmation_ms', out);\n copyIfNumber(attr, 'solana.tx.fee_lamports', out);\n\n return out;\n }\n}\n\n// ─── Wire format (narrow, matches ingest's zod schema) ──────────────────────\n\ninterface IngestSpan {\n traceId: string;\n spanId: string;\n parentSpanId?: string;\n serviceName?: string;\n spanName: string;\n startTimeMs: number;\n durationMs: number;\n\n 'solana.tx.signature'?: string;\n 'solana.tx.status'?: 'confirmed' | 'failed' | 'timeout';\n 'solana.tx.slot'?: number;\n 'solana.tx.cu_used'?: number;\n 'solana.tx.cu_budget'?: number;\n 'solana.tx.cu_utilization'?: number;\n 'solana.tx.program'?: string;\n 'solana.tx.instruction'?: string;\n 'solana.tx.error'?: string;\n 'solana.tx.error_code'?: number;\n 'solana.tx.error_program'?: string;\n 'solana.tx.submit_ms'?: number;\n 'solana.tx.confirmation_ms'?: number;\n 'solana.tx.fee_lamports'?: number;\n}\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\nfunction copyIfString(\n attr: SpanAttributes,\n key: keyof IngestSpan & string,\n out: IngestSpan,\n): void {\n const v = attr[key];\n if (typeof v === 'string') {\n (out as unknown as Record<string, unknown>)[key] = v;\n }\n}\n\nfunction copyIfNumber(\n attr: SpanAttributes,\n key: keyof IngestSpan & string,\n out: IngestSpan,\n): void {\n const v = attr[key];\n if (typeof v === 'number' && Number.isFinite(v)) {\n (out as unknown as Record<string, unknown>)[key] = v;\n }\n}\n\nfunction copyIfEnum<T extends string>(\n attr: SpanAttributes,\n key: keyof IngestSpan & string,\n out: IngestSpan,\n allowed: readonly T[],\n): void {\n const v = attr[key];\n if (typeof v === 'string' && (allowed as readonly string[]).includes(v)) {\n (out as unknown as Record<string, unknown>)[key] = v;\n }\n}\n\nasync function safeReadText(res: Response): Promise<string> {\n try {\n return (await res.text()).slice(0, 500);\n } catch {\n return '<no body>';\n }\n}\n","/**\n * Parse a Sight DSN into its constituent parts.\n *\n * DSN format:\n * https://<apiKey>@<host>[:<port>]/<path>\n *\n * Examples:\n * https://sk_live_abc123@ingest.thesight.dev/ingest (production)\n * http://sk_live_abc123@localhost:3001/ingest (local dev)\n *\n * The API key sits in the URL's username position. The SDK extracts it\n * and sends it as a Bearer token to the ingest URL (with the key removed\n * from the URL). This mirrors the Sentry DSN model — one string encodes\n * both \"where to send\" and \"who you are\", so there's no separate\n * apiKey + ingestUrl to misconfig independently.\n */\n\nexport interface ParsedDsn {\n /** The API key extracted from the DSN's username position. */\n apiKey: string;\n /** The ingest endpoint URL with the API key stripped out. */\n ingestUrl: string;\n}\n\n/**\n * Parse a DSN string into `{ apiKey, ingestUrl }`.\n *\n * Throws with a clear message on:\n * - Malformed URL\n * - Missing API key (no username in the URL)\n */\nexport function parseDsn(dsn: string): ParsedDsn {\n let url: URL;\n try {\n url = new URL(dsn);\n } catch {\n throw new Error(\n `Invalid Sight DSN: \"${dsn}\". ` +\n 'Expected format: https://<apiKey>@<host>/ingest — ' +\n 'get your DSN from the Sight dashboard when you create a project.',\n );\n }\n\n const apiKey = decodeURIComponent(url.username);\n if (!apiKey) {\n throw new Error(\n `Invalid Sight DSN: no API key found in \"${dsn}\". ` +\n 'The API key goes in the username position: https://sk_live_xxx@host/ingest',\n );\n }\n\n // Strip the credentials so the ingest URL is clean for HTTP requests.\n url.username = '';\n url.password = '';\n const ingestUrl = url.toString().replace(/\\/$/, ''); // trim trailing slash\n\n return { apiKey, ingestUrl };\n}\n\n/**\n * Build a DSN from its parts. Used by the dashboard when generating the\n * DSN for a newly-created project.\n */\nexport function buildDsn(apiKey: string, ingestHost: string): string {\n const url = new URL(ingestHost);\n url.username = apiKey;\n if (!url.pathname || url.pathname === '/') {\n url.pathname = '/ingest';\n }\n return url.toString();\n}\n","import type { Connection, Finality, TransactionSignature, VersionedTransactionResponse } from '@solana/web3.js';\nimport { trace, context, SpanStatusCode, type Tracer } from '@opentelemetry/api';\nimport { parseLogs, IdlResolver, enrichTree, flatAttributions } from '@thesight/core';\nimport type { AnchorIdl } from '@thesight/core';\n\n// ─── Options ──────────────────────────────────────────────────────────────\n\nexport interface TrackTransactionOptions {\n /** The signature returned from a prior `sendRawTransaction` / `sendTransaction` call. */\n signature: TransactionSignature;\n\n /**\n * Any `@solana/web3.js` Connection — plain or instrumented. Used to fetch\n * the on-chain transaction via `getTransaction` for enrichment. Does\n * **not** need to be an `InstrumentedConnection`; this helper intentionally\n * never touches the transaction-send path.\n */\n connection: Connection;\n\n /**\n * Service name used for the span. Falls back to the service name\n * configured by `initSight`. Useful when you want a different service\n * name for a specific subsystem's spans.\n */\n serviceName?: string;\n\n /** OTel tracer override. Defaults to `trace.getTracer('@thesight/sdk')`. */\n tracer?: Tracer;\n\n /** Optional IDL resolver for program/error decoding. A fresh one is built if omitted. */\n idlResolver?: IdlResolver;\n\n /**\n * Pre-registered IDLs to hand to a freshly-built resolver. Ignored when\n * `idlResolver` is provided.\n */\n idls?: Record<string, AnchorIdl>;\n\n /** Finality commitment used for the enrichment fetch. Default 'confirmed'. */\n commitment?: Finality;\n\n /** Max wall-time before the span is ended with status=timeout. Default 30000ms. */\n timeoutMs?: number;\n\n /** Base poll interval for retrying getTransaction. Default 500ms. */\n pollIntervalMs?: number;\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────\n\n/**\n * **Non-wrapping observation helper.** Given a transaction signature that\n * was already sent through any `Connection`, fetch the on-chain record,\n * parse the logs, and emit a single OpenTelemetry span describing the\n * transaction.\n *\n * Unlike `InstrumentedConnection`, this helper **never touches the send\n * path** — it only observes after the fact via `getTransaction(signature)`.\n * Use this when:\n *\n * - You want observability for wallet-adapter flows where the send path\n * goes through the wallet and you can't easily override the Connection\n * - You're security-conscious and don't want any SDK code on the critical\n * transaction path\n * - You want to instrument a handful of high-value transactions rather\n * than every call a Connection makes\n *\n * Usage:\n *\n * import { trackSolanaTransaction } from '@thesight/sdk';\n * import { Connection } from '@solana/web3.js';\n *\n * const connection = new Connection(rpcUrl);\n * const signature = await wallet.sendTransaction(tx, connection);\n *\n * // Fire-and-forget. Span flushes on its own.\n * void trackSolanaTransaction({\n * signature,\n * connection,\n * serviceName: 'checkout',\n * idls: { [programId]: idl },\n * });\n *\n * Returns a `Promise<void>` — typically not awaited, but callers who want\n * to wait for the span to export (e.g. short-lived scripts) can await it.\n */\nexport async function trackSolanaTransaction(opts: TrackTransactionOptions): Promise<void> {\n const tracerName = opts.serviceName ?? '@thesight/sdk';\n const tracer = opts.tracer ?? trace.getTracer(tracerName);\n\n const span = tracer.startSpan('solana.trackTransaction', {}, context.active());\n span.setAttribute('solana.tx.signature', opts.signature);\n\n const start = Date.now();\n const deadline = start + (opts.timeoutMs ?? 30_000);\n const commitment = opts.commitment ?? ('confirmed' as Finality);\n const basePoll = opts.pollIntervalMs ?? 500;\n\n const resolver = opts.idlResolver ?? buildResolverFromIdls(opts.idls);\n\n try {\n const txDetails = await pollGetTransaction(opts.connection, opts.signature, commitment, deadline, basePoll);\n if (!txDetails) {\n span.setAttribute('solana.tx.status', 'timeout');\n span.setAttribute('solana.tx.enrichment_ms', Date.now() - start);\n return;\n }\n\n span.setAttribute('solana.tx.status', txDetails.meta?.err ? 'failed' : 'confirmed');\n span.setAttribute('solana.tx.slot', txDetails.slot);\n\n const fee = txDetails.meta?.fee;\n if (fee !== undefined) span.setAttribute('solana.tx.fee_lamports', fee);\n\n const cuUsed = txDetails.meta?.computeUnitsConsumed;\n if (cuUsed !== undefined && cuUsed !== null) {\n span.setAttribute('solana.tx.cu_used', Number(cuUsed));\n span.setAttribute('solana.tx.cu_budget', 200_000);\n span.setAttribute(\n 'solana.tx.cu_utilization',\n parseFloat((Number(cuUsed) / 200_000 * 100).toFixed(1)),\n );\n }\n\n const logs = txDetails.meta?.logMessages ?? [];\n if (logs.length > 0) {\n const { cpiTree } = parseLogs({ logs });\n await enrichTree(cpiTree, resolver);\n\n for (const attr of flatAttributions(cpiTree)) {\n span.addEvent('cpi.invoke', {\n 'cpi.program': attr.programName ?? attr.programId,\n 'cpi.instruction': attr.instructionName ?? 'unknown',\n 'cpi.depth': attr.depth,\n 'cpi.cu_consumed': attr.cuConsumed,\n 'cpi.cu_self': attr.cuSelf,\n 'cpi.percentage': parseFloat(attr.percentage.toFixed(2)),\n });\n }\n\n const root = cpiTree.roots[0];\n if (root) {\n if (root.programName) span.setAttribute('solana.tx.program', root.programName);\n if (root.instructionName) span.setAttribute('solana.tx.instruction', root.instructionName);\n }\n }\n\n span.setAttribute('solana.tx.enrichment_ms', Date.now() - start);\n\n if (txDetails.meta?.err) {\n span.setStatus({ code: SpanStatusCode.ERROR });\n } else {\n span.setStatus({ code: SpanStatusCode.OK });\n }\n } catch (err) {\n span.recordException(err instanceof Error ? err : new Error(String(err)));\n span.setStatus({\n code: SpanStatusCode.ERROR,\n message: err instanceof Error ? err.message : String(err),\n });\n } finally {\n span.end();\n }\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────\n\nfunction buildResolverFromIdls(idls?: Record<string, AnchorIdl>): IdlResolver {\n const resolver = new IdlResolver();\n if (idls) resolver.registerMany(idls);\n return resolver;\n}\n\nasync function pollGetTransaction(\n connection: Connection,\n signature: TransactionSignature,\n commitment: Finality,\n deadline: number,\n basePollMs: number,\n): Promise<VersionedTransactionResponse | null> {\n let attempt = 0;\n while (Date.now() < deadline) {\n try {\n const tx = await connection.getTransaction(signature, {\n commitment,\n maxSupportedTransactionVersion: 0,\n });\n if (tx) return tx;\n } catch {\n // Ignore — retry with backoff\n }\n attempt++;\n const waitMs = Math.min(basePollMs * Math.pow(1.5, attempt - 1), 2_000);\n await new Promise((resolve) => setTimeout(resolve, waitMs));\n }\n return null;\n}\n"],"mappings":"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","names":["trace","context","SpanStatusCode","parseLogs","IdlResolver","enrichTree","flatAttributions","config","resource","processor","exporter","trace","config","IdlResolver","trace","context","SpanStatusCode","parseLogs","enrichTree","flatAttributions"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thesight/sdk",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "description": "Sight SDK — OpenTelemetry-native observability for Solana. One call (initSight) wires a NodeTracerProvider + Sight exporter, then InstrumentedConnection turns every transaction into a span with per-CPI compute-unit attribution, decoded Anchor errors, and wallet-to-program correlation.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",