@thesight/sdk 0.3.7 → 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 +139 -171
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -17
- package/dist/index.d.ts +2 -17
- package/dist/index.js +125 -157
- package/dist/index.js.map +1 -1
- package/package.json +5 -10
package/dist/index.js
CHANGED
|
@@ -5,11 +5,89 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
trace as trace3,
|
|
7
7
|
context as context2,
|
|
8
|
-
SpanStatusCode as
|
|
8
|
+
SpanStatusCode as SpanStatusCode3
|
|
9
9
|
} from "@opentelemetry/api";
|
|
10
|
-
import { parseLogs as parseLogs2, IdlResolver as IdlResolver2, enrichTree as enrichTree2, flatAttributions as flatAttributions2 } from "@thesight/core";
|
|
11
10
|
import { IdlResolver as IdlResolver3 } from "@thesight/core";
|
|
12
11
|
|
|
12
|
+
// src/enrichment.ts
|
|
13
|
+
import { SpanStatusCode } from "@opentelemetry/api";
|
|
14
|
+
import { parseLogs, enrichTree, flatAttributions } from "@thesight/core";
|
|
15
|
+
var SOLANA_CU_BUDGET = 2e5;
|
|
16
|
+
var DEFAULT_ENRICHMENT_TIMEOUT_MS = 3e4;
|
|
17
|
+
var DEFAULT_ENRICHMENT_POLL_MS = 500;
|
|
18
|
+
async function pollForTransaction(getTransaction, signature, commitment, deadline, basePollMs) {
|
|
19
|
+
let attempt = 0;
|
|
20
|
+
while (Date.now() < deadline) {
|
|
21
|
+
try {
|
|
22
|
+
const tx = await getTransaction(signature, {
|
|
23
|
+
commitment,
|
|
24
|
+
maxSupportedTransactionVersion: 0
|
|
25
|
+
});
|
|
26
|
+
if (tx) return tx;
|
|
27
|
+
} catch {
|
|
28
|
+
}
|
|
29
|
+
attempt++;
|
|
30
|
+
const waitMs = Math.min(basePollMs * Math.pow(1.5, attempt - 1), 2e3);
|
|
31
|
+
await sleep(waitMs);
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
function attachTxDetailsToSpan(span, txDetails) {
|
|
36
|
+
span.setAttribute("solana.tx.status", txDetails.meta?.err ? "failed" : "confirmed");
|
|
37
|
+
span.setAttribute("solana.tx.slot", txDetails.slot);
|
|
38
|
+
const fee = txDetails.meta?.fee;
|
|
39
|
+
if (fee !== void 0) span.setAttribute("solana.tx.fee_lamports", fee);
|
|
40
|
+
const cuUsed = txDetails.meta?.computeUnitsConsumed;
|
|
41
|
+
if (cuUsed !== void 0 && cuUsed !== null) {
|
|
42
|
+
span.setAttribute("solana.tx.cu_used", Number(cuUsed));
|
|
43
|
+
span.setAttribute("solana.tx.cu_budget", SOLANA_CU_BUDGET);
|
|
44
|
+
span.setAttribute(
|
|
45
|
+
"solana.tx.cu_utilization",
|
|
46
|
+
parseFloat((Number(cuUsed) / SOLANA_CU_BUDGET * 100).toFixed(1))
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async function attachParsedLogsToSpan(span, logs, resolver) {
|
|
51
|
+
const { cpiTree } = parseLogs({ logs });
|
|
52
|
+
await enrichTree(cpiTree, resolver);
|
|
53
|
+
const attributions = flatAttributions(cpiTree);
|
|
54
|
+
for (const attr of attributions) {
|
|
55
|
+
span.addEvent("cpi.invoke", {
|
|
56
|
+
"cpi.program": attr.programName ?? attr.programId,
|
|
57
|
+
"cpi.instruction": attr.instructionName ?? "unknown",
|
|
58
|
+
"cpi.depth": attr.depth,
|
|
59
|
+
"cpi.cu_consumed": attr.cuConsumed,
|
|
60
|
+
"cpi.cu_self": attr.cuSelf,
|
|
61
|
+
"cpi.percentage": parseFloat(attr.percentage.toFixed(2))
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
const root = cpiTree.roots[0];
|
|
65
|
+
if (root) {
|
|
66
|
+
if (root.programName) span.setAttribute("solana.tx.program", root.programName);
|
|
67
|
+
if (root.instructionName) span.setAttribute("solana.tx.instruction", root.instructionName);
|
|
68
|
+
}
|
|
69
|
+
return cpiTree;
|
|
70
|
+
}
|
|
71
|
+
function finalizeSpan(span, txDetails, startTime) {
|
|
72
|
+
if (!txDetails) {
|
|
73
|
+
span.setAttribute("solana.tx.status", "timeout");
|
|
74
|
+
span.setAttribute("solana.tx.enrichment_ms", Date.now() - startTime);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
span.setAttribute("solana.tx.enrichment_ms", Date.now() - startTime);
|
|
78
|
+
if (txDetails.meta?.err) {
|
|
79
|
+
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
80
|
+
} else {
|
|
81
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function sleep(ms) {
|
|
85
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/index.ts
|
|
89
|
+
import { IdlResolver as IdlResolver4 } from "@thesight/core";
|
|
90
|
+
|
|
13
91
|
// src/init.ts
|
|
14
92
|
import { trace } from "@opentelemetry/api";
|
|
15
93
|
|
|
@@ -193,8 +271,9 @@ function initSight(config) {
|
|
|
193
271
|
});
|
|
194
272
|
try {
|
|
195
273
|
const _require = eval("require");
|
|
196
|
-
const
|
|
197
|
-
const
|
|
274
|
+
const traceBase = _require("@opentelemetry/sdk-trace-base");
|
|
275
|
+
const TracerProvider = traceBase.TracerProvider ?? traceBase.BasicTracerProvider;
|
|
276
|
+
const BatchSpanProcessor = traceBase.BatchSpanProcessor;
|
|
198
277
|
const otelResources = _require("@opentelemetry/resources");
|
|
199
278
|
const processor = new BatchSpanProcessor(exporter, {
|
|
200
279
|
scheduledDelayMillis: config.batchDelayMs ?? 5e3,
|
|
@@ -207,11 +286,11 @@ function initSight(config) {
|
|
|
207
286
|
};
|
|
208
287
|
const resource = typeof otelResources.resourceFromAttributes === "function" ? otelResources.resourceFromAttributes(resourceAttrs) : new otelResources.Resource(resourceAttrs);
|
|
209
288
|
let provider;
|
|
210
|
-
if (typeof
|
|
211
|
-
provider = new
|
|
289
|
+
if (typeof TracerProvider.prototype.addSpanProcessor === "function") {
|
|
290
|
+
provider = new TracerProvider({ resource });
|
|
212
291
|
provider.addSpanProcessor(processor);
|
|
213
292
|
} else {
|
|
214
|
-
provider = new
|
|
293
|
+
provider = new TracerProvider({ resource, spanProcessors: [processor] });
|
|
215
294
|
}
|
|
216
295
|
try {
|
|
217
296
|
provider.register();
|
|
@@ -310,68 +389,40 @@ var SightInlineProcessor = class {
|
|
|
310
389
|
};
|
|
311
390
|
|
|
312
391
|
// src/track.ts
|
|
313
|
-
import { trace as trace2, context, SpanStatusCode } from "@opentelemetry/api";
|
|
314
|
-
import {
|
|
392
|
+
import { trace as trace2, context, SpanStatusCode as SpanStatusCode2 } from "@opentelemetry/api";
|
|
393
|
+
import { IdlResolver as IdlResolver2 } from "@thesight/core";
|
|
315
394
|
async function trackSolanaTransaction(opts) {
|
|
316
395
|
const tracerName = opts.serviceName ?? "@thesight/sdk";
|
|
317
396
|
const tracer = opts.tracer ?? trace2.getTracer(tracerName);
|
|
318
397
|
const span = tracer.startSpan("solana.trackTransaction", {}, context.active());
|
|
319
398
|
span.setAttribute("solana.tx.signature", opts.signature);
|
|
320
399
|
const start = Date.now();
|
|
321
|
-
const deadline = start + (opts.timeoutMs ??
|
|
400
|
+
const deadline = start + (opts.timeoutMs ?? DEFAULT_ENRICHMENT_TIMEOUT_MS);
|
|
322
401
|
const commitment = opts.commitment ?? "confirmed";
|
|
323
|
-
const basePoll = opts.pollIntervalMs ??
|
|
402
|
+
const basePoll = opts.pollIntervalMs ?? DEFAULT_ENRICHMENT_POLL_MS;
|
|
324
403
|
const resolver = opts.idlResolver ?? buildResolverFromIdls(opts.idls);
|
|
325
404
|
try {
|
|
326
|
-
const txDetails = await
|
|
405
|
+
const txDetails = await pollForTransaction(
|
|
406
|
+
(sig, pollOpts) => opts.connection.getTransaction(sig, pollOpts),
|
|
407
|
+
opts.signature,
|
|
408
|
+
commitment,
|
|
409
|
+
deadline,
|
|
410
|
+
basePoll
|
|
411
|
+
);
|
|
327
412
|
if (!txDetails) {
|
|
328
|
-
span
|
|
329
|
-
span.setAttribute("solana.tx.enrichment_ms", Date.now() - start);
|
|
413
|
+
finalizeSpan(span, null, start);
|
|
330
414
|
return;
|
|
331
415
|
}
|
|
332
|
-
span
|
|
333
|
-
span.setAttribute("solana.tx.slot", txDetails.slot);
|
|
334
|
-
const fee = txDetails.meta?.fee;
|
|
335
|
-
if (fee !== void 0) span.setAttribute("solana.tx.fee_lamports", fee);
|
|
336
|
-
const cuUsed = txDetails.meta?.computeUnitsConsumed;
|
|
337
|
-
if (cuUsed !== void 0 && cuUsed !== null) {
|
|
338
|
-
span.setAttribute("solana.tx.cu_used", Number(cuUsed));
|
|
339
|
-
span.setAttribute("solana.tx.cu_budget", 2e5);
|
|
340
|
-
span.setAttribute(
|
|
341
|
-
"solana.tx.cu_utilization",
|
|
342
|
-
parseFloat((Number(cuUsed) / 2e5 * 100).toFixed(1))
|
|
343
|
-
);
|
|
344
|
-
}
|
|
416
|
+
attachTxDetailsToSpan(span, txDetails);
|
|
345
417
|
const logs = txDetails.meta?.logMessages ?? [];
|
|
346
418
|
if (logs.length > 0) {
|
|
347
|
-
|
|
348
|
-
await enrichTree(cpiTree, resolver);
|
|
349
|
-
for (const attr of flatAttributions(cpiTree)) {
|
|
350
|
-
span.addEvent("cpi.invoke", {
|
|
351
|
-
"cpi.program": attr.programName ?? attr.programId,
|
|
352
|
-
"cpi.instruction": attr.instructionName ?? "unknown",
|
|
353
|
-
"cpi.depth": attr.depth,
|
|
354
|
-
"cpi.cu_consumed": attr.cuConsumed,
|
|
355
|
-
"cpi.cu_self": attr.cuSelf,
|
|
356
|
-
"cpi.percentage": parseFloat(attr.percentage.toFixed(2))
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
const root = cpiTree.roots[0];
|
|
360
|
-
if (root) {
|
|
361
|
-
if (root.programName) span.setAttribute("solana.tx.program", root.programName);
|
|
362
|
-
if (root.instructionName) span.setAttribute("solana.tx.instruction", root.instructionName);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
span.setAttribute("solana.tx.enrichment_ms", Date.now() - start);
|
|
366
|
-
if (txDetails.meta?.err) {
|
|
367
|
-
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
368
|
-
} else {
|
|
369
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
419
|
+
await attachParsedLogsToSpan(span, logs, resolver);
|
|
370
420
|
}
|
|
421
|
+
finalizeSpan(span, txDetails, start);
|
|
371
422
|
} catch (err) {
|
|
372
423
|
span.recordException(err instanceof Error ? err : new Error(String(err)));
|
|
373
424
|
span.setStatus({
|
|
374
|
-
code:
|
|
425
|
+
code: SpanStatusCode2.ERROR,
|
|
375
426
|
message: err instanceof Error ? err.message : String(err)
|
|
376
427
|
});
|
|
377
428
|
} finally {
|
|
@@ -379,27 +430,10 @@ async function trackSolanaTransaction(opts) {
|
|
|
379
430
|
}
|
|
380
431
|
}
|
|
381
432
|
function buildResolverFromIdls(idls) {
|
|
382
|
-
const resolver = new
|
|
433
|
+
const resolver = new IdlResolver2();
|
|
383
434
|
if (idls) resolver.registerMany(idls);
|
|
384
435
|
return resolver;
|
|
385
436
|
}
|
|
386
|
-
async function pollGetTransaction(connection, signature, commitment, deadline, basePollMs) {
|
|
387
|
-
let attempt = 0;
|
|
388
|
-
while (Date.now() < deadline) {
|
|
389
|
-
try {
|
|
390
|
-
const tx = await connection.getTransaction(signature, {
|
|
391
|
-
commitment,
|
|
392
|
-
maxSupportedTransactionVersion: 0
|
|
393
|
-
});
|
|
394
|
-
if (tx) return tx;
|
|
395
|
-
} catch {
|
|
396
|
-
}
|
|
397
|
-
attempt++;
|
|
398
|
-
const waitMs = Math.min(basePollMs * Math.pow(1.5, attempt - 1), 2e3);
|
|
399
|
-
await new Promise((resolve) => setTimeout(resolve, waitMs));
|
|
400
|
-
}
|
|
401
|
-
return null;
|
|
402
|
-
}
|
|
403
437
|
|
|
404
438
|
// src/index.ts
|
|
405
439
|
var InstrumentedConnection = class extends Connection {
|
|
@@ -416,7 +450,9 @@ var InstrumentedConnection = class extends Connection {
|
|
|
416
450
|
enrichmentTimeoutMs,
|
|
417
451
|
enrichmentPollIntervalMs,
|
|
418
452
|
disableAutoSpan,
|
|
419
|
-
commitment
|
|
453
|
+
// Note: commitment is intentionally NOT destructured out. It must
|
|
454
|
+
// pass through to super() via connectionConfig so the parent
|
|
455
|
+
// Connection uses it for RPC calls (preflight, blockhash, etc.).
|
|
420
456
|
...connectionConfig
|
|
421
457
|
} = config2 ?? {};
|
|
422
458
|
super(endpoint, connectionConfig);
|
|
@@ -425,12 +461,12 @@ var InstrumentedConnection = class extends Connection {
|
|
|
425
461
|
idlRpcEndpoint,
|
|
426
462
|
skipIdlResolution,
|
|
427
463
|
allowOnChainIdlFetch,
|
|
428
|
-
enrichmentTimeoutMs: enrichmentTimeoutMs ??
|
|
429
|
-
enrichmentPollIntervalMs: enrichmentPollIntervalMs ??
|
|
464
|
+
enrichmentTimeoutMs: enrichmentTimeoutMs ?? DEFAULT_ENRICHMENT_TIMEOUT_MS,
|
|
465
|
+
enrichmentPollIntervalMs: enrichmentPollIntervalMs ?? DEFAULT_ENRICHMENT_POLL_MS,
|
|
430
466
|
disableAutoSpan,
|
|
431
|
-
commitment
|
|
467
|
+
commitment: config2?.commitment
|
|
432
468
|
};
|
|
433
|
-
this.idlResolver = new
|
|
469
|
+
this.idlResolver = new IdlResolver3({
|
|
434
470
|
rpcEndpoint: idlRpcEndpoint ?? endpoint,
|
|
435
471
|
allowOnChainFetch: allowOnChainIdlFetch ?? false
|
|
436
472
|
});
|
|
@@ -478,7 +514,7 @@ var InstrumentedConnection = class extends Connection {
|
|
|
478
514
|
span.setAttribute("solana.tx.status", "failed");
|
|
479
515
|
span.recordException(err instanceof Error ? err : new Error(String(err)));
|
|
480
516
|
span.setStatus({
|
|
481
|
-
code:
|
|
517
|
+
code: SpanStatusCode3.ERROR,
|
|
482
518
|
message: err instanceof Error ? err.message : String(err)
|
|
483
519
|
});
|
|
484
520
|
span.end();
|
|
@@ -502,30 +538,30 @@ var InstrumentedConnection = class extends Connection {
|
|
|
502
538
|
*/
|
|
503
539
|
async enrichSpanInBackground(span, signature, submitStart) {
|
|
504
540
|
const enrichStart = Date.now();
|
|
505
|
-
const deadline = enrichStart + (this.sightConfig.enrichmentTimeoutMs ??
|
|
541
|
+
const deadline = enrichStart + (this.sightConfig.enrichmentTimeoutMs ?? DEFAULT_ENRICHMENT_TIMEOUT_MS);
|
|
506
542
|
const commitment = this.sightConfig.commitment ?? "confirmed";
|
|
507
|
-
const basePollMs = this.sightConfig.enrichmentPollIntervalMs ??
|
|
543
|
+
const basePollMs = this.sightConfig.enrichmentPollIntervalMs ?? DEFAULT_ENRICHMENT_POLL_MS;
|
|
508
544
|
try {
|
|
509
|
-
const txDetails = await
|
|
545
|
+
const txDetails = await pollForTransaction(
|
|
546
|
+
(sig, opts) => super.getTransaction(sig, opts),
|
|
547
|
+
signature,
|
|
548
|
+
commitment,
|
|
549
|
+
deadline,
|
|
550
|
+
basePollMs
|
|
551
|
+
);
|
|
510
552
|
if (!txDetails) {
|
|
511
|
-
span
|
|
512
|
-
span.setAttribute("solana.tx.enrichment_ms", Date.now() - submitStart);
|
|
553
|
+
finalizeSpan(span, null, submitStart);
|
|
513
554
|
span.end();
|
|
514
555
|
return;
|
|
515
556
|
}
|
|
516
|
-
|
|
557
|
+
attachTxDetailsToSpan(span, txDetails);
|
|
517
558
|
if (!this.sightConfig.skipIdlResolution) {
|
|
518
559
|
const logs = txDetails.meta?.logMessages ?? [];
|
|
519
560
|
if (logs.length > 0) {
|
|
520
|
-
await
|
|
561
|
+
await attachParsedLogsToSpan(span, logs, this.idlResolver);
|
|
521
562
|
}
|
|
522
563
|
}
|
|
523
|
-
span
|
|
524
|
-
if (txDetails.meta?.err) {
|
|
525
|
-
span.setStatus({ code: SpanStatusCode2.ERROR });
|
|
526
|
-
} else {
|
|
527
|
-
span.setStatus({ code: SpanStatusCode2.OK });
|
|
528
|
-
}
|
|
564
|
+
finalizeSpan(span, txDetails, submitStart);
|
|
529
565
|
} catch (err) {
|
|
530
566
|
span.recordException(err instanceof Error ? err : new Error(String(err)));
|
|
531
567
|
span.setAttribute(
|
|
@@ -536,77 +572,9 @@ var InstrumentedConnection = class extends Connection {
|
|
|
536
572
|
span.end();
|
|
537
573
|
}
|
|
538
574
|
}
|
|
539
|
-
/**
|
|
540
|
-
* Poll `getTransaction(signature)` until either the on-chain record is
|
|
541
|
-
* returned or the deadline passes. Exponential backoff (1.5x) capped at
|
|
542
|
-
* 2 seconds to balance responsiveness against RPC load.
|
|
543
|
-
*/
|
|
544
|
-
async pollForTransaction(signature, commitment, deadline, basePollMs) {
|
|
545
|
-
let attempt = 0;
|
|
546
|
-
while (Date.now() < deadline) {
|
|
547
|
-
try {
|
|
548
|
-
const tx = await super.getTransaction(signature, {
|
|
549
|
-
commitment,
|
|
550
|
-
maxSupportedTransactionVersion: 0
|
|
551
|
-
});
|
|
552
|
-
if (tx) return tx;
|
|
553
|
-
} catch {
|
|
554
|
-
}
|
|
555
|
-
attempt++;
|
|
556
|
-
const waitMs = Math.min(basePollMs * Math.pow(1.5, attempt - 1), 2e3);
|
|
557
|
-
await sleep(waitMs);
|
|
558
|
-
}
|
|
559
|
-
return null;
|
|
560
|
-
}
|
|
561
|
-
/** Attach the flat fields (slot, fee, CU, status) from a tx response to a span. */
|
|
562
|
-
attachTxDetailsToSpan(span, txDetails) {
|
|
563
|
-
span.setAttribute("solana.tx.status", txDetails.meta?.err ? "failed" : "confirmed");
|
|
564
|
-
span.setAttribute("solana.tx.slot", txDetails.slot);
|
|
565
|
-
const fee = txDetails.meta?.fee;
|
|
566
|
-
if (fee !== void 0) span.setAttribute("solana.tx.fee_lamports", fee);
|
|
567
|
-
const cuUsed = txDetails.meta?.computeUnitsConsumed;
|
|
568
|
-
if (cuUsed !== void 0 && cuUsed !== null) {
|
|
569
|
-
span.setAttribute("solana.tx.cu_used", Number(cuUsed));
|
|
570
|
-
span.setAttribute("solana.tx.cu_budget", 2e5);
|
|
571
|
-
span.setAttribute(
|
|
572
|
-
"solana.tx.cu_utilization",
|
|
573
|
-
parseFloat((Number(cuUsed) / 2e5 * 100).toFixed(1))
|
|
574
|
-
);
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
/**
|
|
578
|
-
* Parse program logs into a CPI tree, enrich with registered IDLs, and
|
|
579
|
-
* emit one `cpi.invoke` event per invocation onto the span. Root
|
|
580
|
-
* program/instruction names are copied onto span attributes so dashboards
|
|
581
|
-
* can filter by them without walking events.
|
|
582
|
-
*/
|
|
583
|
-
async attachParsedLogsToSpan(span, logs) {
|
|
584
|
-
const { cpiTree } = parseLogs2({ logs });
|
|
585
|
-
await enrichTree2(cpiTree, this.idlResolver);
|
|
586
|
-
const attributions = flatAttributions2(cpiTree);
|
|
587
|
-
for (const attr of attributions) {
|
|
588
|
-
span.addEvent("cpi.invoke", {
|
|
589
|
-
"cpi.program": attr.programName ?? attr.programId,
|
|
590
|
-
"cpi.instruction": attr.instructionName ?? "unknown",
|
|
591
|
-
"cpi.depth": attr.depth,
|
|
592
|
-
"cpi.cu_consumed": attr.cuConsumed,
|
|
593
|
-
"cpi.cu_self": attr.cuSelf,
|
|
594
|
-
"cpi.percentage": parseFloat(attr.percentage.toFixed(2))
|
|
595
|
-
});
|
|
596
|
-
}
|
|
597
|
-
const root = cpiTree.roots[0];
|
|
598
|
-
if (root) {
|
|
599
|
-
if (root.programName) span.setAttribute("solana.tx.program", root.programName);
|
|
600
|
-
if (root.instructionName) span.setAttribute("solana.tx.instruction", root.instructionName);
|
|
601
|
-
}
|
|
602
|
-
return cpiTree;
|
|
603
|
-
}
|
|
604
575
|
};
|
|
605
|
-
function sleep(ms) {
|
|
606
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
607
|
-
}
|
|
608
576
|
export {
|
|
609
|
-
|
|
577
|
+
IdlResolver4 as IdlResolver,
|
|
610
578
|
InstrumentedConnection,
|
|
611
579
|
SightSpanExporter,
|
|
612
580
|
buildDsn,
|