@glasstrace/sdk 0.12.1 → 0.12.3

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.d.cts CHANGED
@@ -307,7 +307,7 @@ interface InitClaimResult {
307
307
  * or request failure). Callers that do not need claim information
308
308
  * can safely ignore the return value.
309
309
  */
310
- declare function performInit(config: ResolvedConfig, anonKey: AnonApiKey | null, sdkVersion: string): Promise<InitClaimResult | null>;
310
+ declare function performInit(config: ResolvedConfig, anonKey: AnonApiKey | null, sdkVersion: string, healthReport?: SdkHealthReport | null): Promise<InitClaimResult | null>;
311
311
  /**
312
312
  * Returns the current capture config from the three-tier fallback chain:
313
313
  * 1. In-memory config from latest init response
@@ -394,6 +394,10 @@ declare class GlasstraceExporter implements SpanExporter {
394
394
  */
395
395
  notifyKeyResolved(): void;
396
396
  shutdown(): Promise<void>;
397
+ /**
398
+ * Flushes any pending buffered spans (if the API key has resolved) and
399
+ * delegates to the underlying exporter's forceFlush to drain its queue.
400
+ */
397
401
  forceFlush(): Promise<void>;
398
402
  /**
399
403
  * Enriches a ReadableSpan with all glasstrace.* attributes.
package/dist/index.d.ts CHANGED
@@ -307,7 +307,7 @@ interface InitClaimResult {
307
307
  * or request failure). Callers that do not need claim information
308
308
  * can safely ignore the return value.
309
309
  */
310
- declare function performInit(config: ResolvedConfig, anonKey: AnonApiKey | null, sdkVersion: string): Promise<InitClaimResult | null>;
310
+ declare function performInit(config: ResolvedConfig, anonKey: AnonApiKey | null, sdkVersion: string, healthReport?: SdkHealthReport | null): Promise<InitClaimResult | null>;
311
311
  /**
312
312
  * Returns the current capture config from the three-tier fallback chain:
313
313
  * 1. In-memory config from latest init response
@@ -394,6 +394,10 @@ declare class GlasstraceExporter implements SpanExporter {
394
394
  */
395
395
  notifyKeyResolved(): void;
396
396
  shutdown(): Promise<void>;
397
+ /**
398
+ * Flushes any pending buffered spans (if the API key has resolved) and
399
+ * delegates to the underlying exporter's forceFlush to drain its queue.
400
+ */
397
401
  forceFlush(): Promise<void>;
398
402
  /**
399
403
  * Enriches a ReadableSpan with all glasstrace.* attributes.
package/dist/index.js CHANGED
@@ -9,10 +9,11 @@ import {
9
9
  maybeShowMcpNudge,
10
10
  readEnvVars,
11
11
  resolveConfig,
12
+ sdkLog,
12
13
  uploadSourceMaps,
13
14
  uploadSourceMapsAuto,
14
15
  uploadSourceMapsPresigned
15
- } from "./chunk-MSMOH6IH.js";
16
+ } from "./chunk-J576N5MN.js";
16
17
  import {
17
18
  buildImportGraph,
18
19
  discoverTestFiles,
@@ -30,6 +31,7 @@ import {
30
31
  SessionIdSchema
31
32
  } from "./chunk-OKIP4SRG.js";
32
33
  import {
34
+ DiagLogLevel,
33
35
  INVALID_SPAN_CONTEXT,
34
36
  SamplingDecision,
35
37
  SpanKind,
@@ -173,6 +175,58 @@ function classifyFetchTarget(url) {
173
175
  return "unknown";
174
176
  }
175
177
 
178
+ // src/health-collector.ts
179
+ var tracesExported = 0;
180
+ var tracesDropped = 0;
181
+ var initFailures = 0;
182
+ var lastConfigSyncAt = null;
183
+ function recordSpansExported(count) {
184
+ if (!Number.isFinite(count) || count < 0 || !Number.isInteger(count)) return;
185
+ tracesExported += count;
186
+ }
187
+ function recordSpansDropped(count) {
188
+ if (!Number.isFinite(count) || count < 0 || !Number.isInteger(count)) return;
189
+ tracesDropped += count;
190
+ }
191
+ function recordInitFailure() {
192
+ try {
193
+ initFailures += 1;
194
+ } catch {
195
+ }
196
+ }
197
+ function recordConfigSync(timestamp) {
198
+ try {
199
+ lastConfigSyncAt = timestamp;
200
+ } catch {
201
+ }
202
+ }
203
+ function collectHealthReport(sdkVersion) {
204
+ try {
205
+ const now = Date.now();
206
+ const configAge = lastConfigSyncAt !== null ? Math.max(0, now - lastConfigSyncAt) : 0;
207
+ return {
208
+ tracesExportedSinceLastInit: tracesExported,
209
+ tracesDropped,
210
+ initFailures,
211
+ configAge: Math.round(configAge),
212
+ sdkVersion
213
+ };
214
+ } catch {
215
+ return null;
216
+ }
217
+ }
218
+ function acknowledgeHealthReport(report) {
219
+ const exp = Math.max(0, report.tracesExportedSinceLastInit);
220
+ const expVal = tracesExported - exp;
221
+ tracesExported = Number.isFinite(expVal) ? Math.max(0, expVal) : tracesExported;
222
+ const drop = Math.max(0, report.tracesDropped);
223
+ const dropVal = tracesDropped - drop;
224
+ tracesDropped = Number.isFinite(dropVal) ? Math.max(0, dropVal) : tracesDropped;
225
+ const fail = Math.max(0, report.initFailures);
226
+ const failVal = initFailures - fail;
227
+ initFailures = Number.isFinite(failVal) ? Math.max(0, failVal) : initFailures;
228
+ }
229
+
176
230
  // src/init-client.ts
177
231
  var GLASSTRACE_DIR = ".glasstrace";
178
232
  var CONFIG_FILE = "config";
@@ -222,6 +276,7 @@ function loadCachedConfig(projectRoot) {
222
276
  }
223
277
  const result = SdkInitResponseSchema.safeParse(cached.response);
224
278
  if (result.success) {
279
+ recordConfigSync(cached.cachedAt);
225
280
  return result.data;
226
281
  }
227
282
  console.warn("[glasstrace] Cached config failed validation. Using defaults.");
@@ -372,7 +427,7 @@ async function writeClaimedKey(newApiKey, projectRoot) {
372
427
  } catch {
373
428
  }
374
429
  }
375
- async function performInit(config, anonKey, sdkVersion) {
430
+ async function performInit(config, anonKey, sdkVersion, healthReport) {
376
431
  if (rateLimitBackoff) {
377
432
  rateLimitBackoff = false;
378
433
  return null;
@@ -391,12 +446,16 @@ async function performInit(config, anonKey, sdkVersion) {
391
446
  anonKey,
392
447
  sdkVersion,
393
448
  void 0,
394
- void 0,
449
+ healthReport ?? void 0,
395
450
  void 0,
396
451
  controller.signal
397
452
  );
398
453
  clearTimeout(timeoutId);
399
454
  currentConfig = result;
455
+ recordConfigSync(Date.now());
456
+ if (healthReport) {
457
+ acknowledgeHealthReport(healthReport);
458
+ }
400
459
  await saveCachedConfig(result);
401
460
  if (result.claimResult) {
402
461
  try {
@@ -408,6 +467,7 @@ async function performInit(config, anonKey, sdkVersion) {
408
467
  return null;
409
468
  } catch (err) {
410
469
  clearTimeout(timeoutId);
470
+ recordInitFailure();
411
471
  if (err instanceof DOMException && err.name === "AbortError") {
412
472
  console.warn("[glasstrace] ingestion_unreachable: Init request timed out.");
413
473
  return null;
@@ -442,6 +502,7 @@ async function performInit(config, anonKey, sdkVersion) {
442
502
  return null;
443
503
  }
444
504
  } catch (err) {
505
+ recordInitFailure();
445
506
  console.warn(
446
507
  `[glasstrace] Unexpected init error: ${err instanceof Error ? err.message : String(err)}`
447
508
  );
@@ -526,8 +587,15 @@ var GlasstraceExporter = class {
526
587
  const enrichedSpans = spans.map((span) => this.enrichSpan(span));
527
588
  const exporter = this.ensureDelegate();
528
589
  if (exporter) {
529
- exporter.export(enrichedSpans, resultCallback);
590
+ exporter.export(enrichedSpans, (result) => {
591
+ if (result.code !== 0) {
592
+ sdkLog("warn", `[glasstrace] Span export failed: ${result.error?.message ?? "unknown error"}`);
593
+ }
594
+ resultCallback(result);
595
+ });
596
+ recordSpansExported(enrichedSpans.length);
530
597
  } else {
598
+ recordSpansDropped(enrichedSpans.length);
531
599
  resultCallback({ code: 0 });
532
600
  }
533
601
  }
@@ -546,6 +614,7 @@ var GlasstraceExporter = class {
546
614
  console.warn(
547
615
  `[glasstrace] Shutdown with ${this.pendingSpanCount} buffered spans \u2014 API key never resolved, spans lost.`
548
616
  );
617
+ recordSpansDropped(this.pendingSpanCount);
549
618
  for (const batch of this.pendingBatches) {
550
619
  batch.resultCallback({ code: 0 });
551
620
  }
@@ -556,7 +625,14 @@ var GlasstraceExporter = class {
556
625
  return this.delegate.shutdown();
557
626
  }
558
627
  }
628
+ /**
629
+ * Flushes any pending buffered spans (if the API key has resolved) and
630
+ * delegates to the underlying exporter's forceFlush to drain its queue.
631
+ */
559
632
  forceFlush() {
633
+ if (this.getApiKey() !== API_KEY_PENDING && this.pendingBatches.length > 0) {
634
+ this.flushPending();
635
+ }
560
636
  if (this.delegate?.forceFlush) {
561
637
  return this.delegate.forceFlush();
562
638
  }
@@ -688,6 +764,7 @@ var GlasstraceExporter = class {
688
764
  while (this.pendingSpanCount > MAX_PENDING_SPANS && this.pendingBatches.length > 1) {
689
765
  const evicted = this.pendingBatches.shift();
690
766
  this.pendingSpanCount -= evicted.spans.length;
767
+ recordSpansDropped(evicted.spans.length);
691
768
  evicted.resultCallback({ code: 0 });
692
769
  if (!this.overflowLogged) {
693
770
  this.overflowLogged = true;
@@ -709,9 +786,12 @@ var GlasstraceExporter = class {
709
786
  if (this.pendingBatches.length === 0) return;
710
787
  const exporter = this.ensureDelegate();
711
788
  if (!exporter) {
789
+ let discardedCount = 0;
712
790
  for (const batch of this.pendingBatches) {
791
+ discardedCount += batch.spans.length;
713
792
  batch.resultCallback({ code: 0 });
714
793
  }
794
+ recordSpansDropped(discardedCount);
715
795
  this.pendingBatches = [];
716
796
  this.pendingSpanCount = 0;
717
797
  return;
@@ -721,7 +801,13 @@ var GlasstraceExporter = class {
721
801
  this.pendingSpanCount = 0;
722
802
  for (const batch of batches) {
723
803
  const enriched = batch.spans.map((span) => this.enrichSpan(span));
724
- exporter.export(enriched, batch.resultCallback);
804
+ exporter.export(enriched, (result) => {
805
+ if (result.code !== 0) {
806
+ sdkLog("warn", `[glasstrace] Span export failed: ${result.error?.message ?? "unknown error"}`);
807
+ }
808
+ batch.resultCallback(result);
809
+ });
810
+ recordSpansExported(enriched.length);
725
811
  }
726
812
  }
727
813
  };
@@ -3511,7 +3597,21 @@ async function configureOtel(config, sessionManager) {
3511
3597
  _activeExporter = null;
3512
3598
  return;
3513
3599
  }
3514
- const processor = new BatchSpanProcessor(glasstraceExporter);
3600
+ if (config.verbose) {
3601
+ diag.setLogger(
3602
+ {
3603
+ verbose: (msg) => sdkLog("info", `[otel] ${msg}`),
3604
+ debug: (msg) => sdkLog("info", `[otel] ${msg}`),
3605
+ info: (msg) => sdkLog("info", `[otel] ${msg}`),
3606
+ warn: (msg) => sdkLog("warn", `[otel] ${msg}`),
3607
+ error: (msg) => sdkLog("error", `[otel] ${msg}`)
3608
+ },
3609
+ DiagLogLevel.WARN
3610
+ );
3611
+ }
3612
+ const processor = new BatchSpanProcessor(glasstraceExporter, {
3613
+ scheduledDelayMillis: 1e3
3614
+ });
3515
3615
  const provider = new BasicTracerProvider({
3516
3616
  spanProcessors: [processor]
3517
3617
  });
@@ -3672,7 +3772,8 @@ async function backgroundInit(config, anonKeyForInit, generation) {
3672
3772
  if (config.verbose) {
3673
3773
  console.info("[glasstrace] Background init firing.");
3674
3774
  }
3675
- const initResult = await performInit(config, anonKeyForInit, "0.12.1");
3775
+ const healthReport = collectHealthReport("0.12.3");
3776
+ const initResult = await performInit(config, anonKeyForInit, "0.12.3", healthReport);
3676
3777
  if (generation !== registrationGeneration) return;
3677
3778
  if (initResult?.claimResult) {
3678
3779
  setResolvedApiKey(initResult.claimResult.newApiKey);
@@ -3746,7 +3847,7 @@ async function handleSourceMapUpload(distDir) {
3746
3847
  );
3747
3848
  return;
3748
3849
  }
3749
- const { discoverSourceMapFiles: discoverSourceMapFiles2, computeBuildHash: computeBuildHash2, uploadSourceMaps: uploadSourceMaps2 } = await import("./source-map-uploader-ZFCYOURS.js");
3850
+ const { discoverSourceMapFiles: discoverSourceMapFiles2, computeBuildHash: computeBuildHash2, uploadSourceMaps: uploadSourceMaps2 } = await import("./source-map-uploader-ZHD654EG.js");
3750
3851
  const files = await discoverSourceMapFiles2(distDir);
3751
3852
  if (files.length === 0) {
3752
3853
  console.info("[glasstrace] No source map files found. Skipping upload.");