@statly/observe 1.0.0 → 1.2.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.
@@ -0,0 +1,135 @@
1
+ // src/span.ts
2
+ var Span = class {
3
+ constructor(name, context, tags) {
4
+ this._status = "ok" /* OK */;
5
+ this._tags = {};
6
+ this._metadata = {};
7
+ this._finished = false;
8
+ this.name = name;
9
+ this.context = context;
10
+ this.startTime = Date.now();
11
+ if (tags) this._tags = { ...tags };
12
+ }
13
+ /**
14
+ * Finish the span and calculate duration
15
+ */
16
+ finish(endTime) {
17
+ if (this._finished) return;
18
+ this._endTime = endTime || Date.now();
19
+ this._durationMs = this._endTime - this.startTime;
20
+ this._finished = true;
21
+ }
22
+ setTag(key, value) {
23
+ this._tags[key] = value;
24
+ return this;
25
+ }
26
+ setMetadata(key, value) {
27
+ this._metadata[key] = value;
28
+ return this;
29
+ }
30
+ setStatus(status) {
31
+ this._status = status;
32
+ return this;
33
+ }
34
+ get status() {
35
+ return this._status;
36
+ }
37
+ get tags() {
38
+ return { ...this._tags };
39
+ }
40
+ get durationMs() {
41
+ return this._durationMs;
42
+ }
43
+ toDict() {
44
+ return {
45
+ name: this.name,
46
+ traceId: this.context.traceId,
47
+ spanId: this.context.spanId,
48
+ parentId: this.context.parentId,
49
+ startTime: this.startTime,
50
+ endTime: this._endTime,
51
+ durationMs: this._durationMs,
52
+ status: this._status,
53
+ tags: this._tags,
54
+ metadata: this._metadata
55
+ };
56
+ }
57
+ };
58
+ var TraceContext = class {
59
+ static getActiveSpan() {
60
+ return this.currentSpan;
61
+ }
62
+ static setActiveSpan(span) {
63
+ this.currentSpan = span;
64
+ }
65
+ };
66
+ TraceContext.currentSpan = null;
67
+
68
+ // src/telemetry.ts
69
+ var TelemetryProvider = class _TelemetryProvider {
70
+ constructor() {
71
+ this.client = null;
72
+ }
73
+ static getInstance() {
74
+ if (!_TelemetryProvider.instance) {
75
+ _TelemetryProvider.instance = new _TelemetryProvider();
76
+ }
77
+ return _TelemetryProvider.instance;
78
+ }
79
+ setClient(client) {
80
+ this.client = client;
81
+ }
82
+ /**
83
+ * Start a new span
84
+ */
85
+ startSpan(name, tags) {
86
+ const parent = TraceContext.getActiveSpan();
87
+ const traceId = parent ? parent.context.traceId : this.generateId();
88
+ const parentId = parent ? parent.context.spanId : null;
89
+ const span = new Span(name, {
90
+ traceId,
91
+ spanId: this.generateId(),
92
+ parentId
93
+ }, tags);
94
+ TraceContext.setActiveSpan(span);
95
+ return span;
96
+ }
97
+ /**
98
+ * Finish and report a span
99
+ */
100
+ finishSpan(span) {
101
+ span.finish();
102
+ if (TraceContext.getActiveSpan() === span) {
103
+ TraceContext.setActiveSpan(null);
104
+ }
105
+ if (this.client) {
106
+ this.client.captureSpan(span);
107
+ }
108
+ }
109
+ generateId() {
110
+ return Math.random().toString(16).substring(2, 18);
111
+ }
112
+ };
113
+ async function trace(name, operation, tags) {
114
+ const provider = TelemetryProvider.getInstance();
115
+ const span = provider.startSpan(name, tags);
116
+ try {
117
+ const result = await operation(span);
118
+ return result;
119
+ } catch (error) {
120
+ span.setStatus("error" /* ERROR */);
121
+ span.setTag("error", "true");
122
+ if (error instanceof Error) {
123
+ span.setTag("exception.type", error.name);
124
+ span.setTag("exception.message", error.message);
125
+ }
126
+ throw error;
127
+ } finally {
128
+ provider.finishSpan(span);
129
+ }
130
+ }
131
+
132
+ export {
133
+ TelemetryProvider,
134
+ trace
135
+ };
@@ -1,3 +1,7 @@
1
+ import {
2
+ TelemetryProvider
3
+ } from "./chunk-J5AHUFP2.mjs";
4
+
1
5
  // src/transport.ts
2
6
  var Transport = class {
3
7
  constructor(options) {
@@ -58,10 +62,10 @@ var Transport = class {
58
62
  this.queue = [];
59
63
  try {
60
64
  await this.sendBatch(events);
61
- } catch (error) {
65
+ } catch (error2) {
62
66
  this.queue = [...events, ...this.queue].slice(0, this.maxQueueSize);
63
67
  if (this.debug) {
64
- console.error("[Statly] Failed to send events:", error);
68
+ console.error("[Statly] Failed to send events:", error2);
65
69
  }
66
70
  } finally {
67
71
  this.isSending = false;
@@ -101,13 +105,13 @@ var Transport = class {
101
105
  console.log(`[Statly] Sent ${events.length} event(s)`);
102
106
  }
103
107
  return { success: true, status: response.status };
104
- } catch (error) {
108
+ } catch (error2) {
105
109
  if (this.debug) {
106
- console.error("[Statly] Network error:", error);
110
+ console.error("[Statly] Network error:", error2);
107
111
  }
108
112
  return {
109
113
  success: false,
110
- error: error instanceof Error ? error.message : "Network error"
114
+ error: error2 instanceof Error ? error2.message : "Network error"
111
115
  };
112
116
  }
113
117
  }
@@ -174,16 +178,16 @@ var GlobalHandlers = class {
174
178
  if (!this.errorCallback) {
175
179
  return;
176
180
  }
177
- let error;
181
+ let error2;
178
182
  if (event.reason instanceof Error) {
179
- error = event.reason;
183
+ error2 = event.reason;
180
184
  } else if (typeof event.reason === "string") {
181
- error = new Error(event.reason);
185
+ error2 = new Error(event.reason);
182
186
  } else {
183
- error = new Error("Unhandled Promise Rejection");
184
- error.reason = event.reason;
187
+ error2 = new Error("Unhandled Promise Rejection");
188
+ error2.reason = event.reason;
185
189
  }
186
- this.errorCallback(error, {
190
+ this.errorCallback(error2, {
187
191
  mechanism: { type: "onunhandledrejection", handled: false }
188
192
  });
189
193
  };
@@ -226,13 +230,13 @@ var GlobalHandlers = class {
226
230
  }
227
231
  installOnError() {
228
232
  this.originalOnError = window.onerror;
229
- window.onerror = (message, source, lineno, colno, error) => {
233
+ window.onerror = (message, source, lineno, colno, error2) => {
230
234
  if (this.originalOnError) {
231
- this.originalOnError.call(window, message, source, lineno, colno, error);
235
+ this.originalOnError.call(window, message, source, lineno, colno, error2);
232
236
  }
233
237
  if (this.errorCallback) {
234
- const errorObj = error || new Error(String(message));
235
- if (!error && source) {
238
+ const errorObj = error2 || new Error(String(message));
239
+ if (!error2 && source) {
236
240
  errorObj.filename = source;
237
241
  errorObj.lineno = lineno;
238
242
  errorObj.colno = colno;
@@ -356,6 +360,7 @@ var StatlyClient = class {
356
360
  this.breadcrumbs = new BreadcrumbManager(this.options.maxBreadcrumbs);
357
361
  this.globalHandlers = new GlobalHandlers();
358
362
  this.consoleIntegration = new ConsoleIntegration();
363
+ TelemetryProvider.getInstance().setClient(this);
359
364
  }
360
365
  mergeOptions(options) {
361
366
  return {
@@ -395,8 +400,8 @@ var StatlyClient = class {
395
400
  }
396
401
  this.initialized = true;
397
402
  if (this.options.autoCapture) {
398
- this.globalHandlers.install((error, context) => {
399
- this.captureError(error, context);
403
+ this.globalHandlers.install((error2, context) => {
404
+ this.captureError(error2, context);
400
405
  });
401
406
  }
402
407
  if (this.options.captureConsole) {
@@ -419,15 +424,15 @@ var StatlyClient = class {
419
424
  /**
420
425
  * Capture an exception/error
421
426
  */
422
- captureException(error, context) {
427
+ captureException(error2, context) {
423
428
  let errorObj;
424
- if (error instanceof Error) {
425
- errorObj = error;
426
- } else if (typeof error === "string") {
427
- errorObj = new Error(error);
429
+ if (error2 instanceof Error) {
430
+ errorObj = error2;
431
+ } else if (typeof error2 === "string") {
432
+ errorObj = new Error(error2);
428
433
  } else {
429
434
  errorObj = new Error("Unknown error");
430
- errorObj.originalError = error;
435
+ errorObj.originalError = error2;
431
436
  }
432
437
  return this.captureError(errorObj, context);
433
438
  }
@@ -441,21 +446,45 @@ var StatlyClient = class {
441
446
  });
442
447
  return this.sendEvent(event);
443
448
  }
449
+ /**
450
+ * Capture a completed span
451
+ */
452
+ captureSpan(span) {
453
+ const event = this.buildEvent({
454
+ message: `Span: ${span.name}`,
455
+ level: "span",
456
+ span: span.toDict()
457
+ });
458
+ return this.sendEvent(event);
459
+ }
460
+ /**
461
+ * Start a new tracing span
462
+ */
463
+ startSpan(name, tags) {
464
+ return TelemetryProvider.getInstance().startSpan(name, tags);
465
+ }
466
+ /**
467
+ * Execute a function within a trace span
468
+ */
469
+ async trace(name, operation, tags) {
470
+ const { trace: traceFn } = await import("./telemetry-CXHOTW3Y.mjs");
471
+ return traceFn(name, operation, tags);
472
+ }
444
473
  /**
445
474
  * Internal method to capture an error
446
475
  */
447
- captureError(error, context) {
476
+ captureError(error2, context) {
448
477
  if (Math.random() > this.options.sampleRate) {
449
478
  return "";
450
479
  }
451
480
  const event = this.buildEvent({
452
- message: error.message,
481
+ message: error2.message,
453
482
  level: "error",
454
- stack: error.stack,
483
+ stack: error2.stack,
455
484
  exception: {
456
- type: error.name,
457
- value: error.message,
458
- stacktrace: this.parseStackTrace(error.stack)
485
+ type: error2.name,
486
+ value: error2.message,
487
+ stacktrace: this.parseStackTrace(error2.stack)
459
488
  },
460
489
  extra: context
461
490
  });
@@ -652,73 +681,88 @@ var StatlyClient = class {
652
681
  // src/integrations/nextjs.ts
653
682
  function withStatlyPagesApi(handler) {
654
683
  return async (req, res) => {
655
- Statly.addBreadcrumb({
656
- category: "http",
657
- message: `${req.method} ${req.url}`,
658
- level: "info",
659
- data: {
660
- method: req.method,
661
- url: req.url
662
- }
663
- });
664
- try {
665
- return await handler(req, res);
666
- } catch (error) {
667
- const context = {
668
- request: {
684
+ return Statly.trace(`${req.method} ${req.url}`, async (span) => {
685
+ span.setTag("component", "nextjs-pages-api");
686
+ span.setTag("http.method", req.method || "GET");
687
+ span.setTag("http.url", req.url || "unknown");
688
+ Statly.addBreadcrumb({
689
+ category: "http",
690
+ message: `${req.method} ${req.url}`,
691
+ level: "info",
692
+ data: {
669
693
  method: req.method,
670
- url: req.url,
671
- headers: sanitizeHeaders(req.headers),
672
- query: req.query
694
+ url: req.url
673
695
  }
674
- };
675
- Statly.captureException(error, context);
676
- throw error;
677
- }
696
+ });
697
+ try {
698
+ const result = await handler(req, res);
699
+ return result;
700
+ } catch (error2) {
701
+ const context = {
702
+ request: {
703
+ method: req.method,
704
+ url: req.url,
705
+ headers: sanitizeHeaders(req.headers),
706
+ query: req.query
707
+ }
708
+ };
709
+ Statly.captureException(error2, context);
710
+ throw error2;
711
+ }
712
+ });
678
713
  };
679
714
  }
680
715
  function withStatly(handler) {
681
716
  const wrappedHandler = async (request, context) => {
682
- Statly.addBreadcrumb({
683
- category: "http",
684
- message: `${request.method} ${request.nextUrl?.pathname || request.url}`,
685
- level: "info",
686
- data: {
687
- method: request.method,
688
- url: request.nextUrl?.pathname || request.url
689
- }
690
- });
691
- try {
692
- return await handler(request, context);
693
- } catch (error) {
694
- const headers = {};
695
- request.headers.forEach((value, key) => {
696
- headers[key] = value;
697
- });
698
- const errorContext = {
699
- request: {
717
+ return Statly.trace(`${request.method} ${request.nextUrl?.pathname || request.url}`, async (span) => {
718
+ span.setTag("component", "nextjs-app-router");
719
+ span.setTag("http.method", request.method);
720
+ span.setTag("http.url", request.nextUrl?.pathname || request.url);
721
+ Statly.addBreadcrumb({
722
+ category: "http",
723
+ message: `${request.method} ${request.nextUrl?.pathname || request.url}`,
724
+ level: "info",
725
+ data: {
700
726
  method: request.method,
701
- url: request.nextUrl?.pathname || request.url,
702
- headers: sanitizeHeaders(headers),
703
- searchParams: request.nextUrl?.searchParams?.toString()
727
+ url: request.nextUrl?.pathname || request.url
704
728
  }
705
- };
706
- if (context?.params) {
707
- try {
708
- errorContext.params = await context.params;
709
- } catch {
729
+ });
730
+ try {
731
+ const result = await handler(request, context);
732
+ if (result instanceof Response) {
733
+ span.setTag("http.status_code", result.status.toString());
734
+ }
735
+ return result;
736
+ } catch (error2) {
737
+ const headers = {};
738
+ request.headers.forEach((value, key) => {
739
+ headers[key] = value;
740
+ });
741
+ const errorContext = {
742
+ request: {
743
+ method: request.method,
744
+ url: request.nextUrl?.pathname || request.url,
745
+ headers: sanitizeHeaders(headers),
746
+ searchParams: request.nextUrl?.searchParams?.toString()
747
+ }
748
+ };
749
+ if (context?.params) {
750
+ try {
751
+ errorContext.params = await context.params;
752
+ } catch {
753
+ }
710
754
  }
755
+ Statly.captureException(error2, errorContext);
756
+ throw error2;
711
757
  }
712
- Statly.captureException(error, errorContext);
713
- throw error;
714
- }
758
+ });
715
759
  };
716
760
  return wrappedHandler;
717
761
  }
718
- function captureNextJsError(error, context) {
719
- return Statly.captureException(error, {
762
+ function captureNextJsError(error2, context) {
763
+ return Statly.captureException(error2, {
720
764
  ...context,
721
- digest: error.digest,
765
+ digest: error2.digest,
722
766
  source: "nextjs-error-boundary"
723
767
  });
724
768
  }
@@ -726,12 +770,12 @@ function withStatlyGetServerSideProps(handler) {
726
770
  return async (context) => {
727
771
  try {
728
772
  return await handler(context);
729
- } catch (error) {
730
- Statly.captureException(error, {
773
+ } catch (error2) {
774
+ Statly.captureException(error2, {
731
775
  source: "getServerSideProps",
732
776
  url: context.req?.url || context.resolvedUrl
733
777
  });
734
- throw error;
778
+ throw error2;
735
779
  }
736
780
  };
737
781
  }
@@ -739,31 +783,35 @@ function withStatlyGetStaticProps(handler) {
739
783
  return async (context) => {
740
784
  try {
741
785
  return await handler(context);
742
- } catch (error) {
743
- Statly.captureException(error, {
786
+ } catch (error2) {
787
+ Statly.captureException(error2, {
744
788
  source: "getStaticProps",
745
789
  params: context.params
746
790
  });
747
- throw error;
791
+ throw error2;
748
792
  }
749
793
  };
750
794
  }
751
795
  function withStatlyServerAction(action, actionName) {
752
796
  return async (...args) => {
753
- Statly.addBreadcrumb({
754
- category: "action",
755
- message: `Server action: ${actionName || "unknown"}`,
756
- level: "info"
757
- });
758
- try {
759
- return await action(...args);
760
- } catch (error) {
761
- Statly.captureException(error, {
762
- source: "server-action",
763
- actionName
797
+ return Statly.trace(`Action: ${actionName || "unknown"}`, async (span) => {
798
+ span.setTag("component", "nextjs-server-action");
799
+ span.setTag("action.name", actionName || "unknown");
800
+ Statly.addBreadcrumb({
801
+ category: "action",
802
+ message: `Server action: ${actionName || "unknown"}`,
803
+ level: "info"
764
804
  });
765
- throw error;
766
- }
805
+ try {
806
+ return await action(...args);
807
+ } catch (error2) {
808
+ Statly.captureException(error2, {
809
+ source: "server-action",
810
+ actionName
811
+ });
812
+ throw error2;
813
+ }
814
+ });
767
815
  };
768
816
  }
769
817
  function sanitizeHeaders(headers) {
@@ -816,16 +864,16 @@ function statlyFastifyPlugin(fastify, options, done) {
816
864
  });
817
865
  hookDone();
818
866
  });
819
- fastify.setErrorHandler((error, request, reply) => {
820
- const statusCode = error.statusCode || 500;
867
+ fastify.setErrorHandler((error2, request, reply) => {
868
+ const statusCode = error2.statusCode || 500;
821
869
  if (skipStatusCodes.includes(statusCode)) {
822
- throw error;
870
+ throw error2;
823
871
  }
824
- if (!captureValidationErrors && error.validation) {
825
- throw error;
872
+ if (!captureValidationErrors && error2.validation) {
873
+ throw error2;
826
874
  }
827
- if (shouldCapture && !shouldCapture(error)) {
828
- throw error;
875
+ if (shouldCapture && !shouldCapture(error2)) {
876
+ throw error2;
829
877
  }
830
878
  const context = {
831
879
  request: {
@@ -838,27 +886,27 @@ function statlyFastifyPlugin(fastify, options, done) {
838
886
  params: request.params
839
887
  },
840
888
  error: {
841
- statusCode: error.statusCode,
842
- code: error.code
889
+ statusCode: error2.statusCode,
890
+ code: error2.code
843
891
  }
844
892
  };
845
893
  if (request.ip) {
846
894
  context.ip = request.ip;
847
895
  }
848
- if (error.validation) {
849
- context.validation = error.validation;
896
+ if (error2.validation) {
897
+ context.validation = error2.validation;
850
898
  }
851
899
  Statly.setTag("http.method", request.method);
852
900
  Statly.setTag("http.url", request.routerPath || request.url);
853
901
  Statly.setTag("http.status_code", String(statusCode));
854
- Statly.captureException(error, context);
855
- throw error;
902
+ Statly.captureException(error2, context);
903
+ throw error2;
856
904
  });
857
905
  done();
858
906
  }
859
907
  var statlyPlugin = statlyFastifyPlugin;
860
908
  function createRequestCapture(request) {
861
- return (error, additionalContext) => {
909
+ return (error2, additionalContext) => {
862
910
  const context = {
863
911
  request: {
864
912
  id: request.id,
@@ -868,7 +916,7 @@ function createRequestCapture(request) {
868
916
  },
869
917
  ...additionalContext
870
918
  };
871
- return Statly.captureException(error, context);
919
+ return Statly.captureException(error2, context);
872
920
  };
873
921
  }
874
922
  function sanitizeHeaders2(headers) {
@@ -918,12 +966,12 @@ function init(options) {
918
966
  client = new StatlyClient(finalOptions);
919
967
  client.init();
920
968
  }
921
- function captureException(error, context) {
969
+ function captureException(error2, context) {
922
970
  if (!client) {
923
971
  console.warn("[Statly] SDK not initialized. Call Statly.init() first.");
924
972
  return "";
925
973
  }
926
- return client.captureException(error, context);
974
+ return client.captureException(error2, context);
927
975
  }
928
976
  function captureMessage(message, level = "info") {
929
977
  if (!client) {
@@ -976,6 +1024,20 @@ async function close() {
976
1024
  function getClient() {
977
1025
  return client;
978
1026
  }
1027
+ async function trace2(name, operation, tags) {
1028
+ if (!client) {
1029
+ return operation(null);
1030
+ }
1031
+ return client.trace(name, operation, tags);
1032
+ }
1033
+ function startSpan(name, tags) {
1034
+ if (!client) return null;
1035
+ return client.startSpan(name, tags);
1036
+ }
1037
+ function captureSpan(span) {
1038
+ if (!client) return "";
1039
+ return client.captureSpan(span);
1040
+ }
979
1041
  var Statly = {
980
1042
  init,
981
1043
  captureException,
@@ -986,7 +1048,10 @@ var Statly = {
986
1048
  addBreadcrumb,
987
1049
  flush,
988
1050
  close,
989
- getClient
1051
+ getClient,
1052
+ trace: trace2,
1053
+ startSpan,
1054
+ captureSpan
990
1055
  };
991
1056
 
992
1057
  // src/integrations/express.ts
@@ -1028,8 +1093,8 @@ function requestHandler() {
1028
1093
  }
1029
1094
  function expressErrorHandler(options = {}) {
1030
1095
  return (err, req, res, next) => {
1031
- const error = err instanceof Error ? err : new Error(String(err));
1032
- if (options.shouldHandleError && !options.shouldHandleError(error)) {
1096
+ const error2 = err instanceof Error ? err : new Error(String(err));
1097
+ if (options.shouldHandleError && !options.shouldHandleError(error2)) {
1033
1098
  return next(err);
1034
1099
  }
1035
1100
  const context = {
@@ -1053,7 +1118,7 @@ function expressErrorHandler(options = {}) {
1053
1118
  if (req.statlyContext?.transactionName) {
1054
1119
  Statly.setTag("transaction", req.statlyContext.transactionName);
1055
1120
  }
1056
- Statly.captureException(error, context);
1121
+ Statly.captureException(error2, context);
1057
1122
  next(err);
1058
1123
  };
1059
1124
  }
@@ -1110,5 +1175,8 @@ export {
1110
1175
  flush,
1111
1176
  close,
1112
1177
  getClient,
1178
+ trace2 as trace,
1179
+ startSpan,
1180
+ captureSpan,
1113
1181
  Statly
1114
1182
  };