@thesight/sdk 0.3.8 → 0.4.0

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
@@ -20,7 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- IdlResolver: () => import_core4.IdlResolver,
23
+ IdlResolver: () => import_core5.IdlResolver,
24
24
  InstrumentedConnection: () => InstrumentedConnection,
25
25
  SightSpanExporter: () => SightSpanExporter,
26
26
  buildDsn: () => buildDsn,
@@ -30,15 +30,93 @@ __export(index_exports, {
30
30
  });
31
31
  module.exports = __toCommonJS(index_exports);
32
32
  var import_web3 = require("@solana/web3.js");
33
- var import_api3 = require("@opentelemetry/api");
34
- var import_core3 = require("@thesight/core");
33
+ var import_api4 = require("@opentelemetry/api");
35
34
  var import_core4 = require("@thesight/core");
36
35
 
37
- // src/init.ts
36
+ // src/enrichment.ts
38
37
  var import_api = require("@opentelemetry/api");
38
+ var import_core = require("@thesight/core");
39
+ var SOLANA_CU_BUDGET = 2e5;
40
+ var DEFAULT_ENRICHMENT_TIMEOUT_MS = 3e4;
41
+ var DEFAULT_ENRICHMENT_POLL_MS = 500;
42
+ async function pollForTransaction(getTransaction, signature, commitment, deadline, basePollMs) {
43
+ let attempt = 0;
44
+ while (Date.now() < deadline) {
45
+ try {
46
+ const tx = await getTransaction(signature, {
47
+ commitment,
48
+ maxSupportedTransactionVersion: 0
49
+ });
50
+ if (tx) return tx;
51
+ } catch {
52
+ }
53
+ attempt++;
54
+ const waitMs = Math.min(basePollMs * Math.pow(1.5, attempt - 1), 2e3);
55
+ await sleep(waitMs);
56
+ }
57
+ return null;
58
+ }
59
+ function attachTxDetailsToSpan(span, txDetails) {
60
+ span.setAttribute("solana.tx.status", txDetails.meta?.err ? "failed" : "confirmed");
61
+ span.setAttribute("solana.tx.slot", txDetails.slot);
62
+ const fee = txDetails.meta?.fee;
63
+ if (fee !== void 0) span.setAttribute("solana.tx.fee_lamports", fee);
64
+ const cuUsed = txDetails.meta?.computeUnitsConsumed;
65
+ if (cuUsed !== void 0 && cuUsed !== null) {
66
+ span.setAttribute("solana.tx.cu_used", Number(cuUsed));
67
+ span.setAttribute("solana.tx.cu_budget", SOLANA_CU_BUDGET);
68
+ span.setAttribute(
69
+ "solana.tx.cu_utilization",
70
+ parseFloat((Number(cuUsed) / SOLANA_CU_BUDGET * 100).toFixed(1))
71
+ );
72
+ }
73
+ }
74
+ async function attachParsedLogsToSpan(span, logs, resolver) {
75
+ const { cpiTree } = (0, import_core.parseLogs)({ logs });
76
+ await (0, import_core.enrichTree)(cpiTree, resolver);
77
+ const attributions = (0, import_core.flatAttributions)(cpiTree);
78
+ for (const attr of attributions) {
79
+ span.addEvent("cpi.invoke", {
80
+ "cpi.program": attr.programName ?? attr.programId,
81
+ "cpi.instruction": attr.instructionName ?? "unknown",
82
+ "cpi.depth": attr.depth,
83
+ "cpi.cu_consumed": attr.cuConsumed,
84
+ "cpi.cu_self": attr.cuSelf,
85
+ "cpi.percentage": parseFloat(attr.percentage.toFixed(2))
86
+ });
87
+ }
88
+ const root = cpiTree.roots[0];
89
+ if (root) {
90
+ if (root.programName) span.setAttribute("solana.tx.program", root.programName);
91
+ if (root.instructionName) span.setAttribute("solana.tx.instruction", root.instructionName);
92
+ }
93
+ return cpiTree;
94
+ }
95
+ function finalizeSpan(span, txDetails, startTime) {
96
+ if (!txDetails) {
97
+ span.setAttribute("solana.tx.status", "timeout");
98
+ span.setAttribute("solana.tx.enrichment_ms", Date.now() - startTime);
99
+ return;
100
+ }
101
+ span.setAttribute("solana.tx.enrichment_ms", Date.now() - startTime);
102
+ if (txDetails.meta?.err) {
103
+ span.setStatus({ code: import_api.SpanStatusCode.ERROR });
104
+ } else {
105
+ span.setStatus({ code: import_api.SpanStatusCode.OK });
106
+ }
107
+ }
108
+ function sleep(ms) {
109
+ return new Promise((resolve) => setTimeout(resolve, ms));
110
+ }
111
+
112
+ // src/index.ts
113
+ var import_core5 = require("@thesight/core");
114
+
115
+ // src/init.ts
116
+ var import_api2 = require("@opentelemetry/api");
39
117
 
40
118
  // src/exporter.ts
41
- var import_core = require("@opentelemetry/core");
119
+ var import_core2 = require("@opentelemetry/core");
42
120
 
43
121
  // src/dsn.ts
44
122
  function parseDsn(dsn) {
@@ -91,7 +169,7 @@ var SightSpanExporter = class {
91
169
  }
92
170
  async export(spans, resultCallback) {
93
171
  if (this.shuttingDown) {
94
- resultCallback({ code: import_core.ExportResultCode.FAILED, error: new Error("Exporter is shut down") });
172
+ resultCallback({ code: import_core2.ExportResultCode.FAILED, error: new Error("Exporter is shut down") });
95
173
  return;
96
174
  }
97
175
  const chunks = [];
@@ -102,10 +180,10 @@ var SightSpanExporter = class {
102
180
  for (const chunk of chunks) {
103
181
  await this.sendChunk(chunk);
104
182
  }
105
- resultCallback({ code: import_core.ExportResultCode.SUCCESS });
183
+ resultCallback({ code: import_core2.ExportResultCode.SUCCESS });
106
184
  } catch (err) {
107
185
  resultCallback({
108
- code: import_core.ExportResultCode.FAILED,
186
+ code: import_core2.ExportResultCode.FAILED,
109
187
  error: err instanceof Error ? err : new Error(String(err))
110
188
  });
111
189
  }
@@ -139,8 +217,8 @@ var SightSpanExporter = class {
139
217
  const attr = span.attributes;
140
218
  const resource2 = span.resource.attributes;
141
219
  const serviceName = typeof resource2["service.name"] === "string" ? resource2["service.name"] : void 0;
142
- const startTimeMs = (0, import_core.hrTimeToMilliseconds)(span.startTime);
143
- const endTimeMs = (0, import_core.hrTimeToMilliseconds)(span.endTime);
220
+ const startTimeMs = (0, import_core2.hrTimeToMilliseconds)(span.startTime);
221
+ const endTimeMs = (0, import_core2.hrTimeToMilliseconds)(span.endTime);
144
222
  const durationMs = Math.max(0, endTimeMs - startTimeMs);
145
223
  const out = {
146
224
  traceId: span.spanContext().traceId,
@@ -250,7 +328,7 @@ function initSight(config) {
250
328
  _lastTier1Error = tier1Err;
251
329
  }
252
330
  try {
253
- const proxy = import_api.trace.getTracerProvider();
331
+ const proxy = import_api2.trace.getTracerProvider();
254
332
  const delegate = proxy?.getDelegate?.() ?? proxy;
255
333
  if (delegate && typeof delegate.addSpanProcessor === "function") {
256
334
  const processor2 = new SightInlineProcessor(
@@ -260,7 +338,7 @@ function initSight(config) {
260
338
  );
261
339
  delegate.addSpanProcessor(processor2);
262
340
  return {
263
- tracer: import_api.trace.getTracer("@thesight/sdk"),
341
+ tracer: import_api2.trace.getTracer("@thesight/sdk"),
264
342
  mode: "piggyback",
265
343
  shutdown: async () => {
266
344
  await processor2.shutdown();
@@ -282,7 +360,7 @@ function initSight(config) {
282
360
  }
283
361
  }
284
362
  return {
285
- tracer: import_api.trace.getTracer("@thesight/sdk"),
363
+ tracer: import_api2.trace.getTracer("@thesight/sdk"),
286
364
  mode: "api_only",
287
365
  shutdown: async () => {
288
366
  }
@@ -332,68 +410,40 @@ var SightInlineProcessor = class {
332
410
  };
333
411
 
334
412
  // src/track.ts
335
- var import_api2 = require("@opentelemetry/api");
336
- var import_core2 = require("@thesight/core");
413
+ var import_api3 = require("@opentelemetry/api");
414
+ var import_core3 = require("@thesight/core");
337
415
  async function trackSolanaTransaction(opts) {
338
416
  const tracerName = opts.serviceName ?? "@thesight/sdk";
339
- const tracer = opts.tracer ?? import_api2.trace.getTracer(tracerName);
340
- const span = tracer.startSpan("solana.trackTransaction", {}, import_api2.context.active());
417
+ const tracer = opts.tracer ?? import_api3.trace.getTracer(tracerName);
418
+ const span = tracer.startSpan("solana.trackTransaction", {}, import_api3.context.active());
341
419
  span.setAttribute("solana.tx.signature", opts.signature);
342
420
  const start = Date.now();
343
- const deadline = start + (opts.timeoutMs ?? 3e4);
421
+ const deadline = start + (opts.timeoutMs ?? DEFAULT_ENRICHMENT_TIMEOUT_MS);
344
422
  const commitment = opts.commitment ?? "confirmed";
345
- const basePoll = opts.pollIntervalMs ?? 500;
423
+ const basePoll = opts.pollIntervalMs ?? DEFAULT_ENRICHMENT_POLL_MS;
346
424
  const resolver = opts.idlResolver ?? buildResolverFromIdls(opts.idls);
347
425
  try {
348
- const txDetails = await pollGetTransaction(opts.connection, opts.signature, commitment, deadline, basePoll);
426
+ const txDetails = await pollForTransaction(
427
+ (sig, pollOpts) => opts.connection.getTransaction(sig, pollOpts),
428
+ opts.signature,
429
+ commitment,
430
+ deadline,
431
+ basePoll
432
+ );
349
433
  if (!txDetails) {
350
- span.setAttribute("solana.tx.status", "timeout");
351
- span.setAttribute("solana.tx.enrichment_ms", Date.now() - start);
434
+ finalizeSpan(span, null, start);
352
435
  return;
353
436
  }
354
- span.setAttribute("solana.tx.status", txDetails.meta?.err ? "failed" : "confirmed");
355
- span.setAttribute("solana.tx.slot", txDetails.slot);
356
- const fee = txDetails.meta?.fee;
357
- if (fee !== void 0) span.setAttribute("solana.tx.fee_lamports", fee);
358
- const cuUsed = txDetails.meta?.computeUnitsConsumed;
359
- if (cuUsed !== void 0 && cuUsed !== null) {
360
- span.setAttribute("solana.tx.cu_used", Number(cuUsed));
361
- span.setAttribute("solana.tx.cu_budget", 2e5);
362
- span.setAttribute(
363
- "solana.tx.cu_utilization",
364
- parseFloat((Number(cuUsed) / 2e5 * 100).toFixed(1))
365
- );
366
- }
437
+ attachTxDetailsToSpan(span, txDetails);
367
438
  const logs = txDetails.meta?.logMessages ?? [];
368
439
  if (logs.length > 0) {
369
- const { cpiTree } = (0, import_core2.parseLogs)({ logs });
370
- await (0, import_core2.enrichTree)(cpiTree, resolver);
371
- for (const attr of (0, import_core2.flatAttributions)(cpiTree)) {
372
- span.addEvent("cpi.invoke", {
373
- "cpi.program": attr.programName ?? attr.programId,
374
- "cpi.instruction": attr.instructionName ?? "unknown",
375
- "cpi.depth": attr.depth,
376
- "cpi.cu_consumed": attr.cuConsumed,
377
- "cpi.cu_self": attr.cuSelf,
378
- "cpi.percentage": parseFloat(attr.percentage.toFixed(2))
379
- });
380
- }
381
- const root = cpiTree.roots[0];
382
- if (root) {
383
- if (root.programName) span.setAttribute("solana.tx.program", root.programName);
384
- if (root.instructionName) span.setAttribute("solana.tx.instruction", root.instructionName);
385
- }
386
- }
387
- span.setAttribute("solana.tx.enrichment_ms", Date.now() - start);
388
- if (txDetails.meta?.err) {
389
- span.setStatus({ code: import_api2.SpanStatusCode.ERROR });
390
- } else {
391
- span.setStatus({ code: import_api2.SpanStatusCode.OK });
440
+ await attachParsedLogsToSpan(span, logs, resolver);
392
441
  }
442
+ finalizeSpan(span, txDetails, start);
393
443
  } catch (err) {
394
444
  span.recordException(err instanceof Error ? err : new Error(String(err)));
395
445
  span.setStatus({
396
- code: import_api2.SpanStatusCode.ERROR,
446
+ code: import_api3.SpanStatusCode.ERROR,
397
447
  message: err instanceof Error ? err.message : String(err)
398
448
  });
399
449
  } finally {
@@ -401,27 +451,10 @@ async function trackSolanaTransaction(opts) {
401
451
  }
402
452
  }
403
453
  function buildResolverFromIdls(idls) {
404
- const resolver = new import_core2.IdlResolver();
454
+ const resolver = new import_core3.IdlResolver();
405
455
  if (idls) resolver.registerMany(idls);
406
456
  return resolver;
407
457
  }
408
- async function pollGetTransaction(connection, signature, commitment, deadline, basePollMs) {
409
- let attempt = 0;
410
- while (Date.now() < deadline) {
411
- try {
412
- const tx = await connection.getTransaction(signature, {
413
- commitment,
414
- maxSupportedTransactionVersion: 0
415
- });
416
- if (tx) return tx;
417
- } catch {
418
- }
419
- attempt++;
420
- const waitMs = Math.min(basePollMs * Math.pow(1.5, attempt - 1), 2e3);
421
- await new Promise((resolve) => setTimeout(resolve, waitMs));
422
- }
423
- return null;
424
- }
425
458
 
426
459
  // src/index.ts
427
460
  var InstrumentedConnection = class extends import_web3.Connection {
@@ -438,7 +471,9 @@ var InstrumentedConnection = class extends import_web3.Connection {
438
471
  enrichmentTimeoutMs,
439
472
  enrichmentPollIntervalMs,
440
473
  disableAutoSpan,
441
- commitment,
474
+ // Note: commitment is intentionally NOT destructured out. It must
475
+ // pass through to super() via connectionConfig so the parent
476
+ // Connection uses it for RPC calls (preflight, blockhash, etc.).
442
477
  ...connectionConfig
443
478
  } = config2 ?? {};
444
479
  super(endpoint, connectionConfig);
@@ -447,17 +482,17 @@ var InstrumentedConnection = class extends import_web3.Connection {
447
482
  idlRpcEndpoint,
448
483
  skipIdlResolution,
449
484
  allowOnChainIdlFetch,
450
- enrichmentTimeoutMs: enrichmentTimeoutMs ?? 3e4,
451
- enrichmentPollIntervalMs: enrichmentPollIntervalMs ?? 500,
485
+ enrichmentTimeoutMs: enrichmentTimeoutMs ?? DEFAULT_ENRICHMENT_TIMEOUT_MS,
486
+ enrichmentPollIntervalMs: enrichmentPollIntervalMs ?? DEFAULT_ENRICHMENT_POLL_MS,
452
487
  disableAutoSpan,
453
- commitment
488
+ commitment: config2?.commitment
454
489
  };
455
- this.idlResolver = new import_core3.IdlResolver({
490
+ this.idlResolver = new import_core4.IdlResolver({
456
491
  rpcEndpoint: idlRpcEndpoint ?? endpoint,
457
492
  allowOnChainFetch: allowOnChainIdlFetch ?? false
458
493
  });
459
494
  if (idls) this.idlResolver.registerMany(idls);
460
- this.tracer = tracer ?? import_api3.trace.getTracer("@thesight/sdk");
495
+ this.tracer = tracer ?? import_api4.trace.getTracer("@thesight/sdk");
461
496
  }
462
497
  /**
463
498
  * Register an IDL for a single program. Convenience forwarder for the
@@ -489,7 +524,7 @@ var InstrumentedConnection = class extends import_web3.Connection {
489
524
  if (this.sightConfig.disableAutoSpan) {
490
525
  return super.sendRawTransaction(rawTransaction, options);
491
526
  }
492
- const span = this.tracer.startSpan("solana.sendRawTransaction", {}, import_api3.context.active());
527
+ const span = this.tracer.startSpan("solana.sendRawTransaction", {}, import_api4.context.active());
493
528
  const submitStart = Date.now();
494
529
  let signature;
495
530
  try {
@@ -500,7 +535,7 @@ var InstrumentedConnection = class extends import_web3.Connection {
500
535
  span.setAttribute("solana.tx.status", "failed");
501
536
  span.recordException(err instanceof Error ? err : new Error(String(err)));
502
537
  span.setStatus({
503
- code: import_api3.SpanStatusCode.ERROR,
538
+ code: import_api4.SpanStatusCode.ERROR,
504
539
  message: err instanceof Error ? err.message : String(err)
505
540
  });
506
541
  span.end();
@@ -524,30 +559,30 @@ var InstrumentedConnection = class extends import_web3.Connection {
524
559
  */
525
560
  async enrichSpanInBackground(span, signature, submitStart) {
526
561
  const enrichStart = Date.now();
527
- const deadline = enrichStart + (this.sightConfig.enrichmentTimeoutMs ?? 3e4);
562
+ const deadline = enrichStart + (this.sightConfig.enrichmentTimeoutMs ?? DEFAULT_ENRICHMENT_TIMEOUT_MS);
528
563
  const commitment = this.sightConfig.commitment ?? "confirmed";
529
- const basePollMs = this.sightConfig.enrichmentPollIntervalMs ?? 500;
564
+ const basePollMs = this.sightConfig.enrichmentPollIntervalMs ?? DEFAULT_ENRICHMENT_POLL_MS;
530
565
  try {
531
- const txDetails = await this.pollForTransaction(signature, commitment, deadline, basePollMs);
566
+ const txDetails = await pollForTransaction(
567
+ (sig, opts) => super.getTransaction(sig, opts),
568
+ signature,
569
+ commitment,
570
+ deadline,
571
+ basePollMs
572
+ );
532
573
  if (!txDetails) {
533
- span.setAttribute("solana.tx.status", "timeout");
534
- span.setAttribute("solana.tx.enrichment_ms", Date.now() - submitStart);
574
+ finalizeSpan(span, null, submitStart);
535
575
  span.end();
536
576
  return;
537
577
  }
538
- this.attachTxDetailsToSpan(span, txDetails);
578
+ attachTxDetailsToSpan(span, txDetails);
539
579
  if (!this.sightConfig.skipIdlResolution) {
540
580
  const logs = txDetails.meta?.logMessages ?? [];
541
581
  if (logs.length > 0) {
542
- await this.attachParsedLogsToSpan(span, logs);
582
+ await attachParsedLogsToSpan(span, logs, this.idlResolver);
543
583
  }
544
584
  }
545
- span.setAttribute("solana.tx.enrichment_ms", Date.now() - submitStart);
546
- if (txDetails.meta?.err) {
547
- span.setStatus({ code: import_api3.SpanStatusCode.ERROR });
548
- } else {
549
- span.setStatus({ code: import_api3.SpanStatusCode.OK });
550
- }
585
+ finalizeSpan(span, txDetails, submitStart);
551
586
  } catch (err) {
552
587
  span.recordException(err instanceof Error ? err : new Error(String(err)));
553
588
  span.setAttribute(
@@ -558,75 +593,7 @@ var InstrumentedConnection = class extends import_web3.Connection {
558
593
  span.end();
559
594
  }
560
595
  }
561
- /**
562
- * Poll `getTransaction(signature)` until either the on-chain record is
563
- * returned or the deadline passes. Exponential backoff (1.5x) capped at
564
- * 2 seconds to balance responsiveness against RPC load.
565
- */
566
- async pollForTransaction(signature, commitment, deadline, basePollMs) {
567
- let attempt = 0;
568
- while (Date.now() < deadline) {
569
- try {
570
- const tx = await super.getTransaction(signature, {
571
- commitment,
572
- maxSupportedTransactionVersion: 0
573
- });
574
- if (tx) return tx;
575
- } catch {
576
- }
577
- attempt++;
578
- const waitMs = Math.min(basePollMs * Math.pow(1.5, attempt - 1), 2e3);
579
- await sleep(waitMs);
580
- }
581
- return null;
582
- }
583
- /** Attach the flat fields (slot, fee, CU, status) from a tx response to a span. */
584
- attachTxDetailsToSpan(span, txDetails) {
585
- span.setAttribute("solana.tx.status", txDetails.meta?.err ? "failed" : "confirmed");
586
- span.setAttribute("solana.tx.slot", txDetails.slot);
587
- const fee = txDetails.meta?.fee;
588
- if (fee !== void 0) span.setAttribute("solana.tx.fee_lamports", fee);
589
- const cuUsed = txDetails.meta?.computeUnitsConsumed;
590
- if (cuUsed !== void 0 && cuUsed !== null) {
591
- span.setAttribute("solana.tx.cu_used", Number(cuUsed));
592
- span.setAttribute("solana.tx.cu_budget", 2e5);
593
- span.setAttribute(
594
- "solana.tx.cu_utilization",
595
- parseFloat((Number(cuUsed) / 2e5 * 100).toFixed(1))
596
- );
597
- }
598
- }
599
- /**
600
- * Parse program logs into a CPI tree, enrich with registered IDLs, and
601
- * emit one `cpi.invoke` event per invocation onto the span. Root
602
- * program/instruction names are copied onto span attributes so dashboards
603
- * can filter by them without walking events.
604
- */
605
- async attachParsedLogsToSpan(span, logs) {
606
- const { cpiTree } = (0, import_core3.parseLogs)({ logs });
607
- await (0, import_core3.enrichTree)(cpiTree, this.idlResolver);
608
- const attributions = (0, import_core3.flatAttributions)(cpiTree);
609
- for (const attr of attributions) {
610
- span.addEvent("cpi.invoke", {
611
- "cpi.program": attr.programName ?? attr.programId,
612
- "cpi.instruction": attr.instructionName ?? "unknown",
613
- "cpi.depth": attr.depth,
614
- "cpi.cu_consumed": attr.cuConsumed,
615
- "cpi.cu_self": attr.cuSelf,
616
- "cpi.percentage": parseFloat(attr.percentage.toFixed(2))
617
- });
618
- }
619
- const root = cpiTree.roots[0];
620
- if (root) {
621
- if (root.programName) span.setAttribute("solana.tx.program", root.programName);
622
- if (root.instructionName) span.setAttribute("solana.tx.instruction", root.instructionName);
623
- }
624
- return cpiTree;
625
- }
626
596
  };
627
- function sleep(ms) {
628
- return new Promise((resolve) => setTimeout(resolve, ms));
629
- }
630
597
  // Annotate the CommonJS export names for ESM import in node:
631
598
  0 && (module.exports = {
632
599
  IdlResolver,