observa-sdk 0.0.6 → 0.0.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
@@ -24,6 +24,14 @@ __export(index_exports, {
24
24
  init: () => init
25
25
  });
26
26
  module.exports = __toCommonJS(index_exports);
27
+ var contextModule = null;
28
+ try {
29
+ const requireFn = globalThis.require;
30
+ if (typeof requireFn !== "undefined") {
31
+ contextModule = requireFn("./context.js");
32
+ }
33
+ } catch {
34
+ }
27
35
  function getNodeEnv() {
28
36
  try {
29
37
  const proc = globalThis.process;
@@ -246,6 +254,20 @@ var Observa = class {
246
254
  isProduction;
247
255
  sampleRate;
248
256
  maxResponseChars;
257
+ // Buffering and retry (now stores canonical events)
258
+ eventBuffer = [];
259
+ flushPromise = null;
260
+ flushInProgress = false;
261
+ maxBufferSize = 100;
262
+ flushIntervalMs = 5e3;
263
+ // Flush every 5 seconds
264
+ flushIntervalId = null;
265
+ // Span hierarchy tracking (for manual trace management)
266
+ currentTraceId = null;
267
+ rootSpanId = null;
268
+ spanStack = [];
269
+ // Stack for tracking parent-child relationships
270
+ traceStartTime = null;
249
271
  constructor(config) {
250
272
  this.apiKey = config.apiKey;
251
273
  let apiUrlEnv;
@@ -290,35 +312,465 @@ var Observa = class {
290
312
  `\u{1F517} [Observa] Auth: ${jwtContext ? "JWT (auto-extracted)" : "Legacy (config)"}`
291
313
  );
292
314
  }
315
+ try {
316
+ if (typeof setInterval !== "undefined") {
317
+ this.flushIntervalId = setInterval(() => {
318
+ this.flush().catch((err) => {
319
+ console.error("[Observa] Periodic flush failed:", err);
320
+ });
321
+ }, this.flushIntervalMs);
322
+ }
323
+ } catch {
324
+ }
325
+ }
326
+ /**
327
+ * Flush buffered events to the API
328
+ * Returns a promise that resolves when all events are sent
329
+ */
330
+ async flush() {
331
+ if (this.flushInProgress || this.eventBuffer.length === 0) {
332
+ return this.flushPromise || Promise.resolve();
333
+ }
334
+ this.flushInProgress = true;
335
+ this.flushPromise = this._doFlush();
336
+ try {
337
+ await this.flushPromise;
338
+ } finally {
339
+ this.flushInProgress = false;
340
+ this.flushPromise = null;
341
+ }
342
+ }
343
+ /**
344
+ * Helper: Create base event properties
345
+ */
346
+ createBaseEventProperties() {
347
+ const traceId = this.currentTraceId || crypto.randomUUID();
348
+ return {
349
+ tenant_id: this.tenantId,
350
+ project_id: this.projectId,
351
+ environment: this.environment,
352
+ trace_id: traceId
353
+ };
354
+ }
355
+ /**
356
+ * Helper: Add event to buffer with proper span hierarchy
357
+ */
358
+ addEvent(eventData) {
359
+ const baseProps = this.createBaseEventProperties();
360
+ const parentSpanId = this.spanStack.length > 0 ? this.spanStack[this.spanStack.length - 1] : null;
361
+ const event = {
362
+ ...baseProps,
363
+ span_id: eventData.span_id || crypto.randomUUID(),
364
+ parent_span_id: (eventData.parent_span_id !== void 0 ? eventData.parent_span_id : parentSpanId) ?? null,
365
+ timestamp: eventData.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
366
+ event_type: eventData.event_type,
367
+ conversation_id: eventData.conversation_id ?? null,
368
+ session_id: eventData.session_id ?? null,
369
+ user_id: eventData.user_id ?? null,
370
+ agent_name: eventData.agent_name ?? null,
371
+ version: eventData.version ?? null,
372
+ route: eventData.route ?? null,
373
+ attributes: eventData.attributes
374
+ };
375
+ this.eventBuffer.push(event);
376
+ if (this.eventBuffer.length >= this.maxBufferSize) {
377
+ this.flush().catch((err) => {
378
+ console.error("[Observa] Auto-flush failed:", err);
379
+ });
380
+ }
381
+ }
382
+ /**
383
+ * Start a new trace (manual trace management)
384
+ */
385
+ startTrace(options = {}) {
386
+ if (this.currentTraceId) {
387
+ console.warn("[Observa] Ending previous trace before starting new one");
388
+ this.endTrace().catch(console.error);
389
+ }
390
+ this.currentTraceId = crypto.randomUUID();
391
+ this.rootSpanId = crypto.randomUUID();
392
+ this.spanStack = [this.rootSpanId];
393
+ this.traceStartTime = Date.now();
394
+ this.addEvent({
395
+ event_type: "trace_start",
396
+ span_id: this.rootSpanId,
397
+ parent_span_id: null,
398
+ conversation_id: options.conversationId || null,
399
+ session_id: options.sessionId || null,
400
+ user_id: options.userId || null,
401
+ attributes: {
402
+ trace_start: {
403
+ name: options.name || null,
404
+ metadata: options.metadata || null
405
+ }
406
+ }
407
+ });
408
+ return this.currentTraceId;
409
+ }
410
+ /**
411
+ * Track a tool call
412
+ */
413
+ trackToolCall(options) {
414
+ const spanId = crypto.randomUUID();
415
+ this.addEvent({
416
+ event_type: "tool_call",
417
+ span_id: spanId,
418
+ attributes: {
419
+ tool_call: {
420
+ tool_name: options.toolName,
421
+ args: options.args || null,
422
+ result: options.result || null,
423
+ result_status: options.resultStatus,
424
+ latency_ms: options.latencyMs,
425
+ error_message: options.errorMessage || null
426
+ }
427
+ }
428
+ });
429
+ return spanId;
430
+ }
431
+ /**
432
+ * Track a retrieval operation
433
+ */
434
+ trackRetrieval(options) {
435
+ const spanId = crypto.randomUUID();
436
+ this.addEvent({
437
+ event_type: "retrieval",
438
+ span_id: spanId,
439
+ attributes: {
440
+ retrieval: {
441
+ retrieval_context_ids: options.contextIds || null,
442
+ retrieval_context_hashes: options.contextHashes || null,
443
+ k: options.k || null,
444
+ top_k: options.k || null,
445
+ similarity_scores: options.similarityScores || null,
446
+ latency_ms: options.latencyMs
447
+ }
448
+ }
449
+ });
450
+ return spanId;
451
+ }
452
+ /**
453
+ * Track an error with stack trace support
454
+ */
455
+ trackError(options) {
456
+ const spanId = crypto.randomUUID();
457
+ let stackTrace = options.stackTrace;
458
+ if (!stackTrace && options.error instanceof Error && options.error.stack) {
459
+ stackTrace = options.error.stack;
460
+ }
461
+ this.addEvent({
462
+ event_type: "error",
463
+ span_id: spanId,
464
+ attributes: {
465
+ error: {
466
+ error_type: options.errorType,
467
+ error_message: options.errorMessage,
468
+ stack_trace: stackTrace || null,
469
+ context: options.context || null
470
+ }
471
+ }
472
+ });
473
+ return spanId;
474
+ }
475
+ /**
476
+ * Track user feedback
477
+ */
478
+ trackFeedback(options) {
479
+ const spanId = options.spanId || crypto.randomUUID();
480
+ const parentSpanId = options.parentSpanId ?? (this.spanStack.length > 0 ? this.spanStack[this.spanStack.length - 1] : null);
481
+ let rating = options.rating;
482
+ if (rating !== void 0 && rating !== null) {
483
+ rating = Math.max(1, Math.min(5, rating));
484
+ }
485
+ this.addEvent({
486
+ event_type: "feedback",
487
+ span_id: spanId,
488
+ parent_span_id: parentSpanId ?? null,
489
+ conversation_id: options.conversationId ?? null,
490
+ session_id: options.sessionId ?? null,
491
+ user_id: options.userId ?? null,
492
+ agent_name: options.agentName ?? null,
493
+ version: options.version ?? null,
494
+ route: options.route ?? null,
495
+ attributes: {
496
+ feedback: {
497
+ type: options.type,
498
+ rating: rating ?? null,
499
+ comment: options.comment || null,
500
+ outcome: options.outcome || null
501
+ }
502
+ }
503
+ });
504
+ return spanId;
505
+ }
506
+ /**
507
+ * Track final output
508
+ */
509
+ trackOutput(options) {
510
+ const spanId = crypto.randomUUID();
511
+ this.addEvent({
512
+ event_type: "output",
513
+ span_id: spanId,
514
+ attributes: {
515
+ output: {
516
+ final_output: options.finalOutput || null,
517
+ output_length: options.outputLength || null
518
+ }
519
+ }
520
+ });
521
+ return spanId;
522
+ }
523
+ /**
524
+ * Execute a function within a span context (for nested operations)
525
+ * This allows tool calls to be nested under LLM calls, etc.
526
+ */
527
+ withSpan(spanId, fn) {
528
+ this.spanStack.push(spanId);
529
+ try {
530
+ return fn();
531
+ } finally {
532
+ this.spanStack.pop();
533
+ }
534
+ }
535
+ /**
536
+ * Execute an async function within a span context (for nested operations)
537
+ */
538
+ async withSpanAsync(spanId, fn) {
539
+ this.spanStack.push(spanId);
540
+ try {
541
+ return await fn();
542
+ } finally {
543
+ this.spanStack.pop();
544
+ }
545
+ }
546
+ /**
547
+ * End trace and send events (manual trace management)
548
+ */
549
+ async endTrace(options = {}) {
550
+ if (!this.currentTraceId || !this.rootSpanId) {
551
+ throw new Error("[Observa] No active trace. Call startTrace() first.");
552
+ }
553
+ const traceEvents = this.eventBuffer.filter(
554
+ (e) => e.trace_id === this.currentTraceId
555
+ );
556
+ const llmEvents = traceEvents.filter((e) => e.event_type === "llm_call");
557
+ const totalTokens = llmEvents.reduce(
558
+ (sum, e) => sum + (e.attributes.llm_call?.total_tokens || 0),
559
+ 0
560
+ );
561
+ const totalCost = llmEvents.reduce(
562
+ (sum, e) => sum + (e.attributes.llm_call?.cost || 0),
563
+ 0
564
+ );
565
+ const totalLatency = this.traceStartTime !== null ? Date.now() - this.traceStartTime : null;
566
+ this.addEvent({
567
+ event_type: "trace_end",
568
+ span_id: this.rootSpanId,
569
+ parent_span_id: null,
570
+ attributes: {
571
+ trace_end: {
572
+ total_latency_ms: totalLatency,
573
+ total_tokens: totalTokens || null,
574
+ total_cost: totalCost || null,
575
+ outcome: options.outcome || "success"
576
+ }
577
+ }
578
+ });
579
+ const traceEventsToSend = this.eventBuffer.filter(
580
+ (e) => e.trace_id === this.currentTraceId
581
+ );
582
+ if (traceEventsToSend.length > 0) {
583
+ await this._sendEventsWithRetry(traceEventsToSend);
584
+ this.eventBuffer = this.eventBuffer.filter(
585
+ (e) => e.trace_id !== this.currentTraceId
586
+ );
587
+ }
588
+ const traceId = this.currentTraceId;
589
+ this.currentTraceId = null;
590
+ this.rootSpanId = null;
591
+ this.spanStack = [];
592
+ this.traceStartTime = null;
593
+ return traceId;
594
+ }
595
+ /**
596
+ * Convert legacy TraceData to canonical events
597
+ */
598
+ traceDataToCanonicalEvents(trace) {
599
+ const events = [];
600
+ const baseEvent = {
601
+ tenant_id: trace.tenantId,
602
+ project_id: trace.projectId,
603
+ environment: trace.environment,
604
+ trace_id: trace.traceId,
605
+ conversation_id: trace.conversationId || null,
606
+ session_id: trace.sessionId || null,
607
+ user_id: trace.userId || null
608
+ };
609
+ events.push({
610
+ ...baseEvent,
611
+ span_id: trace.spanId,
612
+ parent_span_id: trace.parentSpanId || null,
613
+ timestamp: trace.timestamp,
614
+ event_type: "trace_start",
615
+ attributes: {
616
+ trace_start: {
617
+ metadata: trace.metadata || null
618
+ }
619
+ }
620
+ });
621
+ if (trace.model) {
622
+ const llmSpanId = crypto.randomUUID();
623
+ events.push({
624
+ ...baseEvent,
625
+ span_id: llmSpanId,
626
+ parent_span_id: trace.spanId,
627
+ timestamp: trace.timestamp,
628
+ event_type: "llm_call",
629
+ attributes: {
630
+ llm_call: {
631
+ model: trace.model,
632
+ input: trace.query || null,
633
+ output: trace.response || null,
634
+ input_tokens: trace.tokensPrompt || null,
635
+ output_tokens: trace.tokensCompletion || null,
636
+ total_tokens: trace.tokensTotal || null,
637
+ latency_ms: trace.latencyMs,
638
+ time_to_first_token_ms: trace.timeToFirstTokenMs || null,
639
+ streaming_duration_ms: trace.streamingDurationMs || null,
640
+ finish_reason: trace.finishReason || null,
641
+ response_id: trace.responseId || null,
642
+ system_fingerprint: trace.systemFingerprint || null,
643
+ cost: null
644
+ // Cost calculation handled by backend
645
+ }
646
+ }
647
+ });
648
+ }
649
+ events.push({
650
+ ...baseEvent,
651
+ span_id: crypto.randomUUID(),
652
+ parent_span_id: trace.spanId,
653
+ timestamp: trace.timestamp,
654
+ event_type: "output",
655
+ attributes: {
656
+ output: {
657
+ final_output: trace.response || null,
658
+ output_length: trace.responseLength || null
659
+ }
660
+ }
661
+ });
662
+ events.push({
663
+ ...baseEvent,
664
+ span_id: trace.spanId,
665
+ parent_span_id: trace.parentSpanId || null,
666
+ timestamp: trace.timestamp,
667
+ event_type: "trace_end",
668
+ attributes: {
669
+ trace_end: {
670
+ total_latency_ms: trace.latencyMs,
671
+ total_tokens: trace.tokensTotal || null,
672
+ total_cost: null,
673
+ // Cost calculation handled by backend
674
+ outcome: trace.status && trace.status >= 200 && trace.status < 300 ? "success" : "error"
675
+ }
676
+ }
677
+ });
678
+ return events;
293
679
  }
294
- async track(event, action) {
680
+ /**
681
+ * Internal flush implementation
682
+ */
683
+ async _doFlush() {
684
+ const eventsToFlush = [...this.eventBuffer];
685
+ this.eventBuffer = [];
686
+ if (eventsToFlush.length === 0) {
687
+ return;
688
+ }
689
+ const eventsByTrace = /* @__PURE__ */ new Map();
690
+ for (const event of eventsToFlush) {
691
+ if (!eventsByTrace.has(event.trace_id)) {
692
+ eventsByTrace.set(event.trace_id, []);
693
+ }
694
+ eventsByTrace.get(event.trace_id).push(event);
695
+ }
696
+ for (const [traceId, events] of eventsByTrace.entries()) {
697
+ await this._sendEventsWithRetry(events);
698
+ }
699
+ }
700
+ /**
701
+ * Send canonical events with exponential backoff retry
702
+ */
703
+ async _sendEventsWithRetry(events, maxRetries = 3) {
704
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
705
+ try {
706
+ await this.sendEvents(events);
707
+ return;
708
+ } catch (error) {
709
+ if (attempt === maxRetries) {
710
+ console.error(
711
+ `[Observa] Failed to send events after ${maxRetries + 1} attempts, re-buffering:`,
712
+ error
713
+ );
714
+ this.eventBuffer.push(...events);
715
+ if (this.eventBuffer.length > this.maxBufferSize * 2) {
716
+ const toDrop = this.eventBuffer.length - this.maxBufferSize;
717
+ this.eventBuffer.splice(0, toDrop);
718
+ }
719
+ return;
720
+ }
721
+ const delayMs = 100 * Math.pow(2, attempt);
722
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
723
+ }
724
+ }
725
+ }
726
+ /**
727
+ * Cleanup (call when shutting down)
728
+ */
729
+ async end() {
730
+ if (this.flushIntervalId) {
731
+ clearInterval(this.flushIntervalId);
732
+ this.flushIntervalId = null;
733
+ }
734
+ await this.flush();
735
+ }
736
+ async track(event, action, options) {
295
737
  if (this.sampleRate < 1 && Math.random() > this.sampleRate) {
296
738
  return action();
297
739
  }
298
740
  const startTime = Date.now();
299
741
  const traceId = crypto.randomUUID();
300
742
  const spanId = traceId;
301
- const originalResponse = await action();
743
+ const runWithContext = contextModule?.runInTraceContextAsync || ((ctx, fn) => fn());
744
+ const context = contextModule?.createSpanContext?.(
745
+ traceId,
746
+ spanId,
747
+ null
748
+ ) || { traceId, spanId, parentSpanId: null };
749
+ const originalResponse = await runWithContext(context, action);
302
750
  if (!originalResponse.body) return originalResponse;
303
751
  const responseHeaders = {};
304
752
  originalResponse.headers.forEach((value, key) => {
305
753
  responseHeaders[key] = value;
306
754
  });
307
755
  const [stream1, stream2] = originalResponse.body.tee();
308
- console.log(`[Observa] Starting captureStream for trace ${traceId}`);
309
- this.captureStream({
756
+ const capturePromise = this.captureStream({
310
757
  stream: stream2,
311
758
  event,
312
759
  traceId,
313
760
  spanId,
314
- parentSpanId: null,
761
+ parentSpanId: context.parentSpanId,
315
762
  startTime,
316
763
  status: originalResponse.status,
317
764
  statusText: originalResponse.statusText,
318
765
  headers: responseHeaders
319
- }).catch((err) => {
320
- console.error("[Observa] captureStream promise rejected:", err);
321
766
  });
767
+ if (options?.trackBlocking) {
768
+ await capturePromise;
769
+ } else {
770
+ capturePromise.catch((err) => {
771
+ console.error("[Observa] captureStream promise rejected:", err);
772
+ });
773
+ }
322
774
  return new Response(stream1, {
323
775
  headers: originalResponse.headers,
324
776
  status: originalResponse.status,
@@ -420,17 +872,40 @@ var Observa = class {
420
872
  systemFingerprint: extracted.systemFingerprint ?? null,
421
873
  ...headers !== void 0 && { headers },
422
874
  // Conversation tracking fields
423
- ...event.conversationId !== void 0 && { conversationId: event.conversationId },
875
+ ...event.conversationId !== void 0 && {
876
+ conversationId: event.conversationId
877
+ },
424
878
  ...event.sessionId !== void 0 && { sessionId: event.sessionId },
425
879
  ...event.userId !== void 0 && { userId: event.userId },
426
- ...event.messageIndex !== void 0 && { messageIndex: event.messageIndex },
427
- ...event.parentMessageId !== void 0 && { parentMessageId: event.parentMessageId }
880
+ ...event.messageIndex !== void 0 && {
881
+ messageIndex: event.messageIndex
882
+ },
883
+ ...event.parentMessageId !== void 0 && {
884
+ parentMessageId: event.parentMessageId
885
+ }
428
886
  };
429
887
  console.log(
430
- `[Observa] Trace data prepared, calling sendTrace for ${traceId}, response length: ${fullResponse.length}`
888
+ `[Observa] Trace data prepared, converting to canonical events for ${traceId}, response length: ${fullResponse.length}`
431
889
  );
432
- await this.sendTrace(traceData);
433
- console.log(`[Observa] sendTrace completed for ${traceId}`);
890
+ const canonicalEvents = this.traceDataToCanonicalEvents(traceData);
891
+ this.eventBuffer.push(...canonicalEvents);
892
+ if (this.eventBuffer.length >= this.maxBufferSize) {
893
+ this.flush().catch((err) => {
894
+ console.error("[Observa] Auto-flush failed:", err);
895
+ });
896
+ } else {
897
+ this._sendEventsWithRetry(canonicalEvents).catch((err) => {
898
+ console.error("[Observa] Failed to send events:", err);
899
+ });
900
+ }
901
+ if (!this.isProduction) {
902
+ const traceUrl = `${this.apiUrl.replace(
903
+ /\/api.*$/,
904
+ ""
905
+ )}/traces/${traceId}`;
906
+ console.log(`[Observa] \u{1F54A}\uFE0F Trace captured: ${traceUrl}`);
907
+ }
908
+ console.log(`[Observa] Trace queued for sending: ${traceId}`);
434
909
  } catch (err) {
435
910
  console.error("[Observa] Error capturing stream:", err);
436
911
  if (err instanceof Error) {
@@ -442,15 +917,57 @@ var Observa = class {
442
917
  }
443
918
  }
444
919
  }
445
- async sendTrace(trace) {
446
- if (!this.isProduction) {
447
- formatBeautifulLog(trace);
920
+ /**
921
+ * Send canonical events to Observa backend
922
+ * (internal method, use _sendEventsWithRetry for retry logic)
923
+ */
924
+ async sendEvents(events) {
925
+ if (events.length === 0) {
926
+ return;
927
+ }
928
+ const traceId = events[0]?.trace_id;
929
+ if (!this.isProduction && traceId) {
930
+ const llmEvent = events.find((e) => e.event_type === "llm_call");
931
+ const outputEvent = events.find((e) => e.event_type === "output");
932
+ const traceEndEvent = events.find((e) => e.event_type === "trace_end");
933
+ if (llmEvent && outputEvent) {
934
+ const llmAttrs = llmEvent.attributes.llm_call;
935
+ const outputAttrs = outputEvent.attributes.output;
936
+ const traceData = {
937
+ traceId: llmEvent.trace_id,
938
+ spanId: llmEvent.parent_span_id || llmEvent.span_id,
939
+ parentSpanId: llmEvent.parent_span_id || null,
940
+ timestamp: llmEvent.timestamp,
941
+ tenantId: llmEvent.tenant_id,
942
+ projectId: llmEvent.project_id,
943
+ environment: llmEvent.environment,
944
+ query: llmAttrs?.input || "",
945
+ response: outputAttrs?.final_output || "",
946
+ responseLength: outputAttrs?.output_length || 0,
947
+ ...llmAttrs?.model && { model: llmAttrs.model },
948
+ tokensPrompt: llmAttrs?.input_tokens ?? null,
949
+ tokensCompletion: llmAttrs?.output_tokens ?? null,
950
+ tokensTotal: llmAttrs?.total_tokens ?? null,
951
+ latencyMs: llmAttrs?.latency_ms || 0,
952
+ timeToFirstTokenMs: llmAttrs?.time_to_first_token_ms ?? null,
953
+ streamingDurationMs: llmAttrs?.streaming_duration_ms ?? null,
954
+ finishReason: llmAttrs?.finish_reason ?? null,
955
+ responseId: llmAttrs?.response_id ?? null,
956
+ systemFingerprint: llmAttrs?.system_fingerprint ?? null,
957
+ ...llmEvent.conversation_id && {
958
+ conversationId: llmEvent.conversation_id
959
+ },
960
+ ...llmEvent.session_id && { sessionId: llmEvent.session_id },
961
+ ...llmEvent.user_id && { userId: llmEvent.user_id }
962
+ };
963
+ formatBeautifulLog(traceData);
964
+ }
448
965
  }
449
966
  try {
450
967
  const baseUrl = this.apiUrl.replace(/\/+$/, "");
451
- const url = `${baseUrl}/api/v1/traces/ingest`;
968
+ const url = `${baseUrl}/api/v1/events/ingest`;
452
969
  console.log(
453
- `[Observa] Sending trace - URL: ${url}, TraceID: ${trace.traceId}, Tenant: ${trace.tenantId}, Project: ${trace.projectId}, APIKey: ${this.apiKey ? `Yes(${this.apiKey.length} chars)` : "No"}`
970
+ `[Observa] Sending ${events.length} canonical events - URL: ${url}, TraceID: ${traceId}, Tenant: ${events[0]?.tenant_id}, Project: ${events[0]?.project_id}, APIKey: ${this.apiKey ? `Yes(${this.apiKey.length} chars)` : "No"}`
454
971
  );
455
972
  const controller = new AbortController();
456
973
  const timeoutId = setTimeout(() => controller.abort(), 1e4);
@@ -461,7 +978,7 @@ var Observa = class {
461
978
  Authorization: `Bearer ${this.apiKey}`,
462
979
  "Content-Type": "application/json"
463
980
  },
464
- body: JSON.stringify(trace),
981
+ body: JSON.stringify(events),
465
982
  signal: controller.signal
466
983
  });
467
984
  clearTimeout(timeoutId);
@@ -480,9 +997,13 @@ var Observa = class {
480
997
  `[Observa] Backend API error: ${response.status} ${response.statusText}`,
481
998
  errorJson.error || errorText
482
999
  );
1000
+ throw new Error(
1001
+ `Observa API error: ${response.status} ${errorJson.error?.message || errorText}`
1002
+ );
483
1003
  } else {
1004
+ const result = await response.json().catch(() => ({}));
484
1005
  console.log(
485
- `\u2705 [Observa] Trace sent successfully - Trace ID: ${trace.traceId}`
1006
+ `\u2705 [Observa] Events sent successfully - Trace ID: ${traceId}, Event count: ${result.event_count || events.length}`
486
1007
  );
487
1008
  }
488
1009
  } catch (fetchError) {
@@ -493,7 +1014,7 @@ var Observa = class {
493
1014
  throw fetchError;
494
1015
  }
495
1016
  } catch (error) {
496
- console.error("[Observa] Failed to send trace:", error);
1017
+ console.error("[Observa] Failed to send events:", error);
497
1018
  if (error instanceof Error) {
498
1019
  console.error("[Observa] Error message:", error.message);
499
1020
  console.error("[Observa] Error name:", error.name);
@@ -506,6 +1027,7 @@ var Observa = class {
506
1027
  console.error("[Observa] Error stack:", error.stack);
507
1028
  }
508
1029
  }
1030
+ throw error;
509
1031
  }
510
1032
  }
511
1033
  };